JPA Criteria requests queries to retrieve related objects using a SINGLE query with joins instead of multiple queries

We move from the native Hibernate criteria to the JPA query criteria in the area of ​​updating sleep mode from 4.3.11 to 5.2.12 and we find different behavior. Previously, sleep criteria used a single query with joins to obtain one-to-many selections, but JPA uses separate queries to retrieve related objects for each root object.

I know that I can explicitly set the sampling mode, for example entityRoot.fetch("attributes", JoinType.INNER);, but we need to do this in some AbstractDao implementation that should work for any impatient one-to-many relationship, so I cannot explicitly set this.

So, is there any way to define JPA criteria for finding related objects in a single query using default joins instead of separate queries for each root object?

Code example:

    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();
+6
source share
1 answer

Short answer

You cannot configure it this way, but you can implement the necessary behavior.

Long answer

As you can read in Hibernate 5.2. User Guide , there are several ways to apply a sampling strategy:

@Fetch - , FetchMode.JOIN , :

EAGER . , , - SQL.

, attributes @Fetch(FetchMode.JOIN), overridden:

, JPQL - , FetchMode.JOIN .

JPQL, JOIN FETCH .

, FetchMode.JOIN , , .

JPA FetchParent::fetch .

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));

, . @Entity Metamodel, :

    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())
            ));
0

All Articles