[英]Bi-directional @OneToOne with ONLY CascadeType.REMOVE saves changes: that is not wanted
The problem: 问题:
I have two entities, SomeEntity
and SomeEntityInfo
in a bi-directional one-to-one relation with only CascadeType.REMOVE
cascading set. 我只有两个
CascadeType.REMOVE
级联集, SomeEntity
具有双向一对一关系的SomeEntity
和SomeEntityInfo
。
If SomeEntity.someEntityInfo
is changed, and SomeEntity
is (already existing) saved -> there shouldn't happen a cascading database update to the SomeEntityInfo
table / object. 如果更改
SomeEntity.someEntityInfo
,并且保存(已经存在) SomeEntity
>不应对SomeEntityInfo
表/对象进行级联数据库更新。
But instead, the related entity is updated too 但是相反,相关实体也被更新
edit / update In other words: I want the SomeEntityInfo
to be "(somewhat-)immutable". 编辑/更新换句话说:我希望
SomeEntityInfo
是“(某种程度上)不可变的”。 It should be created when SomeEntity
is created, but not updated / version checked - optimistic locking - if SomeEntity
is re-saved. 它应在创建
SomeEntity
创建,但不进行更新/检查版本-乐观锁定-如果重新保存SomeEntity
。
what i did so far 我到目前为止所做的
Returning a copy of SomeEntityInfo
in the getter of SomeEntity
results in 在
SomeEntityInfo
的getter中返回SomeEntityInfo
的副本SomeEntity
导致
a new object was found through a relationship that was not marked cascade PERSIST [..]
通过未标记为层叠PERSIST [..]的关系找到了一个新对象
(desperately)annotating with (拼命地)用
@OneToOne(cascade = { CascadeType.REMOVE }) @JoinColumn(name = "someentityinfo_id", updatable = false, insertable = true) private SomeEntityInfo someEntityInfo;
is related to the ID of the foreign key, not to the data inside the referenced object 与外键的ID有关,与所引用对象内部的数据无关
Example - DB schema (mysql db) 示例-数据库架构(mysql db)
CREATE TABLE someentity (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
version INT(11) NULL DEFAULT NULL,
someentityinfo_id INT(11) UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (id)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
CREATE TABLE someentityinfo (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
version INT(11) NULL DEFAULT NULL,
status varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
ALTER TABLE someentity
ADD INDEX FK_someentityinfo_id (someentityinfo_id);
ALTER TABLE someentity
ADD CONSTRAINT FK_someentityinfo_id FOREIGN KEY (someentityinfo_id) REFERENCES someentityinfo (id);
Entity Code 实体代码
SomeEntity SomeEntity
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Version;
@Entity
@Table(name = "someentity")
public class SomeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Version
private Integer version;
@OneToOne(cascade = { CascadeType.REMOVE })
@JoinColumn(name = "someentityinfo_id")
private SomeEntityInfo someEntityInfo;
[getter/setter]
}
SomeEntityInfo SomeEntityInfo
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Version;
@Entity
@Table(name = "someentityinfo")
public class SomeEntityInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Version
private Integer version;
private String status;
@OneToOne(mappedBy = "someEntityInfo")
private SomeEntity someEntity;
[getter/setter]
}
Used test scenario 二手测试方案
// create and persist entity and its info object
em.getTransaction().begin();
SomeEntity se = new SomeEntity();
SomeEntityInfo seInfo = new SomeEntityInfo();
se.setSomeEntityInfo(seInfo);
seInfo.setSomeEntity(se);
seInfo.setStatus("status 1");
em.persist(se);
em.persist(se.getSomeEntityInfo());
em.getTransaction().commit();
// load created entity, modify its info and expect
// to NOT update the info object while saving the entity again
em.getTransaction().begin();
SomeEntity loadedSe = em.find(SomeEntity.class, Integer.valueOf(se.getId()));
loadedSe.getSomeEntityInfo().setStatus("do not cascade update");
// as Chris said below, not necessary to explicit save managed entity again
// em.persist(loadedSe);
em.getTransaction().commit();
Environment 环境
EclipseLink, version: Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd EclipseLink,版本:Eclipse Persistence Services-2.5.2.v20140319-9ad6abd
Additional Information 附加信息
The specification ( http://wiki.eclipse.org/Introduction_to_EclipseLink_JPA_(ELUG)#.40OneToOne ) sais: 规范( http://wiki.eclipse.org/Introduction_to_EclipseLink_JPA_(ELUG)#.40OneToOne )表示:
cascade – By default, JPA does not cascade any persistence operations to the target of the association.
级联–默认情况下,JPA不会将任何持久性操作级联到关联目标。
that is not the case (changes are cascaded).. what am I missing here? 情况并非如此(更改是级联的)..我在这里缺少什么?
SomeEntityInfo
instance is managed , meaning any changes to it will be saved, unrelated to cascading. SomeEntityInfo
实例是受管理的 ,这意味着对它的任何更改都将被保存,与级联无关。 Take a look at this answer for more details. 请查看此答案以获取更多详细信息。
Changes aren't actually cascaded. 更改实际上不是级联的。 What
CascadeType.REMOVE
means is essentially ON DELETE CASCADE
, ie remove any orphaned rows. CascadeType.REMOVE
含义实质上是ON DELETE CASCADE
,即删除任何孤立的行。 While there is a ON UPDATE CASCADE
it is less used and only affects the other end of the foreign key. 尽管存在
ON UPDATE CASCADE
,但使用较少,仅影响外键的另一端。
If you make changes to an object that is handled by the ORM, the changes will be persisted. 如果您对由ORM处理的对象进行更改,则更改将被保留。 However it has nothing to do with cascading.
但是,它与级联无关。
So if you don't want to update SomeEntityInfo
in the database, don't update it in your code. 因此,如果您不想更新数据库中的
SomeEntityInfo
,请不要在代码中对其进行更新。 EclipseLink is doing its job perfectly fine here. EclipseLink在这里做得很好。
The answers below are correct, I just wanted to add that the em.persist() isn't doing anything - it is a no-op because the loadedSe instance is a managed entity. 下面的答案是正确的,我只是想补充一点,em.persist()没有做任何事情-它是空操作,因为loadSe实例是一个托管实体。 If it were not a managed entity, the JPA specification requires that calling persist on an existing, unmanaged entity instance result in an exception - either on the persist or flush call, or when the transaction tries to commit.
如果不是托管实体,则JPA规范要求在现有的非托管实体实例上进行持久化调用会导致异常-持久化或刷新调用,或者在事务尝试提交时。
All changes to managed entities are synchronized to the database when flush is called or the transaction commits. 当调用flush或提交事务时,对托管实体的所有更改都将同步到数据库。 All entities accessed from an EntityManager are managed by that EntityManager context until they are cleared.
从EntityManager访问的所有实体均由该EntityManager上下文管理,直到清除它们为止。 If you called em.clear before making the changes, the changes would not have been picked up by the commit.
如果在进行更改之前调用了em.clear,则提交将不会拾取更改。 Another alternative is to use the read-only query hint on the find operation: http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Query_Hints#Read_Only
另一种选择是在查找操作上使用只读查询提示: http : //wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Query_Hints#Read_Only
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.