只有CascadeType.REMOVE的双向@OneToOne保存更改:这是不需要的

时间:2014-11-27 11:34:54

标签: java jpa orm eclipselink

问题:

我有两个实体,SomeEntitySomeEntityInfo,双向一对一关系,只有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不会将任何持久性操作级联到关联的目标。

情况并非如此(更改是级联的)..我在这里缺少什么?

3 个答案:

答案 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