我有一个实体A,其中包含与实体B和实体C的@OneToOne关系。实体B和实体C又与实体B1和实体C1具有@OneToOne关系:
A
|
|--B @OneToOne
|
|--B1 @OneToOne
|--C
|
|--C1 @OneToOne
我需要选择实体A的所有数据以及实体B和实体C的某些字段。实体B1和C1的字段必须忽略。
默认情况下,Hibernate将所有* ToOne关系视为EADGER,并且配置带有延迟加载的* ToOne的唯一方法是将注释@LazyToOne(LazyToOneOption.NO_PROXY)与休眠编译工具结合使用。
因此,仅执行单个查询并仅加载希望的关系:
我使用@LazyToOne(LazyToOneOption.NO_PROXY)配置了所有@OneToOne关系
我使用Criteria API检索A实体,获取B和C关系(希望的关系)。
// 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
@JoinColumn(name = "BADGE_ID")
@LazyToOne(LazyToOneOption.NO_PROXY)
private BadgeEntity badge;
// The C Entity Relation (wished)
@OneToOne
@JoinColumn(name = "ADDRESS_ID")
@LazyToOne(LazyToOneOption.NO_PROXY)
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
@JoinColumn(name = "BADGE_TYPE_ID")
@LazyToOne(LazyToOneOption.NO_PROXY)
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
@JoinColumn(name = "ADDRESS_TYPE_ID")
@LazyToOne(LazyToOneOption.NO_PROXY)
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());
}
}
执行查询方法时,我希望在User表上执行单个查询,并在Badge和Address表上直接获取。
此查询实际上已执行,但是,如果我尝试访问UserEntity的关系(例如徽章),则会执行一个奇怪的额外查询:
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_.email as email2_6_0_,
userentity0_.name as name3_6_0_,
userentity0_.surname as surname4_6_0_,
addressent1_.city as city2_1_1_,
addressent1_.description as descript3_1_1_,
addressent1_.state as state4_1_1_,
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
名称:Jhon 姓:母鹿 电子邮件:jhon.doe@gmail.com
-- STRANGE EXTRA QUERY I DON'T KNOW WHY EXECUTED
Hibernate:
select
userentity_.address_id as address_5_6_,
userentity_.badge_id as badge_id6_6_
from
users userentity_
where
userentity_.user_id=?