刷新并在单个事务中持久存在多个约束违规

时间:2016-10-18 14:38:23

标签: java oracle jpa eclipselink spring-transactions

我正在尝试使用JPA(EclipseLink)在一个事务中保留许多记录。 Entry具有在DB上定义的主键。

我的Feed中有一些重复内容,所以我知道会有违规行为。我希望针对单个项目捕获约束违例异常并继续保持其他项目。

这是我的代码:

实体:

@Entity
@Table(name="TEST")
public class TestEntity {

    @Id
    private String name;

    public TestEntity() {

    }

    public TestEntity(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

操作:

@Transactional
public void test() {

    List<String> list = Arrays.asList("A", "B","C","D","D","E","F","G");//duplicated D

    for(String name: list) {
        try {
            TestEntity entity = new TestEntity(name);
            logger.info("" + entity);
            entityManager.persist(entity);
            logger.info("Persist done");
            entityManager.flush();
            logger.info("Flush done");
        } catch(RuntimeException e) {
            logger.warn("Entry {}  was skipped due to following exception {}.",  name, e.getLocalizedMessage());
        }
    }

    logger.info("Importing dictionary finished. ");
}

结果我得到了第二个D的约束违例异常(这就是我所期望的),但我也为下一个项目得到了这个例外,那是我不明白的事情。

我收到:内部异常:java.sql.SQLIntegrityConstraintViolationException:ORA-00001:违反了唯一约束(BBHDEV4.SYS_C0023890)

导致该问题的原因是什么?

2 个答案:

答案 0 :(得分:2)

问题是第二个D仍然是同一个Transaction / Unit of Work的一部分,所以在接下来的迭代中,hibernate会再次尝试保存它。

要解决此问题(使用vanilla JPA),您可以

  • 为每个要更新的项目创建新的交易。
  • 使用JPA update statement进行更新。

使用hibernate你有第三个选项就是使用无状态会话,但我不知道eclipselink是否具有无状态 entitymanager 的概念

答案 1 :(得分:2)

您的实体管理器维护一个由所有managed实体(即持久化上下文)和其对象的事务状态组成的状态。

因此,在刷新触发约束违规之后,D实体仍然被管理并处于失效状态,并且将在下一次显式刷新调用时再次刷新(您不应该显式调用flush)或在当前事务结束时。你必须在恢复循环之前手动从持久化上下文(detach())中逐出D实体。

无论如何,如果所有操作都是同一事务的一部分并且错误被抛出事务边界,事务管理器将自动回滚整个事务。

交易是一个工作单元,如果您不希望您的不同操作独立,您必须在不同的交易中执行它们。通常,实体管理器范围与事务管理器范围相同(至少在由容器管理时,即注入@PersitenceContext),您将立即解决这两个问题。