5

我需要删除一个实体并创建另一个:

@Stateless
public class StatelessBean {
  @PersistenceUnit(unitName = "Unit001")
  EntityManagerFactory emf;

  protected void test() {
    EntityManager em = emf.createEntityManager();
    MyObj obj1 = em.find(MyObj.class, 100);
    MyObj obj2 = new MyObj();
    obj2.setKey("the same unique key as in obj1");
    em.remove(obj1);
    // em.flush(); 
    em.persist(obj2); // works fine when flush() is uncommented
    em.close();
  }
}

如果我留下em.flush()评论,那么我得到com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException(新旧对象具有相同的键值)

这种异常行为的原因可能是什么?

服务器:Glassfish 3.1.2

Eclipse 持久性服务 - 2.3.2.v20111125-r10461

持久性.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
  <persistence-unit name="Unit001">
    <jta-data-source>jdbc/Unit001DS</jta-data-source>
    <properties>
      <property name="eclipselink.logging.level" value="INFO"/>
      <property name="eclipselink.target-database" value="MySQL"/>
    </properties>
  </persistence-unit>
</persistence>

连接池:

${ASADMIN} --port ${DOMAIN_ADMIN_PORT}  create-jdbc-connection-pool --datasourceclassname com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource --restype javax.sql.ConnectionPoolDataSource --property "User=user:Password=pass:URL=jdbc\:mysql\://${DB_ADDRESS}/db" Unit001DS
${ASADMIN} --port ${DOMAIN_ADMIN_PORT}  create-jdbc-resource --connectionpoolid Unit001DS jdbc/Unit001DS
4

2 回答 2

6

原因可能是 Eclipselink 在提交期间更改了操作顺序,如Eclipselink 文档中所述:

默认情况下,EclipseLink 在删除操作之前首先执行插入和更新操作,以确保保持引用完整性。这是首选方法。

您可以通过刷新(正如您已经发现的那样)或通过为 Eclipselink 设置特殊参数来更改此行为:

如果您被迫通过删除对象并插入替换来替换具有唯一约束的对象,如果插入操作发生在删除操作之前,则可能会导致约束冲突。在这种情况下,调用setShouldPerformDeletesFirst在插入操作之前执行删除操作。

于 2012-04-12T09:17:17.590 回答
1

EclipseLink 维护和优化提交顺序以:

1 - 维护参照完整性约束:如果您插入两个相关对象(或许多对象的一个​​大互连图),则必须以非常具体且不明显的顺序插入/更新/删除它们以维护参照完整性约束。在某些情况下,甚至可能需要插入一个 shell 对象并在解决循环依赖之后更新其外键。删除必须最后发生,因为用于引用已删除对象的对象必须首先更新关系。

2 - 组操作和表访问:这避免了数据库死锁,并允许优化使用批量写入。

3 - 一致地按 id 排序更新/删除:这是可配置的,并避免潜在的数据库死锁。

如果 EclipseLink 只是盲目地按照应用程序执行操作的顺序编写操作,那么您将不得不自己管理所有这些。我真的怀疑你真的想这样做。对于您真正需要的情况,您可以使用flush()。

于 2012-04-12T12:33:01.730 回答