如何在 JPA / Hibernate 中映射只读集合而不导致数据库更新
是否可以在 hibernate / jpa 中创建在获取包含实体时获取的关系,但在保存包含实体时永远不会导致任何数据库更新?我将尝试通过一个例子来阐明要求。
我有一个简单的实体 B
@Entity
public class B {
private int bId;
@Id
public int getBId() {
return bId;
}
public void setBId(int aId) {
bId = aId;
}
}
和另一个实体 A,其中包含到此类的单向多对多映射。
@Entity
public class A {
private int aId;
private List<B> bs;
@Id
public int getAId() {
return aId;
}
public void setAId(int aId) {
this.aId = aId;
}
@ManyToMany
@JoinTable(name = "A_B",
joinColumns = {@JoinColumn(name = "AID")},
inverseJoinColumns = {@JoinColumn(name = "BID")}
)
public List<B> getBs() {
return bs;
}
public void setBs(List<B> aBs) {
bs = aBs;
}
}
当从数据库中获取实体 A 并按如下方式合并时
A a = em.find(A.class, 1);
a.getBs().size();
em.merge(a);
,合并会产生以下 SQL 语句,
Hibernate:
delete
from
A_B
where
AID=?
Hibernate:
insert
into
A_B
(AID, BID)
values
(?, ?)
Hibernate:
insert
into
A_B
(AID, BID)
values
(?, ?)
我必须避免这些删除+更新。对于我的应用程序,我可以确保永远不会使用 hibernate 更新映射表。无论如何,都需要更新包含的实体。
所以我的问题是:是否可以映射此类“只读”集合并避免数据库更改?
最好的问候
托马斯
更新:
这些是我正在使用的表格和数据:
CREATE TABLE A (
AID INTEGER NOT NULL
)
DATA CAPTURE NONE ;
CREATE TABLE B (
BID INTEGER NOT NULL
)
DATA CAPTURE NONE ;
CREATE TABLE A_B (
AID INTEGER NOT NULL,
BID INTEGER NOT NULL
)
DATA CAPTURE NONE ;
INSERT INTO A (AID) VALUES (1);
INSERT INTO B (BID) VALUES (1);
INSERT INTO B (BID) VALUES (2);
INSERT INTO A_B (AID, BID) VALUES (1, 1);
INSERT INTO A_B (AID, BID) VALUES (1, 2);
此外,在执行合并之前还需要初始化集合:
a.getBs().size();
注意:我已将上面的行添加到原帖也是。
is it possible to create relations in hibernate / jpa that are fetched when the containing entity is fetched but will never ever result in any db updates, when the containing entity is saved? I'll try to make the requirement clear by an example.
I have a simple entity B
@Entity
public class B {
private int bId;
@Id
public int getBId() {
return bId;
}
public void setBId(int aId) {
bId = aId;
}
}
And another entity A, which contains a uni-directional many-to-many mapping to this class.
@Entity
public class A {
private int aId;
private List<B> bs;
@Id
public int getAId() {
return aId;
}
public void setAId(int aId) {
this.aId = aId;
}
@ManyToMany
@JoinTable(name = "A_B",
joinColumns = {@JoinColumn(name = "AID")},
inverseJoinColumns = {@JoinColumn(name = "BID")}
)
public List<B> getBs() {
return bs;
}
public void setBs(List<B> aBs) {
bs = aBs;
}
}
When entity A is fetched from db and merged afterwards as follows
A a = em.find(A.class, 1);
a.getBs().size();
em.merge(a);
, the merge results in the following SQL statements
Hibernate:
delete
from
A_B
where
AID=?
Hibernate:
insert
into
A_B
(AID, BID)
values
(?, ?)
Hibernate:
insert
into
A_B
(AID, BID)
values
(?, ?)
I have to avoid these deletes + updates. For my application I can ensure that the mapping table will never ever be updated using hibernate. Anyway, it is required to update the containing entity.
So my question is: Is it possible to map such "read-only" collections and to avoid db changes?
Best regards
Thomas
Update:
These are the tables and the data I'm using:
CREATE TABLE A (
AID INTEGER NOT NULL
)
DATA CAPTURE NONE ;
CREATE TABLE B (
BID INTEGER NOT NULL
)
DATA CAPTURE NONE ;
CREATE TABLE A_B (
AID INTEGER NOT NULL,
BID INTEGER NOT NULL
)
DATA CAPTURE NONE ;
INSERT INTO A (AID) VALUES (1);
INSERT INTO B (BID) VALUES (1);
INSERT INTO B (BID) VALUES (2);
INSERT INTO A_B (AID, BID) VALUES (1, 1);
INSERT INTO A_B (AID, BID) VALUES (1, 2);
In addition the collection also needs to be initialized before the merge is performed:
a.getBs().size();
Note: I've added the line from above to the original post, too.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如评论中所写,我最初无法重现该行为。在不改变
B
集合的情况下,Hibernate 只是更新 A,而保持连接表不变。但是,通过更改B
的集合(例如添加B
),我可以获得先删除后插入的行为。我不知道这是否说明了您的情况,但这是我的解释...当使用
Collection
或List
没有时@IndexColumn
(或@CollectionId
),您将得到 Bag 语义 及其所有缺点:当您删除元素或更改集合时,Hibernate 首先删除所有元素,然后插入(它没有办法维持秩序)。因此,为了避免这种行为,请使用:
Set语义(即,如果不需要
List
,则使用Set
, 95% 的情况都是如此)。带主键的包语义(即使用
List
和@CollectionId
) - 我没有对此进行测试。true 列表语义< /a> (即使用带有
@org.hibernate.annotations.IndexColumn
的List
或 JPA 2.0 等效的@OrderColumn
(如果您使用的是JPA 2.0)如果您不需要
List
,那么选项 1 是显而易见的选择。如果你这样做,我只测试了选项3(感觉更自然),你可以像这样实现(需要一个额外的列和连接表中(B_ID,BS_ORDER)的唯一约束):并且Hibernate将更新
BS_ORDER更新/删除
列。Bs
时所需的参考资料
是的,我知道这就是您所要求的,但是:
我认为您关心的是删除然后插入,并且您问题的“只读部分”看起来像是真正问题的丑陋解决方法。
“额外”的要求,即不保存已修改的持久集合的状态(这是不寻常的,当你有一个持久集合时,如果你修改它的状态,你通常希望保存它)并不清楚,至少对我来说不是。
无论如何......关于第二点,Hibernate 的 @Immutable 注释不适合这里(它不允许所有修改,如果有的话抛出异常)。但也许您可以处理集合的临时副本,而不是修改持久副本?
As written in a comment, I couldn't initially reproduce the behavior. Without altering the collection of
B
, Hibernate was just updating A, leaving the join table untouched. However, by altering the collection ofBs
(e.g. adding aB
), I could get the DELETE then INSERT behavior. I don't know if this illustrates your scenario but here is my explanation...When using a
Collection
orList
without an@IndexColumn
(or a@CollectionId
), you get Bag semantic with all their drawbacks: when you remove an element or alter the collection, Hibernate first delete all elements and then insert (it has no way to maintain the order).So, to avoid this behavior, either use:
Set semantic (i.e. use a
Set
if you don't need aList
, which is true for 95% of the cases).Bag semantic with primary key (i.e. use a
List
with a@CollectionId
) - I didn't test this.true List semantic (i.e. use a
List
with a@org.hibernate.annotations.IndexColumn
or the JPA 2.0 equivalent@OrderColumn
if you are using JPA 2.0)The Option 1 is the obvious choice if you don't need a
List
. If you do, I only tested Option 3 (feels more natural) that you would implement like this (requires an extra column and a unique constraint on (B_ID, BS_ORDER) in the join table):And Hibernate will update the
BS_ORDER
column as required upon update/removal ofBs
.References
Yes, I know that this is what you were asking for but:
I thought that your concern was the DELETE then INSERT and the "read only part" of your question was looking like an ugly workaround of the real problem.
The "extra" requirement i.e. not saving the state of a persistent collection that has been modified (which is unusual, when you have a persistent collection, you usually want to save it if you modify its state) wasn't clear, at least not for me.
Anyway... Regarding the second point, Hibernate's
@Immutable
annotation won't fit here (it disallows all modifications, throwing an exception if any). But maybe you could work on a transient copy of the collection instead of modifying the persistent one?