[英]DTO Query with JPA CriteriaQuery and Hibernate using entity as a DTO constructor creates many selects
休眠 5.2.10.Final
jpa 2.1
我想使用 JPA Criteria Query 和 Hibernate 將投影查詢映射到 DTO(數據傳輸對象)。 我指定了一個將應用於查詢執行結果的構造函數。
如果構造函數用於整個實體類,我有多個選擇而不是一個(這是一個長時間運行的數千條記錄的過程)。 如果構造函數用於實體的一組參數,那么我在控制台中只會看到一個選擇。 我不明白我在哪里弄錯了還是一個錯誤?
public class ServiceDAO {
public List<ServicesDTO> getAllServicesByFilter(ServicesFilter filter) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ServicesDTO> criteria = cb.createQuery(ServicesDTO.class);
Root<ServicesEntity> serviceEntity = criteria.from(ServicesEntity.class);
// here is only one select to get list of services
criteria.select(cb.construct(ServicesDTO.class, serviceEntity.get("active"), serviceEntity.get("providerId"), serviceEntity.get("serviceId")));
// in this case I have multiple selects
//criteria.select(cb.construct(ServicesDTO.class, serviceEntity));
if(filter != null) {
List<Predicate> pcl = new ArrayList<Predicate>();
if(filter.getActive() != null)
pcl.add(cb.equal(serviceEntity.get("active"), filter.getActive()));
if(filter.getProviderId() != null)
pcl.add(cb.equal(serviceEntity.get("providerId"), filter.getProviderId()));
if(filter.getServiceId() != null)
pcl.add(cb.equal(serviceEntity.get("serviceId"), filter.getServiceId()));
criteria.where(pcl.toArray(new Predicate[pcl.size()]));
}
return entityManager.createQuery(criteria).getResultList();
}
}
——
public class ServicesDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean active;
private Integer providerId;
private Integer serviceId;
public ServicesDTO() {}
public ServicesDTO(Boolean active, String providerId, Integer serviceId) {
this.active = active;
this.providerId = Integer.parseInt(providerId);
this.serviceId = serviceId;
}
public ServicesDTO(ServicesEntity service) {
if(service != null) {
this.active = service.isActive();
this.providerId = Integer.parseInt(service.getProviderId());
this.serviceId = service.getServiceId();
}
// getters & setters
}
——
@Entity
@Table
public class ServicesEntity {
@Id
@Column(name = "id", unique = true)
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Column(name = "serviceId", nullable = false)
private int serviceId;
@Column(nullable = false)
private String providerId;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="categoryId")
private Categories categoryId;
private boolean active;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "service", cascade = CascadeType.ALL)
private List<Service_Area_Ref> areas = new ArrayList<Service_Area_Ref>();
@ManyToOne(fetch=FetchType.LAZY, optional = true)
@JoinColumn(name="parentCatId")
private Categories parentCatId;
public ServicesEntity() {}
public ServicesEntity(int serviceId) {
this.serviceId = serviceId;
}
// getters & setters
// equals & hashcode
}
是的,確實如此。 可能沒有太多的用例。 給定的
@Entity
public class A {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private Integer value;
public class ADto {
private Integer va;
public ADto(A a) {
this.va = a.getValue();
}
public ADto(Integer va) {
this.va = va;
}
然后
tx.begin();
A a1 = new A();
a1.setValue(1);
A a2 = new A();
a1.setValue(2);
em.persist(a1);
em.persist(a2);
tx.commit();
em.clear();
System.out.println("As usual");
em.createQuery("select new dto.ADto(a.value) from A a where a.value <= 2", ADto.class).getResultList();
System.out.println("As A");
em.createQuery("select new dto.ADto(a) from A a where a.value <= 2", ADto.class).getResultList();
給你
create table A (id integer generated by default as identity (start with 1), value integer, primary key (id))
create table B (id integer generated by default as identity (start with 1), value integer, primary key (id))
insert into A (id, value) values (default, ?)
insert into A (id, value) values (default, ?)
As usual
select a0_.value as col_0_0_ from A a0_ where a0_.value<=2
As A
select a0_.id as col_0_0_ from A a0_ where a0_.value<=2
select a0_.id as id1_0_0_, a0_.value as value2_0_0_ from A a0_ where a0_.id=?
select a0_.id as id1_0_0_, a0_.value as value2_0_0_ from A a0_ where a0_.id=?
並且您不喜歡每次為新 ADto 實例選擇實體 A 的事實。 這樣做可能是因為您可以創建一個包含多個實體的 DTO,而不僅僅是 A,如 A、B 和 C,那么 JPA/Hibernate 如何在單個 select 語句中方便地做到這一點? 雖然它可以選擇所有屬性,然后跟蹤哪些屬性屬於哪些實體,然后構造它們並將它們傳遞給你的 DTO,這樣你就可以解構它們,這對於罕見的事情來說似乎是很多工作。 如果您選擇所需的屬性並根據第一種情況創建構造函數,它可能會更有效,更好。
我正在使用 Hibernate 5.3,也遇到了這種行為。 但是我發現如果使用 JPA Tuple
作為 DTO 容器和multiselect
,這個問題不會發生。 所以我的最終解決方案是先使用Tuple
查詢結果集,然后手動將其轉換為 DTO,例如:
CriteriaQuery<Tuple> criteria = cb.createTupleQuery();
.......
criteria.multiselect(serviceEntity);
List<ServicesDTO> result = entityManager.createQuery(criteria).getResultList().stream()
.map(t->new ServicesDTO(t.get(0,ServicesEntity.class)))
.collect(toList());
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.