I have an Entity A which contains a @OneToOne relation to the Entity B and Entity C. Entity B and Entity C have, in turn, a @OneToOne relation to Entity B1 and Entity C1: A
--B @OneToOne |
--B1 @OneToOne |
--C |
--C1 @OneToOne |
I need to select all data of Entity A with some fields of Entity B and Entity C. Fields of Entity B1 and C1 have to be ignored. // The 'A' Entity (that I want to load with criteria) @Entity @Table(name = "users", schema = "test_db") public class UserEntity { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name = "USER_ID") private Integer userId; @Column(name = "NAME") private String name; @Column(name = "SURNAME") private String surname; @Column(name = "EMAIL") private String email; // The B Entity Relation (wished) @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "BADGE_ID") private BadgeEntity badge; // The C Entity Relation (wished) @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ADDRESS_ID") private AddressEntity address; // Getters and Setters } // The B Entity (relation that I eventually want to load fetching LEFT on father) @Entity @Table(name = "badges", schema = "test_db") public class BadgeEntity { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name = "BADGE_ID", nullable = false) private Integer badgeId; @Column(name = "CODE", nullable = false, length = 10) private String code; @Column(name = "DESCRIPTION", nullable = false, length = 255) private String description; // The B1 Entity Relation (not wished) @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "BADGE_TYPE_ID") private BadgeTypeEntity badgeType; // Getters and Setters } // The C Entity (relation that I eventually want to load fetching LEFT on father) @Entity @Table(name = "addresses", schema = "test_db") public class AddressEntity { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name = "ADDRESS_ID") private Integer addressId; @Column(name = "DESCRIPTION") private String description; @Column(name = "CITY") private String city; @Column(name = "STATE") private String state; // The C1 Entity Relation (not wished) @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ADDRESS_TYPE_ID") private AddressTypeEntity addressType; // Getters and Setters } // The B1 Entity (relation that I don't want to load) @Entity @Table(name = "badge_types", schema = "test_db") public class BadgeTypeEntity { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name = "BADGE_TYPE_ID") private Integer badgeTypeId; @Column(name = "CODE") private String code; // Getters and Setters } // The C1 Entity (relation that I don't want to load) @Entity @Table(name = "address_types", schema = "test_db") public class AddressTypeEntity { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name = "ADDRESS_TYPE_ID") private Integer addressTypeId; @Column(name = "CODE") private String code; // Getters and Setters } /** *
- Load all UserEntities executing only single query,
- fetching RIGHT on BadgeEntity and AddressEntity (@OneToOne relations),
- avoiding load of nested BadgeTypeEntity and AddressTypeEntity
* **/ @Override public List<UserEntity> getUserByFilter() { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class); Root<UserEntity> user = cq.from(UserEntity.class); // Fetch User --> Address (wished relation, without nested @OneToOne relations) Fetch<UserEntity, AddressEntity> addressFetch = user.fetch(UserEntity_.address, JoinType.LEFT); // Fetch User --> Badge (wished relation, without nested @OneToOne relations) Fetch<UserEntity, BadgeEntity> badgeFetch = user.fetch(UserEntity_.badge, JoinType.LEFT); List<Predicate> predicates = new ArrayList<Predicate>(); cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()]))).distinct(true); TypedQuery<UserEntity> query = em.createQuery(cq); List<UserEntity> result = query.getResultList(); return result; }
@Test @Transactional public void getUserByFilter_Test() { try { UserEntity eUser = userCustomRepository.getUserByFilter().get(0); // At this point, one single query is executed System.out.println("\n********************************"); System.out.println("Name : " + eUser.getName()); System.out.println("Surname : " + eUser.getSurname()); System.out.println("Email : " + eUser.getEmail()); System.out.println("********************************\n"); // Calling get on alrready fetched relation, cause execution of an extra query! BadgeEntity eBadge = eUser.getBadge(); System.out.println("Badge Code : " + eBadge.getCode()); Assert.assertNotNull(eUser); } catch (Exception ex) { ex.printStackTrace(); Assert.fail(ex.getMessage()); } } I configured bytecode enhancement via maven plugin: <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <compilerArguments> <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.hibernate.orm.tooling</groupId> <artifactId>hibernate-enhance-maven-plugin</artifactId> <version>$ {hibernate.version} </version> <executions> <execution> <configuration> <failOnError>true</failOnError> <enableLazyInitialization>true</enableLazyInitialization> </configuration> <phase> compile </phase> <goals> <goal>enhance</goal> </goals> </execution> </executions> </plugin> </plugins> </build> Executing the query method, I’m expecting the execution of a single query,on User table, fetching right on Badge and Address tables. With Bytecode Enhancement enabled, three different query are executed. It seems that with enhancement enable, hibernate ignore fetches configured into criteria query Hibernate: select distinct userentity0_.user_id as user_id1_6_0_, addressent1_.address_id as address_1_1_1_, badgeentit2_.badge_id as badge_id1_3_2_, userentity0_.address_id as address_5_6_0_, userentity0_.badge_id as badge_id6_6_0_, userentity0_.email as email2_6_0_, userentity0_.name as name3_6_0_, userentity0_.surname as surname4_6_0_, addressent1_.address_type_id as address_5_1_1_, addressent1_.city as city2_1_1_, addressent1_.description as descript3_1_1_, addressent1_.state as state4_1_1_, badgeentit2_.badge_type_id as badge_ty4_3_2_, badgeentit2_.code as code2_3_2_, badgeentit2_.description as descript3_3_2_ from users userentity0_ left outer join addresses addressent1_ on userentity0_.address_id=addressent1_.address_id left outer join badges badgeentit2_ on userentity0_.badge_id=badgeentit2_.badge_id where 1=1 Hibernate: select addresstyp0_.address_type_id as address_1_0_0_, addresstyp0_.code as code2_0_0_ from address_types addresstyp0_ where addresstyp0_.address_type_id=? Hibernate: select badgetypee0_.badge_type_id as badge_ty1_2_0_, badgetypee0_.code as code2_2_0_ from badge_types badgetypee0_ where badgetypee0_.badge_type_id=? ******************************** Name : Jhon Surname : Doe Email : jhon.doe@gmail.com ******************************** Badge Code : AA11223344 Instead, without bytecode enhancement, a single query is righty executed: Hibernate: select distinct userentity0_.user_id as user_id1_6_0_, addressent1_.address_id as address_1_1_1_, badgeentit2_.badge_id as badge_id1_3_2_, userentity0_.address_id as address_5_6_0_, userentity0_.badge_id as badge_id6_6_0_, userentity0_.email as email2_6_0_, userentity0_.name as name3_6_0_, userentity0_.surname as surname4_6_0_, addressent1_.address_type_id as address_5_1_1_, addressent1_.city as city2_1_1_, addressent1_.description as descript3_1_1_, addressent1_.state as state4_1_1_, badgeentit2_.badge_type_id as badge_ty4_3_2_, badgeentit2_.code as code2_3_2_, badgeentit2_.description as descript3_3_2_ from users userentity0_ left outer join addresses addressent1_ on userentity0_.address_id=addressent1_.address_id left outer join badges badgeentit2_ on userentity0_.badge_id=badgeentit2_.badge_id where 1=1 ******************************** Name : Jhon Surname : Doe Email : jhon.doe@gmail.com ******************************** Badge Code : AA11223344 |