在单个事务中刷新和持久存在多个约束冲突
Multiple constraint violation on flush and persist in a single transaction
我正在尝试使用 JPA (EclipseLink) 在一个事务中保存多条记录。该条目具有在数据库上定义的主键。
我的 Feed 中有一些重复项,所以我知道会违反约束条件。我想捕获单个项目的 Constraint Violation Exception 并继续保留其他项目。
这是我的代码:
实体:
@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)
是什么导致了这个问题?
问题是第二个 D
仍然是同一个事务 / Unit of Work 的一部分,因此在接下来的迭代中,hibernate 会再次尝试保存它。
要解决此问题(使用原始 JPA),您可以
- 为每个要更新的项目创建一个新交易。
- 使用 JPA 进行更新 update statement。
对于 hibernate,你有第三种选择,即使用无状态会话,但我不知道 eclipselink 是否有无状态的概念 entitymanager
您的实体管理器维护由所有 managed 实体组成的状态(即持久性上下文)及其对象的事务状态。
因此在刷新触发约束违规后,D 实体仍处于托管状态并处于陈旧状态,将在下一次显式刷新调用(您不应该显式调用刷新)或结束时再次刷新当前交易。在恢复循环之前,您必须手动从持久性上下文 (detach()
) 中逐出 D 实体。
无论如何,如果您的所有操作都是同一事务的一部分,并且在事务边界之外抛出错误,事务管理器将自动回滚整个事务。
一个事务是一个工作单元,如果你不想让你的不同操作是独立的,你必须在不同的事务中执行它们。通常实体管理器范围与事务范围相同(至少在由容器管理时,即注入@PersitenceContext),您将同时解决这两个问题。
我正在尝试使用 JPA (EclipseLink) 在一个事务中保存多条记录。该条目具有在数据库上定义的主键。
我的 Feed 中有一些重复项,所以我知道会违反约束条件。我想捕获单个项目的 Constraint Violation Exception 并继续保留其他项目。
这是我的代码:
实体:
@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)
是什么导致了这个问题?
问题是第二个 D
仍然是同一个事务 / Unit of Work 的一部分,因此在接下来的迭代中,hibernate 会再次尝试保存它。
要解决此问题(使用原始 JPA),您可以
- 为每个要更新的项目创建一个新交易。
- 使用 JPA 进行更新 update statement。
对于 hibernate,你有第三种选择,即使用无状态会话,但我不知道 eclipselink 是否有无状态的概念 entitymanager
您的实体管理器维护由所有 managed 实体组成的状态(即持久性上下文)及其对象的事务状态。
因此在刷新触发约束违规后,D 实体仍处于托管状态并处于陈旧状态,将在下一次显式刷新调用(您不应该显式调用刷新)或结束时再次刷新当前交易。在恢复循环之前,您必须手动从持久性上下文 (detach()
) 中逐出 D 实体。
无论如何,如果您的所有操作都是同一事务的一部分,并且在事务边界之外抛出错误,事务管理器将自动回滚整个事务。
一个事务是一个工作单元,如果你不想让你的不同操作是独立的,你必须在不同的事务中执行它们。通常实体管理器范围与事务范围相同(至少在由容器管理时,即注入@PersitenceContext),您将同时解决这两个问题。