我们正在从Hibernate本机标准转向JPA标准查询,将hibernate从4.3.11升级到5.2.12,并发现了不同的行为。以前的hibernate标准使用带有连接的单个查询来急切获取一对多关联实体,但JPA使用单独的查询来获取每个根实体的关联实体。
我知道我可以像entityRoot.fetch("attributes", JoinType.INNER);
一样明确设置获取模式,但是我们需要在一些AbstractDao实现中执行它,该实现应该适用于任何急切的一对多关联,因此无法明确设置它。 / p>
因此,我可以以某种方式告诉JPA标准,在默认情况下使用连接在单个查询中急切获取关联实体,而不是为每个根实体分别进行单独查询吗?
代码示例:
CriteriaBuilder builder = createCriteriaBuilder();
CriteriaQuery<T> criteriaQuery = builder.createQuery(getEntityClass());
Root<T> entityRoot = criteriaQuery.from(getEntityClass());
criteriaQuery.select(entityRoot);
criteriaQuery.where(builder.equal(entityRoot.get("param1"), "value"));
return getEntityManager().createQuery(criteriaQuery).getResultList();
答案 0 :(得分:1)
您无法以这种方式配置它,但您可以实现必要的行为。
正如您在Hibernate 5.2 User Guide中所读到的,有几种方法可以应用提取策略:
@Fetch
注释是一种静态方式来应用提取策略,FetchMode.JOIN
完全按照您的描述运行:
本质上是EAGER的抓取方式。要获取的数据是 通过使用SQL外连接获得。
问题是,即使您使用attributes
注释标记@Fetch(FetchMode.JOIN)
集合,也会overridden:
我们之所以没有使用JPQL查询来获取多个 部门实体是因为FetchMode.JOIN策略 被查询获取指令覆盖。
要使用JPQL查询获取多个关系,请使用JOIN FETCH 必须使用指令。
因此,FetchMode.JOIN对于获取实体时非常有用 直接,通过他们的标识符或自然身份。
没有FetchParent::fetch
的JPA条件查询也会这样做。
由于您需要一个抽象DAO的通用解决方案,可能的方法是使用反射处理所有渴望的一对多关联:
Arrays.stream(getEntityClass().getDeclaredFields())
.filter(field ->
field.isAnnotationPresent(OneToMany.class))
.filter(field ->
FetchType.EAGER == field.getAnnotation(OneToMany.class).fetch())
.forEach(field ->
entityRoot.fetch(field.getName(), JoinType.INNER));
当然,为每个查询调用反射都是低效的。您可以从Metamodel获取所有已加载的@Entity
类,对其进行处理,并存储结果以供进一步使用:
Metamodel metamodel = getEntityManager().getMetamodel();
List<Class> entityClasses = metamodel.getEntities().stream()
.map(Type::getJavaType)
.collect(Collectors.toList());
Map<Class, List<String>> fetchingAssociations = entityClasses.stream()
.collect(Collectors.toMap(
Function.identity(),
aClass -> Arrays.stream(aClass.getDeclaredFields())
.filter(field ->
field.isAnnotationPresent(OneToMany.class))
.filter(field ->
FetchType.EAGER == field.getAnnotation(OneToMany.class).fetch())
.map(Field::getName)
.collect(Collectors.toList())
));