我有两张桌子:
DOCUMENT
--------
DOC_ID (PK)
.
.
.
SECTION
-------
DOC_ID (FK, PK)
SECTION_NUM (PK)
.
.
.
数据库中的条目可能如下所示:
文件:
DOC_ID | . . .
--------------
1 | . . .
2 | . . .
部分:
DOC_ID | SECTION_NUM | . . .
---------------------------
1 | 1 | . . .
1 | 2 | . . .
1 | 3 | . . .
2 | 1 | . . .
Document
在DOC_ID上生成了ID,而Section
在DOC_ID和SECTION_NUM上有一个复合主键。
SECTION_NUM是一个本地(应用程序)生成的序列号,每个文档都是新的。
我的实体类如下所示:
@Entity
@Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
@Id
@Column(name = "DOC_ID", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "DocIdSeq")
@SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
private Long docId;
}
@Entity
@Table(name = "SECTION")
@IdClass(SectionId.class)
public class Section implements java.io.Serializable {
@Id
@Column(name = "DOC_ID", nullable = false)
private Long docId;
@Id
@Column(name = "SECTION_NUM", nullable = false)
private Integer sectionNum;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "DOC_ID")
private Document document;
}
public class SectionId implements java.io.Serializable {
private Long docId;
private Integer sectionNum;
}
插入新文档及相关章节时,我会执行以下操作:
Document doc = new Document();
Section section = new Section();
section.setDocument(doc);
section.setSectionNum(1);
entityManager.persist(doc);
持久化时,我得到一个异常,声明列SECTION_NUM不允许NULL。 我正在使用OpenEJB(它在后台依赖OpenJPA进行单元测试),并且在步进OpenJPA代码时发现它成功地持久化了Document对象,但是当涉及到Section对象时,它反射地创建了一个新实例并设置了所有字段为null,因此在将之前链接到Document对象之前丢失了sectionNum值。
不幸的是,我无法更改数据库架构,因为它是一个遗留系统。 有没有人做过类似的事情并让它发挥作用?
答案 0 :(得分:0)
我一直想更新这个,但是太忙了......
好的,事实证明这对JPA来说是不可能的。 但是,有一种解决方法。
以前我提到Document类看起来像这样。
@Entity
@Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
@Id
@Column(name = "DOC_ID", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"DocIdSeq")
@SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
private Long docId;
}
这只是一个缩短版本以澄清问题。 真正的班级也有一系列章节:
@Entity
@Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
@Id
@Column(name = "DOC_ID", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"DocIdSeq")
@SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
private Long docId;
@OneToMany
private Set<Section> sections = new HashSet<Section>(0);
}
如果Section有一个简单的主键,JPA可以很容易地处理这个关系,因为它接受来自应用程序的id,或者从序列中生成它,但它不能同时使用一个id。
因此,解决方案是自己管理关系,并添加生命周期功能:
@Entity
@Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
@Id
@Column(name = "DOC_ID", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"DocIdSeq")
@SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
private Long docId;
@Transient
private Set<Section> sections = new HashSet<Section>(0);
@PostPersist
public void updateChildIds() {
for (Section section : this.sections) {
section.getId().setDocId(this.docId);
}
}
}
如您所见,Section关系现在是Transient,这意味着JPA不会管理它。 在持久化Document之后,框架将调用updateChildIds函数,您可以使用新持久化的Document id手动更新Section id。
这可以通过以下方面证明:
@Stateless
public void DocumentFacade implements DocumentFacadeLocal {
@PersistenceContext
private EntityManager entityManager;
public void save(Document entity) throws Exception {
this.entityManager.persist(entity);
this.entityManager.flush();
this.persistTransientEntities(entity);
this.entityManager.flush();
}
private void persistTransientEntities(CaseInstructionSheet entity) {
for (Section section : entity.getSections()) {
this.entityManager.persist(section);
}
}
}
答案 1 :(得分:0)
实际上,JPA完全能够解决这个问题。您要查找的注释是MapsId
。
在您的情况下,在Section
,在docId
上,您只需添加以下内容:
@MapsId("docId")
MapsId
注释的值是复合主键的属性名称(在本例中是相同的)