我在JPA中嵌入多对一关系中的单个值时遇到问题。下面是我用来初始化测试模式以及测试实体的一些代码。
这个想法是有一个表EMPLOYEE,它引用了表RANK。表RANK又包含RANK的名称。每个员工都参考其排名。
当我尝试运行我的代码时,我得到以下异常:
Caused by: org.hibernate.AnnotationException: SecondaryTable JoinColumn cannot reference a non primary key
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:623)
at org.hibernate.cfg.annotations.EntityBinder.bindJoinToPersistentClass(EntityBinder.java:764)
at org.hibernate.cfg.annotations.EntityBinder.createPrimaryColumnsToSecondaryTable(EntityBinder.java:756)
at org.hibernate.cfg.annotations.EntityBinder.finalSecondaryTableBinding(EntityBinder.java:684)
at org.hibernate.cfg.SecondaryTableSecondPass.doSecondPass(SecondaryTableSecondPass.java:29)
这是一个数据库架构
CREATE TABLE RANK (
ID INT NOT NULL,
NAME VARCHAR
);
ALTER TABLE RANK ADD PRIMARY KEY (ID);
INSERT INTO RANK (ID, NAME) VALUES (1, 'WORKER')
CREATE TABLE EMPLOYEE(
ID INT NOT NULL,
NAME VARCHAR,
RANK_ID INT NOT NULL
);
ALTER TABLE EMPLOYEE ADD PRIMARY KEY (ID);
INSERT INTO EMPLOYEE (ID, NAME, RANK_ID) VALUES (1, 'Bobby', 1);
最后,唯一的实体:
@Entity
@Table(name = "EMPLOYEE")
@SecondaryTable(name = "RANK", pkJoinColumns=@PrimaryKeyJoinColumn(name="ID", referencedColumnName = "RANK_ID"))
public class Employee {
@Id
@Column(name = "id")
@GeneratedValue(generator = "increment")
@GenericGenerator(name = "increment", strategy = "increment")
private int id;
@Column(name = "NAME")
private String name;
@Column(name = "RANK_ID")
private int rankId;
@Column(name = "name", table = "RANK")
private String rank;
public Employee() {
}
}
我很感激有关如何解决此案的任何提示。看起来很常见,因为JPA无法做到这一点。
答案 0 :(得分:1)
使用辅助表注释时引用Employee ID并删除rank_id
。您是说通过使用该注释将数据分布在多个表中,因此两者的主键基本相同。实际上,由于两个PK都具有相同的标识符,因此甚至不需要定义@PrimaryKeyJoinColumn
。
请参阅此处:https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/SecondaryTable.html
另一方面,如果您只想要多对多关系,则可以使用@ManyToOne
中的@JoinColumn
注释和rank_id
。
答案 1 :(得分:0)
如果您阅读this section of the Java Persistence Wikibook,您会看到JPA规范不涵盖您想要的内容,因此基础提供商可能不支持:
JPA规范没有直接涵盖这一点,所以如果你有这个场景,首先要考虑的是,如果你有灵活性,就是改变你的数据模型,使其保持在规范的范围内。
@SecondaryTable
主要用于将一对一表格链接到一个实体。
'标准'实现所需内容的方法是为Rank
创建单独的实体,并在Employee
中获取EAGER。然后,您可以在Employee
中使用名为getRankName()
的方法返回this.rank.getName()
。因此,您的Employee
类看起来像这样:
@Entity
@Table(name = "EMPLOYEE")
public class Employee {
@Id
@Column(name = "id")
@GeneratedValue(generator = "increment")
@GenericGenerator(name = "increment", strategy = "increment")
private int id;
@Column(name = "NAME")
private String name;
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "RANK_ID", referencedColumnName = "ID")
private Rank rank;
public Employee() {
}
public String getRankName() {
if (this.rank == null) {
return null;
}
return this.rank.getName();
}
}