首页 > 解决方案 > 如果在并发操作中不存在,则 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;

这是简单的插入逻辑:

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

当我通过并发线程调用它时,我得到了这个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]

我可以捕捉到这个异常,但 ConstraintViolationException 可以在唯一索引以外的任何情况下发生。

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

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

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

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

标签: javaspringhibernatejpaspring-data-jpa

解决方案


推荐阅读