Java JPA标准查询的简单where条件

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

这是我第一次尝试使用JPA和标准查询

我有以下(简化)实体:

@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