Java JPA标准查询的简单where条件
这是我第一次尝试使用JPA和标准查询 我有以下(简化)实体:Java JPA标准查询的简单where条件,java,hibernate,jpa,Java,Hibernate,Jpa,这是我第一次尝试使用JPA和标准查询 我有以下(简化)实体: @Entity @Table(name = "hours") @XmlRootElement public class Hours implements Serializable { @EmbeddedId protected HoursPK hoursPK; @Column(name = "total_hours") private Integer totalHours; @JoinCol
@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;
}
}
我使用Hibernate 4.3.1作为JPA提供程序,上面的代码失败,但有一个例外:
线程“main”java.lang.IllegalArgumentException中出现异常:参数值[foobar]与预期类型[persistence.Trainer(n/a)]不匹配
位于org.hibernate.jpa.spi.BaseQueryImpl.validateBinding(BaseQueryImpl.java:885)
对于一个查询来说,这似乎非常复杂,即使是SQL新手也可以在几分钟内编写,除了这个事实之外,我不知道如何为上述查询中hours
表中的trainer\u id
列提供正确的值
我还尝试:
Predicate who = builder.equal(root.get("trainer_id"), "foobar");
但除了一个例外,这是失败的:
java.lang.IllegalArgumentException:无法在此ManagedType[persistence.Hours]上找到具有给定名称[trainer\u 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);
CriteriaQuery cq=builder.createQuery(Trainer.class);
根traineroot=cq.from(Trainer.class);
cq.where(builder.equal(trainerRoot.get(Trainer.id),“foobar”);
TypedQuery trainerQuery=em.createQuery(cq);
Trainer foobarTrainer=trainerQuery.getSingleResult();
....
谓词who=builder.equal(root.get(Hours\uuu.trainer),foobarTrainer);
但这似乎是一种相当愚蠢(而且缓慢)的方式
我确信我在这里遗漏了一些非常明显的东西,但我找不到它。首先,JPA查询总是使用类名和字段名。从不使用列名。因此尝试使用
trainer\u id
是行不通的
builder.equal(root.get(Hours_.trainer), "foobar");
您试图将小时实体的trainer字段与字符串“foobar”进行比较。培训师属于培训师类型。驯兽师不可能等同于一根绳子。它的ID、firstName或lastName都是String类型,可以与字符串进行比较。所以你可能想要
builder.equal(root.get(Hours_.trainer).get(Trainer_.id), "foobar");
也就是说,正如您所注意到的,CriteriaAPI极其复杂,导致代码无法读取,难以维护。当您必须从多个可选条件(因此得名)动态组合查询时,它非常有用,但对于静态查询,您肯定应该使用JPQL,它甚至比SQL更简单、更短:
select h from Hours h
where h.trainer.id = :trainerId
and h.hoursPK.dateHeld >= :from
and h.hoursPK.dateHeld < :to
order by h.hoursPK.dateHeld
从小时数中选择小时数
其中h.trainer.id=:trainerId
和h.hoursPK.datehold>=:from
和h.hoursPK.datehold<:to
h.hoursPK.datehold订购
我强烈建议不要使用复合键,尤其是当它的一个组件是可能需要更改的功能数据(DateHold)时。使用数字、单列、自动生成的主键,一切都会变得更简单、更高效。谢谢,
root.get(Hours\uu.trainer).get(trainer\uu.id)
成功了。关于复合PK:这是我对ORMs最大的批评之一:它们迫使你使用真正的PKs。从关系的角度来看,将自动生成的列添加到小时表中确实可以改进模型。它只会将另一个索引的开销添加到表中,而不会带来任何好处(在SQL中)。在我讨论过这个问题之后,我同意:API提供的类型安全的好处并不能保证它的复杂性。JPA是如何强制您使用复合密钥的?相反,使用单个列键要容易得多。还是我误解了你?自动生成的键极大地改进了模型:它避免了在所有引用小时列的表中都有两列外键,使联接更快,并且当您意识到必须更改DateHold值时,它不会强制您修改PK和所有FK。很抱歉,这是一个输入错误。我想写的不是“真实的东西”。如果真正的PK是一个复合键,我看不出仅仅为了让ORM的生活更轻松而添加额外的列和索引有什么好处。PKs不会改变,在这种情况下,它们真的不会改变。如果Murphy说删除错误的信息并添加正确的信息会更干净,除非你有几十个FK指向它。假设另一个表“bar”的“natural”键是两个“hoursfoo”。您将得到一个包含3列的PK。现在假设另一个表“baz”的“natural”键是一对“barbing”,您将得到一个包含4列的PK。我见过应用程序使用“自然”键,有11列的表,其中9列是PK的一部分,包含与表无关的数据。如果你的Web应用程序中需要引用此类表中的一行的链接,你需要在URL中包含2或3个参数,并在其中一个参数中解析日期,而不是一个简单的整数。无论是否使用ORM,代理PKs都是更好的设计。
select h from Hours h
where h.trainer.id = :trainerId
and h.hoursPK.dateHeld >= :from
and h.hoursPK.dateHeld < :to
order by h.hoursPK.dateHeld