问题:
我有两个实体,SomeEntity
和SomeEntityInfo
,双向一对一关系,只有CascadeType.REMOVE
级联设置。
如果SomeEntity.someEntityInfo
发生了更改,SomeEntity
已经(已经存在)已保存 - >不应该对SomeEntityInfo
表/对象进行级联数据库更新。
但相反,相关实体也会更新
编辑/更新
换句话说:我希望SomeEntityInfo
成为"(有些)不可变的"。它应该在创建SomeEntity
时创建,但不会更新/版本检查 - 乐观锁定 - 如果重新保存SomeEntity
。
到目前为止我做了什么
在SomeEntityInfo
的getter中返回SomeEntity
的副本会导致
通过未标记为级联PERSIST [...]
的关系找到了一个新对象
(拼命)用
注释@OneToOne(cascade = { CascadeType.REMOVE })
@JoinColumn(name = "someentityinfo_id", updatable = false, insertable = true)
private SomeEntityInfo someEntityInfo;
与外键的ID有关,而与引用对象内的数据无关
示例 - 数据库架构(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);
实体代码
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
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]
}
使用过的测试场景
// 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();
环境
EclipseLink,版本:Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd
其他信息
规范(http://wiki.eclipse.org/Introduction_to_EclipseLink_JPA_(ELUG)#.40OneToOne)sais:
cascade - 默认情况下,JPA不会将任何持久性操作级联到关联的目标。
情况并非如此(更改是级联的)..我在这里缺少什么?
答案 0 :(得分:1)
SomeEntityInfo
实例是托管,这意味着对其进行的任何更改都将被保存,与级联无关。有关详细信息,请查看this answer。
答案 1 :(得分:0)
更改实际上并非级联。 CascadeType.REMOVE
的含义基本上是ON DELETE CASCADE
,即删除任何孤立的行。虽然存在ON UPDATE CASCADE
,但它的使用较少,只会影响外键的另一端。
如果对ORM处理的对象进行更改,则更改将保持不变。但它与级联没有任何关系。
因此,如果您不想更新数据库中的SomeEntityInfo
,请不要在代码中更新它。 EclipseLink在这里完美地完成了它的工作。
答案 2 :(得分:0)
下面的答案是正确的,我只是想补充一点,em.persist()并没有做任何事情 - 它是一个无操作,因为loadedSe实例是一个被管实体。如果它不是托管实体,则JPA规范要求在现有的非托管实体实例上调用persist会导致异常 - 在persist或flush调用上,或者在事务尝试提交时。
在调用flush或事务提交时,对托管实体的所有更改都将同步到数据库。从EntityManager访问的所有实体都由该EntityManager上下文管理,直到它们被清除。如果您在进行更改之前调用了em.clear,则提交不会获取更改。另一种方法是在查找操作上使用只读查询提示:http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Query_Hints#Read_Only