java - JPA 选择关联并使用 NamedEntityGraph
问题描述
我们使用 Java 11、Spring Boot、Hibernate 5 和 QueryDSL 构建的内部框架可以自动生成大量查询。我试图保持一切高效并仅在需要时加载关联。加载完整实体时,程序员可以声明要使用的 NamedEntityGraph。现在有一种情况会生成这样的查询:
select user.groups
from User user
where user.id = ?1
有问题的实体如下所示:
@Entity
@NamedEntityGraph(name = User.ENTITY_GRAPH,
attributeNodes = {
@NamedAttributeNode(User.Fields.permissions),
@NamedAttributeNode(value = User.Fields.groups, subgraph = "user-groups-subgraph")
},
subgraphs = @NamedSubgraph(
name = "user-groups-subgraph",
attributeNodes = {
@NamedAttributeNode(Group.Fields.permissions)
}
))
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
@ElementCollection(targetClass = Permission.class)
@CollectionTable(name = "USERS_PERMISSIONS", joinColumns = @JoinColumn(name = "uid"))
private Set<Permission> permissions = EnumSet.of(Permission.ROLE_USER);
@ManyToMany(fetch = LAZY)
private Set<Group> groups = new HashSet<>();
}
@Entity
@NamedEntityGraph(name = Group.ENTITY_GRAPH,
attributeNodes = {
@NamedAttributeNode(value = Group.Fields.permissions)
})
public class Group {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Enumerated(EnumType.STRING)
@ElementCollection(targetClass = Permission.class)
@CollectionTable(
name = "GROUPS_PERMISSIONS",
joinColumns = @JoinColumn(name = "gid")
)
@NonNull
private Set<Permission> permissions = EnumSet.noneOf(Permission.class);
}
直接选择用户或组时,生成的查询仅应用提供的 NamedEntityGraphs。但对于上述查询,例外是:
org.hibernate.QueryException:
query specified join fetching, but the owner of the fetched association was not present in the select list
[FromElement{explicit,collection join,fetch join,fetch non-lazy properties,classAlias=user,role=foo.bar.User.permissions,tableName={none},tableAlias=permission3_,origin=null,columns={,className=null}}]
我首先尝试了用户图,但由于我们正在获取组,所以我尝试了组图。同样的例外。
问题是,没有简单的方法可以将 a 添加FETCH JOIN
到生成的查询中,因为我不知道应该加入关联的哪些属性。我将不得不加载实体图,遍历它和任何子图并生成正确的连接子句。
有关查询生成的更多详细信息:
// QueryDsl 4.3.x Expressions, where propType=Group.class, entityPath=User, assocProperty=groups
final Path<?> expression = Expressions.path(propType, entityPath, assocProperty);
// user.id = ?1
final BooleanExpression predicate = Expressions.predicate(Ops.EQ, idPath, Expressions.constant(rootId));
// QuerydslJpaPredicateExecutor#createQuery from Spring Data JPA
final JPQLQuery<P> query = createQuery(predicate).select(expression).from(path);
// Add Fetch Graph
((AbstractJPAQuery<?, ?>) query).setHint(GraphSemantic.FETCH.getJpaHintName(), entityManager.getEntityGraph(fetchGraph));
编辑:
我可以通过一个简单的 JPQL 查询来重现这一点。很奇怪,如果我尝试进行类型化查询,它会选择一个 List of Sets of Group 而未键入的只是一个 List of Group。也许在概念上有些错误 - 我正在选择一个 Collection 并且我正在尝试对其应用 fetch join。但是 JPQL 不允许从子查询中进行 SELECT,所以我不确定要更改什么..
// em is EntityManager
List gs = em
.createQuery("SELECT u.groups FROM User u WHERE u.id = ?1")
.setParameter(1, user.getId())
.setHint(GraphSemantic.FETCH.getJpaHintName(), em.getEntityGraph(Group.ENTITY_GRAPH))
.getResultList();
相同的例外:
org.hibernate.QueryException:查询指定连接提取,但提取关联的所有者不在选择列表中
解决方案
推荐阅读
- html - 反应中的网站图标/标签通知
- javascript - 我如何抓取数据
- html - html标签“tel”属性在android chrome中不起作用
- css - 从浏览器中提取光标图像(抓取、抓取等)
- mysql - 如何将对象数组转换为表格?
- sip - 在 Linphone 4.x 中发送邀请的问题
- elasticsearch - Painless (Elasticsearch) 不能使用关键字 - 脚本错误
- java - PyCharm 在 macOS 上使用非常松散的安全性
- dialogflow-es - Dialogflow 与 Viber 的集成不适用于 Rich Media 类型的消息
- reactjs - 传递参数时,onClick 事件会无限触发