【问题标题】:Spring data JPA: How to SELECT, if not found then create?Spring data JPA:如何选择,如果没有找到然后创建?
【发布时间】:2020-02-08 10:15:21
【问题描述】:

Spring data JPA中有没有简洁的方式来执行:

  1. 按字段选择记录
  2. 如果找到则返回
  3. 如果找不到,则创建一个新的并返回它

原子地?

@Transactional 对此有帮助吗? (我的意思是创建一个方法,标记它@Transactional,在里面执行select,if/else)

BTW 解决这个问题的一般纯SQL方式是什么? (让我们假设 MySql DB。)

【问题讨论】:

    标签: sql spring-data-jpa spring-transactions acid


    【解决方案1】:

    虽然您可以找到特定于供应商的解决方案,但您想要实现的目标没有纯 SQL。但是,您可以使用 Transactional 轻松实现此目的:

    class EntityService {
        private final entityRepository;
    
        // constructor omitted
    
        @Transactional
        public Entity getOrCreate(Entity entity, Long entityId) {
            return entityRepository.findById(entityId)
                .orElseGet(() -> entityRepository.save(entity));
        }
    }
    
    

    【讨论】:

    • 如果线程 1 和线程 2 都没有找到并同时到达orElseGet 点怎么办?它们都保存(插入)相同的数据
    • 如果您有竞争条件并且想要创建具有唯一字段的新实体,则只有一个保存可以完成,而另一个保存会失败并出现异常,例如ConstraintViolationException
    • 那么我想搜索的实体字段(我在问题中没有提到它是主键)应该有@Column(unique=true)吗?
    • 主键默认是唯一的——这就是 PK 的目的,它唯一地标识表中的一行。我会说 10 次中有 9 次你不必担心并发性,除非你的用例出于某种原因真的很特别。在非常罕见的情况下,这里发生竞争条件(相信我,这将非常罕见),请求将失败并且用户可以再次重试,您的数据库将保持一致状态。
    • 是的,默认情况下主键是唯一的。因此,如果我想通过另一个字段进行搜索,我需要标记字段@Column(unique=true) 是否足够? (然后用@Retryable 标记getOrCreate 方法让第二个线程再试一次并找到)?
    猜你喜欢
    • 2018-12-13
    • 2014-07-08
    • 2016-08-06
    • 1970-01-01
    • 2023-02-03
    • 2015-05-29
    • 1970-01-01
    • 2023-03-29
    • 2014-12-29
    相关资源
    最近更新 更多