在并发环境

时间:2018-05-16 08:02:46

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

我遇到了以下问题:

我有一个实体:

        @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 字段具有唯一性约束。

实体的相应存储库:

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. 如果主机确实存在,请返回主机
  3. 考虑 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);
      }
      

      }

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

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

      如果是这样,除了上面列出的任何建议或替代解决方案,我真的很感激,

      谢谢,欢呼

1 个答案:

答案 0 :(得分:0)

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

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

在HostService中,您可以使用以下方法:

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