JPA 批量插入导致 ConstraintViolationException
JPA batch insert causing ConstraintViolationException
我正在从事主要使用 EJB 和 JPA 的项目,但我遇到了不应发生的 ConstraintViolationException 问题。
首先,我有 MyEntity
class 和 @Id 以及一些独特的字段。
我有 @Stateless MyEntityRepository
class 和 find()
方法,通过调用 EntityManager get() 方法只是 returns MyEntity(或 null)。
另一个@Stateless bean 是 SaveEntityBean
:
@Stateless
public class SaveEntityBean {
@Inject
EntityManager em;
@Inject
MyEntityRepository repository;
public void saveEntity(MyEntity me) {
if(repository.find(me) == null) {
//the place with ConstraintViolationException
em.persist(me);
}
}
public void saveEntities(List<...> entities) {
for(MyEntity me: entities)
saveEntity(e);
}
}
并且方法 saveEntities(List<...> entities)
是从另一个 bean 调用的:
@Stateless
@Startup
public class SaveEntityBean {
@Inject
SaveEntityBean saveBean;
//...
@Schedule(hour = "*", minute = "*", second="*/5")
@AccessTimeout(unit = TimeUnit.MINUTES, value = 1)
public void mainLogicMethod() {
List<MyEntity> entities = io.calculateAndGetEntities();
saveBean.saveEntities(entities);
}
}
其中io.calculateAndGetEntities()
方法是长IO工作。问题是有时我得到 org.hibernate.exception.ConstraintViolationException
我认为不应该发生,因为我在调用 persist() 方法之前检查了 MyEntityRepository.find(me) != null
条件。
我唯一的想法是提交之间有一些延迟,所以在条件检查中调用 MyEntityRepository.find(me)
方法后,提交发生,紧接着 persist()
方法抛出异常。
请给我任何阅读和学习以及如何解决问题的建议。
编辑:
我发现这是线程的问题,所以解决方案可能是锁定 write/read.
好吧,一般在高并发环境下,这个问题真的很难解决,简单的Locking是行不通的(而且你不想使用table锁)。现在我使用来自 org.hibernate.annotations.SQLInsert
的 Hibernate @SQLInsert
注释,所以我的实体看起来像这样:
@SQLInsert(sql = "INSERT INTO my_entity_table(a, b, c) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE a=a;")
public class MyEntity implements Serializable {...}
当然会有问题,当您想向用户显示一些错误消息时,但在我的情况下它已经足够了。
据我所知,此 SQL 语句仅在 MySQL 中受支持,在其他 RDBMS 中,您应该使用不同的方法。
我正在从事主要使用 EJB 和 JPA 的项目,但我遇到了不应发生的 ConstraintViolationException 问题。
首先,我有 MyEntity
class 和 @Id 以及一些独特的字段。
我有 @Stateless MyEntityRepository
class 和 find()
方法,通过调用 EntityManager get() 方法只是 returns MyEntity(或 null)。
另一个@Stateless bean 是 SaveEntityBean
:
@Stateless
public class SaveEntityBean {
@Inject
EntityManager em;
@Inject
MyEntityRepository repository;
public void saveEntity(MyEntity me) {
if(repository.find(me) == null) {
//the place with ConstraintViolationException
em.persist(me);
}
}
public void saveEntities(List<...> entities) {
for(MyEntity me: entities)
saveEntity(e);
}
}
并且方法 saveEntities(List<...> entities)
是从另一个 bean 调用的:
@Stateless
@Startup
public class SaveEntityBean {
@Inject
SaveEntityBean saveBean;
//...
@Schedule(hour = "*", minute = "*", second="*/5")
@AccessTimeout(unit = TimeUnit.MINUTES, value = 1)
public void mainLogicMethod() {
List<MyEntity> entities = io.calculateAndGetEntities();
saveBean.saveEntities(entities);
}
}
其中io.calculateAndGetEntities()
方法是长IO工作。问题是有时我得到 org.hibernate.exception.ConstraintViolationException
我认为不应该发生,因为我在调用 persist() 方法之前检查了 MyEntityRepository.find(me) != null
条件。
我唯一的想法是提交之间有一些延迟,所以在条件检查中调用 MyEntityRepository.find(me)
方法后,提交发生,紧接着 persist()
方法抛出异常。
请给我任何阅读和学习以及如何解决问题的建议。
编辑: 我发现这是线程的问题,所以解决方案可能是锁定 write/read.
好吧,一般在高并发环境下,这个问题真的很难解决,简单的Locking是行不通的(而且你不想使用table锁)。现在我使用来自 org.hibernate.annotations.SQLInsert
的 Hibernate @SQLInsert
注释,所以我的实体看起来像这样:
@SQLInsert(sql = "INSERT INTO my_entity_table(a, b, c) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE a=a;")
public class MyEntity implements Serializable {...}
当然会有问题,当您想向用户显示一些错误消息时,但在我的情况下它已经足够了。 据我所知,此 SQL 语句仅在 MySQL 中受支持,在其他 RDBMS 中,您应该使用不同的方法。