【问题标题】:Flush() required for multiple Eclipselink merges in the same transaction?同一事务中的多个 Eclipselink 合并需要 Flush()?
【发布时间】:2012-01-22 06:52:29
【问题描述】:

我在单个事务中遇到多个EntityManager.merge() 调用的问题。这是使用 Oracle 数据库。两个对象都不存在。实体:

public class A {
    @Id
    @Column("ID")
    public Long getID();

    @OneToOne(targetEntity = B.class)
    @JoinColumn("ID")
    public B getB();
}

public class B {
    @Id
    @Column("ID")
    public Long getID();
}

合并代码如下所示:

@Transactional
public void create(Object A, Object B) {
    Object A = entitymanager.merge(A);
    B.setId(A.getId());
    entitymanager.merge(B);
}

对象 A 的 ID 是通过序列生成的,并在 B 上正确设置。查看日志,在调用 B 上的合并之前调用 A 上的合并。有一个从 A 到 B 的 @OneToOne 映射。但是,在方法结束时,当它实际提交时,它会尝试在 B 上执行 INSERT,然后再对 A 执行 INSERT,这会引发 IntegrityConstraintViolation因为“找不到父键”。

如果我在第二次合并之前添加entitymanager.flush(),它可以正常工作。

@Transactional
public void create(Object A, Object B) {
    Object A = entitymanager.merge(A);
    entitymanager.flush();
    B.setId(A.getId());
    entitymanager.merge(B);
}

但是,flush() 是一项不必要的昂贵操作。所有这些都应该发生在同一个事务中(@Transactional 的默认传播是Propagation.REQUIRED)。

知道为什么没有flush() 就不能工作吗?为什么即使 A 上的合并发生在 B 上的合并之前,COMMIT 上的实际 INSERT 却相反?

【问题讨论】:

    标签: oracle10g eclipselink entitymanager


    【解决方案1】:

    除非有双向@OneToOne 注释,否则合并似乎是按字母顺序排列的(至少,这是一种可能性)。

    以前:

    public class A {
        @OneToOne(targetEntity = B.class)
        @JoinColumn("ID")
        public B getB();
    }
    
    public class B {
        @Id
        @Column("ID")
        public Long getID();
    }
    

    现在:

    public class A {
        @OneToOne(targetEntity = B.class)
        @JoinColumn("ID")
        public B getB();
    }
    
    public class B {
        @Id
        @Column("ID")
        public Long getID();
    
        @OneToOne(targetEntity = A.class)
        @JoinColumn("ID")
        public A getA();
    }
    

    对于我正在做的事情,B有办法获得A并不重要,但我仍然不明白为什么A中的注释还不够。

    【讨论】:

      【解决方案2】:

      正如弗兰克所说,您显示的代码没有设置 A->B 关系,因此提供者无法知道这个 B 对象需要在 A 之前插入。其他关系可能会导致它认为一般需要先插入A。

      在某些数据库上可以进行延迟约束,是指将数据库设置为将约束处理延迟到事务结束。如果您推迟或删除约束,您可以查看生成的 SQL 是否正确,或者是否存在其他代码问题和遗漏的映射。

      【讨论】:

      • 我应该指定在 A 中有一个连接关系设置。这似乎还不够。不遵循合并顺序的 Eclipselink 对我来说也没有意义。
      【解决方案3】:

      如果实体 A 和 B 没有关系(即 @OneToOne、@OneToMany、...),则持久性提供程序无法计算正确的插入顺序。 IIRC EclipseLink 在向数据库发送 SQL 语句时不使用对象创建顺序。

      如果您不想使用 flush(),只需将您的约束设置为延迟。

      【讨论】:

      • A 和 B 确实有 @OneToOne 关系,据我所知,对象 A 上的方法如下:@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE, optional = true, targetEntity = B.class) public B getB() {}
      • 通过“将您的约束设置为延迟”,您是否引用了数据库中没有外键约束?这似乎也不是一个好的选择。我不得不认为必须有一种方法来强制 Eclipselink 中 INSERT 语句的顺序。
      猜你喜欢
      • 1970-01-01
      • 2015-04-20
      • 1970-01-01
      • 2015-04-19
      • 1970-01-01
      • 2019-05-19
      • 2011-06-19
      • 2021-04-01
      • 1970-01-01
      相关资源
      最近更新 更多