【问题标题】:Implementing findOrCreate strategy with spring data in concurrent environment在并发环境中使用spring数据实现findOrCreate策略
【发布时间】:2018-10-26 04:26:29
【问题描述】:

我面临以下问题:

我有一个实体:

        @Table(name = "host",
                uniqueConstraints = 
    { 
@UniqueConstraint(name = "uq_host_0", 
        columnNames = {"orgName", "hostName"})}
)
class Host {

   private String id;

   private String hostName;
   private String orgName;

   //gets
   //sets
   //constructors
   //...

} 

此实体在 orgName + hostName 字段上具有唯一性约束。

以及实体对应的Repository:

public interface HostRepository extends JpaRepository<Host, String> {

    Page<Host> findByOrgId(String orgId, Pageable pageable);

    Host findOneByOrgNameIdAndId(String orgName, String id);

    Host findOneByOrgNameAndHostName(String orgName, String hostName);

   //..
}

我想用 findOrCreate 方法创建一个服务:

  1. 如果主机不存在则创建新主机
  2. 如果主机确实存在,则返回主机

考虑到 hostName + orgName 字段的唯一性约束。

这个方法应该假设它可以在同一应用程序的多个不同实例以及不同线程中执行。

目前我想出了两个解决方案:

  1. 使用 Propagation = RequiresNew 使用单独的方法进行创建

    @Service
     public class HostService {
    
       @Autowired
       private HostRepository hostRepository;
    
        @Transactional
        public Host findOrCreate(Host host) {
    
           try {
    
            return create(host);
    
          } catch(ConstraintViolationException e) {
            //means the host has already been created by other transaction
             return hostRepository.findFirstByOrgNameAndHostName(host.getHostName(), host.getOrgName());
          }
    
        }
    
     @Transactional(propagation = Propagation.REQUIRES_NEW)
     public Host create(Host host) {
         //constraint violation may be thrown
          hostRepository.save(host);
       }
    }
    
  2. 在一种方法中执行所有逻辑,但隔离级别 = 可序列化:

    @Service
    public class HostService {
    
     @Autowired
     private HostRepository hostRepository;
    
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public Host findOrCreate(Host host) {
    
       Optional<Host> existing = Optional.ofNullable(hostRepository.findOneByOrgNameAndHostName(host.getOrgName(), host.getHostName()));
    
       if(existing.isPresent()) {
          return existing;
       }
    
       return hostRepository.save(host);
    }
    

    }

在我看来,这两个选项都可以在并发环境中工作,而第一个选项更可取,因为工作速度更快。但是,我担心我可能会错过水下岩石。

以前有人遇到过这个问题吗?

如果是这样,我非常感谢您提供除上述之外的任何建议或替代解决方案,

谢谢,干杯

【问题讨论】:

    标签: spring hibernate spring-data spring-data-jpa spring-transactions


    【解决方案1】:

    我有个主意。 您可以在 HostRepository 中定义一种方法,例如:

    Optional<Host> findByOrgNameAndHostName(String orgName, String hostName)
    

    而在HostService中,可以使用方法:

    Host host = hostRepository.findByOrgNameAndHostName("hoge", "fuga")
                   .orElseGet(() -> hostRepository.save(new Host("hoge", "fuga")))
    

    【讨论】:

    • 这可行,但它仍然不是线程安全的。如果另一个进程或线程在findByOrgNameAndHostName 完成之后但在save 发生之前插入新的Host("hoge","fuga"),则会出现重复键异常或重复行。
    猜你喜欢
    • 2016-10-30
    • 2013-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2015-02-06
    • 2021-11-14
    相关资源
    最近更新 更多