У меня есть объект A, который содержит отношение @OneToOne к объекту B и объекту C. Объект B и объект C, в свою очередь, имеют отношение @OneToOne к объекту B1 и объекту C1:
A
|
|--B @OneToOne
|
|--B1 @OneToOne
|--C
|
|--C1 @OneToOne
Мне нужно выбрать все данные объекта A с некоторыми полями объекта B и объекта C. Поля объекта B1 и C1 следует игнорировать.
Hibernate по умолчанию рассматривает все отношения * ToOne как EADGER, и единственный способ настроить * ToOne с отложенной загрузкой - это использовать аннотацию @LazyToOne (LazyToOneOption.NO_PROXY) в сочетании с инструментарием компиляции hibernate.
Итак, чтобы выполнить только один запрос и загрузить только желаемые отношения:
Я настроил все отношение @OneToOne с @LazyToOne (LazyToOneOption.NO_PROXY)
Я использовал 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.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=?