所以這是我第一次嘗試使用JPA和CriteriaQuery
。簡單的條件爲JPA CriteriaQuery
我有以下(簡化的)實體:
@Entity
@Table(name = "hours")
@XmlRootElement
public class Hours implements Serializable
{
@EmbeddedId
protected HoursPK hoursPK;
@Column(name = "total_hours")
private Integer totalHours;
@JoinColumn(name = "trainer_id", referencedColumnName = "id", nullable = false, insertable = false, updatable = false)
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Trainer trainer;
public Hours()
{
}
... getter and setter for the attributes
}
@Embeddable
public class HoursPK implements Serializable
{
@Basic(optional = false)
@Column(name = "date_held", nullable = false)
@Temporal(TemporalType.DATE)
private Date dateHeld;
@Basic(optional = false)
@Column(name = "trainer_id", nullable = false, length = 20)
private String trainerId;
@Column(name = "total_hours")
private Integer totalHours;
public HoursPK()
{
}
... getter and setter ...
}
@Entity
@Table(name = "trainer")
public class Trainer implements Serializable
{
@Id
@Basic(optional = false)
@Column(name = "id", nullable = false, length = 20)
private String id;
@Basic(optional = false)
@Column(name = "firstname", nullable = false, length = 200)
private String firstname;
@Basic(optional = false)
@Column(name = "lastname", nullable = false, length = 200)
private String lastname;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "trainer", fetch = FetchType.LAZY)
private List<Hours> hoursList;
... more attributes, getters and setters
@XmlTransient
public List<Hours> getHoursList() {
return hoursList;
}
public void setHoursList(List<Hours> hoursList) {
this.hoursList = hoursList;
}
}
本質上Trainer
保持培訓和在培訓所花費的時間被存儲在Hours
實體。對於hours
表中的PK是(trainer_id, date_held)
因爲每個教練只持有每天一個培訓
我想創建一個CriteriaQuery
獲取某個月份的一個教練的所有時間這是我的嘗試:。
EntityManagerFactory emf = ...
EntityManager em = emf.createEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Hours> c = builder.createQuery(Hours.class);
Root<Hours> root = c.from(Hours.class);
Calendar cal = Calendar.getInstance();
cal.set(2014, 0, 1);
Expression<Date> from = builder.literal(cal.getTime());
cal.set(2014, 1, 1);
Expression<Date> to = builder.literal(cal.getTime());
Predicate who = builder.equal(root.get(Hours_.trainer), "foobar"); // it fails here
Predicate gt = builder.greaterThanOrEqualTo(root.get(Hours_.hoursPK).get(HoursPK_.dateHeld), from);
Predicate lt = builder.lessThan(root.get(Hours_.hoursPK).get(HoursPK_.dateHeld), to);
c.where(gt,lt,who);
c.orderBy(builder.asc(root.get(Hours_.hoursPK).get(HoursPK_.dateHeld) ));
TypedQuery<Hours> q = em.createQuery(c);
List<Hours> resultList = q.getResultList();
我使用Hibernate 4.3.1作爲JPA提供者和上面的代碼失敗例外:
在線程異常「主」 java.lang.IllegalArgumentException異常:參數值foobar的]沒有匹配預期類型[persistence.Trainer (N/A)] 在org.hibernate.jpa.spi.BaseQueryImpl.validateBinding(BaseQueryImpl.java:885)
除了事實,這似乎是一個非常查詢複雜,即使是新手SQL可能在幾分鐘內寫完,我不知道如何爲上述查詢中的hours
表中的trainer_id
列提供正確的值。
我也試過:
Predicate who = builder.equal(root.get("trainer_id"), "foobar");
但失敗,出現異常:
java.lang.IllegalArgumentException異常:無法找到這個ManagedType與給定名稱[trainer_id]屬性[持久性。小時]
它的工作,當我獲得一個實際的實體實例,映射到"foobar"
ID:
CriteriaQuery<Trainer> cq = builder.createQuery(Trainer.class);
Root<Trainer> trainerRoot = cq.from(Trainer.class);
cq.where(builder.equal(trainerRoot.get(Trainer_.id), "foobar"));
TypedQuery<Trainer> trainerQuery = em.createQuery(cq);
Trainer foobarTrainer = trainerQuery.getSingleResult();
....
Predicate who = builder.equal(root.get(Hours_.trainer), foobarTrainer);
但是,這似乎是一個非常愚蠢(和緩慢)的方式來做到這一點。
我確定我錯過了一些非常明顯的東西,但我找不到它。
感謝,'root.get(Hours_.trainer).get(Trainer_.id)'做了竅門。關於複合PK:這是我對ORM最大的批評之一:他們強迫你使用「真實」的PK。將一個自動生成的列添加到小時表中確實可以從關係角度改進模型。它只會將另一個索引的開銷添加到表中,而不會給予任何好處(在SQL中)。在我玩弄這個之後,我同意:API給我們的類型安全帶來的好處並不能保證它的複雜性。 –
JPA如何強制您使用複合鍵?相反:使用單列鍵更容易。或者我誤解了你?一個自動生成的鍵極大地改進了模型:它避免了在所有引用hours列的表中有兩列的外鍵,它使聯接更快,並且它不會強制你修改PK和所有FK,當你意識到必須更改dateHeld值。 –
對不起,這是一個錯字。我的意思是寫**,不是**「真實的東西」*。如果真正的PK *是一個複合鍵,我認爲添加額外的列和索引並沒有任何好處,只是爲了讓ORM的生活更輕鬆。 PK不會改變,在這種情況下,他們確實不會。如果墨菲出現這種情況,那麼刪除錯誤信息並在 –