JPA,具有外键和序列号的混合代理键
我有两个表:
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上有一个复合主键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 对象时,它会反射性地创建一个新实例并设置所有fields 为 null,因此在将其链接到之前保留的 Document 对象之前会丢失sectionNum 值。
不幸的是,我无法更改数据库架构,因为它是一个遗留系统。 有没有人做过类似的事情并且成功了?
I've got two tables:
DOCUMENT
--------
DOC_ID (PK)
.
.
.
SECTION
-------
DOC_ID (FK, PK)
SECTION_NUM (PK)
.
.
.
Entries in the database might look like this:
Document:
DOC_ID | . . .
--------------
1 | . . .
2 | . . .
Section:
DOC_ID | SECTION_NUM | . . .
---------------------------
1 | 1 | . . .
1 | 2 | . . .
1 | 3 | . . .
2 | 1 | . . .
Document
has a generated Id on DOC_ID, while Section
has a composite primary key over DOC_ID and SECTION_NUM.
SECTION_NUM is a locally(application) generated sequence number starting fresh for every document.
My entity classes look as follows:
@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;
}
When inserting a new Document and related Section, I do the following:
Document doc = new Document();
Section section = new Section();
section.setDocument(doc);
section.setSectionNum(1);
entityManager.persist(doc);
When persisting I get an exception stating that NULL is not allowed for column SECTION_NUM.
I'm using OpenEJB (which relies on OpenJPA behind the scenes for unit testing), and found when stepping through OpenJPA code that it successfully persists the Document object, but when it comes to the Section object it creates a new instance reflectively and sets all fields to null, so losing the sectionNum value, before linking it to the Document object persisted earlier.
Unfortunately I can't change the DB schema, as it's a legacy system.
Has anybody done something similar and got it working?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一段时间以来我一直想更新这个,但太忙了...
好吧,事实证明这对于 JPA 来说是不可能的。
不过,有一个解决方法。
之前我提到过 Document 类看起来像这样。
这只是澄清问题的简化版本。
真正的类也有一个Sections的集合:
如果Section有一个简单的主键,JPA将很容易处理这种关系,因为它会接受来自应用程序的id,或者从序列生成它,但它不会同时执行这两种操作一个 ID。
因此,解决方案是自己管理关系,并添加生命周期函数:
如您所见,Section 关系现在是 Transient 的,这意味着 JPA 不会管理它。
持久化文档后,框架将调用 updateChildIds 函数,您可以在其中使用新持久化的文档 id 手动更新部分 id。
这可以在以下外观中得到证明:
I've been meaning to update this for some time, but been too busy...
Ok, so it turns out this isn't really possible with JPA.
However, there is a workaround.
Previously I mentioned that the Document class looks like this.
That was just a shortened version to clarify the issue.
The real class has a collection of Sections too:
If Section had a simple primary key, JPA would easily handle the relationship, as it would accept an id from the application, or generate it from a sequence, but it won't do both with one id.
So, the solution is to manage the relationship yourself, and add a lifecycle function:
As you can see, the Section relationship is now Transient, meaning JPA won't manage it.
After persisting a Document, the framework will call the updateChildIds function, where you manually update the Section id's with the newly persisted Document id's.
This could be demonstrated in the following facade:
事实上,JPA 完全能够处理这个问题。您要查找的注释是
MapsId
。在您的情况下,在
Section
的docId
上,您只需添加以下内容:MapsId
注释的值是属性名称您的复合主键(在本例中是相同的)Actually, JPA is perfectly able to handle this. The annotation you are looking for is
MapsId
.In your case, in your
Section
, on thedocId
you simply need to add the following:The value of the
MapsId
annotation is the attribute name of your compound primary key (which in this case is the same)