JPA如果并发操作中不存在则插入实体

ani*_*777 5 java spring hibernate jpa spring-data-jpa

我有一个简单的用例:

我想插入一个不存在的实体。

这是实体类,它组合了唯一的列:

@Entity
@Table(uniqueConstraints = {
        @UniqueConstraint(columnNames = {
                "firstName", "lastName"
        })
})
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;
Run Code Online (Sandbox Code Playgroud)

这是简单的插入逻辑:

 Optional<Customer> customer = customerRepository.findByFirstNameAndLastName("John", "Doe");
        if (!customer.isPresent()) {
            customerRepository.save(new Customer("John", "Doe"));
        }
Run Code Online (Sandbox Code Playgroud)

当我通过并发线程调用它时,我得到了这个ConstraintViolationException,这很明显

 insert into customer (first_name, last_name, id) values (?, ?, ?) [23505-200]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

 org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PUBLIC.UKKIYY7M3FWM4VO5NIL9IBP5846_INDEX_5 ON PUBLIC.CUSTOMER(FIRST_NAME, LAST_NAME) VALUES 8"; SQL statement:
 insert into customer (first_name, last_name, id) values (?, ?, ?) [23505-200]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:459) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:429) ~[h2-1.4.200.jar:1.4.200]
Run Code Online (Sandbox Code Playgroud)

我可以捕获此异常,但 ConstraintViolationException 可能发生在除唯一索引之外的任何场景中。

我还尝试了 JPA 的乐观锁定,但它似乎只在更新和删除操作中起作用,而在插入操作中不起作用。

我不想要本机查询(例如ON DUPLICATE KEY在 mysql 中、ON CONFLICT在 PostgresSQL 中),因为我想让操作在 JPA 本身中处理。

另外,我无法使其线程安全,因为应用程序将在多个 JVM 上运行。

有什么办法可以处理这个问题,比如锁定插入或至少更新相同的键?