Hibernate异常:无法在使用@IdClass注释的实体中找到属性....

时间:2014-07-15 13:35:57

标签: java hibernate

我有这个复合键类:

@Embeddable
public class TaskEntityId implements Serializable {
    private static final long serialVersionUID = 1L;

    public String objectUuid;
    @Column(name="DOMAIN_OBJECT_UUID", nullable=false, length=36)
    public String getObjectUuid() { return objectUuid; }
    public void setObjectUuid(String uuid) { this.objectUuid = uuid; }

    public String taskName;
    @Column(name = "TASK_NAME", nullable=false, length=32)
    public String getTaskName() { return taskName; }
    public void setTaskName(String taskName) { this.taskName = taskName; }

    public Date createdTimestamp = new Date();
    @Column(name = "CREATED_TS", nullable=false, updatable=false)
    @Temporal(TemporalType.TIMESTAMP)
    public Date getCreatedTimestamp() { return createdTimestamp; }
    public void setCreatedTimestamp(Date createdTimestamp) { this.createdTimestamp = createdTimestamp; }

    @Override
    public boolean equals(Object o) {...}

    @Override
    public int hashCode() {...}

    @Override
    public String toString() {...}
}

在此实体类中使用的是:

@Entity
@Table(name = "TASK")
@IdClass(TaskEntityId.class)
public class TaskEntity implements Serializable {

    @EmbeddedId
    TaskEntityId id;

    public TaskEntityId getId() { return id; }

    public void setId(TaskEntityId id) { this.id = id; }

...

我还根据对其他类似问题的建议,在本课程中添加了一些额外的getter:

public String getObjectUuid() { return id.getObjectUuid(); }
public String getTaskName() { return id.getTaskName(); }
public Date getCreatedTimestamp() { return id.getCreatedTimestamp(); }

但是,我得到了这个例外:

Error in custom provider, org.hibernate.AnnotationException: Unable to find properties (createdTimestamp, objectUuid, taskName) in entity annotated with @IdClass:c.m.d.p.TaskEntity

2 个答案:

答案 0 :(得分:7)

解决方案是删除

@IdClass(TaskEntityId.class)
来自TaskEntity类的

注释。而且我还需要一个空的默认构造函数。

答案 1 :(得分:2)

要映射复合键,可以使用EmbeddedId或IdClass注释。我知道这个问题并不严格关于JPA,但规范定义的规则也适用。所以这里是:

2.1.4主键和实体标识

...

复合主键必须对应于单个持久字段或属性,或者对应于下面描述的一组此类字段或属性。必须定义主键类以表示复合主键。当数据库键由多个列组成时,从旧数据库映射时,通常会出现复合主键。 EmbeddedId和IdClass注释用于表示复合主键。见9.1.14和9.1.15。

...

以下规则适用于复合主键:

主键类必须是公共的,并且必须具有公共的无参数构造函数。 如果使用基于属性的访问,则主键类的属性必须是公共的或受保护的。 主键类必须是可序列化的。 主键类必须定义equals和hashCode方法。这些方法的值相等的语义必须与键映射到的数据库类型的数据库相等一致。 复合主键必须表示并映射为可嵌入类(请参见第9.1.14节“EmbeddedId注释”),或者必须表示并映射到实体类的多个字段或属性(请参见第9.1.15节“IdClass”)注解”)。 如果复合主键类映射到实体类的多个字段或属性,则主键类中的主键字段或属性的名称以及实体类的名称必须对应,并且它们的类型必须相同。

使用IdClass

复合主键的类可能看起来像(可能是静态内部类):

public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, String confPathID) {
        this.id = levelStation;
        this.name = confPathID;
    }
    // equals, hashCode
}

实体:

@Entity
@IdClass(TimePK.class)
class Time implements Serializable {
    @Id
    private Integer levelStation;
    @Id
    private Integer confPathID;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    // getters, setters
}

IdClass注释将多个字段映射到表PK。

使用EmbeddedId

复合主键的类可能看起来像(可能是静态内部类):

*

@Embeddable
public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;
    public TimePK() {}
    public TimePK(Integer levelStation, String confPathID) {
        this.id = levelStation;
        this.name = confPathID;
    }
    // equals, hashCode
}

* 实体:

@Entity
class Time implements Serializable {
    @EmbeddedId
    private TimePK timePK;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    //...
}

@EmbeddedId注释将PK类映射到表PK。

的差异:

从物理模型的角度来看,没有差异 EmbeddedId以某种方式更清楚地传达了密钥是一个复合密钥,当组合的pk本身是一个有意义的实体或在代码中重用时,IMO是有意义的。 @IdClass可用于指定某些字段组合是唯一的,但这些字段组合没有特殊含义。 它们也会影响你编写查询的方式(使它们或多或少冗长):

使用IdClass

从时间t

中选择t.levelStation

with EmbeddedId

从时间t

中选择t.timePK.levelStation