微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

通过数据库中的唯一约束重复检查不起作用

如何解决通过数据库中的唯一约束重复检查不起作用

我有一个Spring Boot服务,该服务应在与Oracle DB的事务中保留两种类型的多个实体。第一个实体类型的表格非常庞大(每天3个Mio条目,已分区,...),我遇到了需要对重复项做出反应的问题。我使用一些字段在其上创建哈希,并且在数据库中对该字段有唯一约束。我认为只保存一个实体并对ConstraintViolationException做出反应是一个聪明的主意。基于保存第一个实体列表的结果,我需要创建第二个实体并保存它,但是它将回滚所有内容。 我现在的问题是这种方法通常是错误的,还是可以,并且存在一些小问题?如果通常是错误的话,那么我应该如何进行重复检查(不能选择“先期选择”)?

这里有一些伪代码可以使您更好地理解

@Entity
public class Foo{

    public String uniqueHash;
    
    // couple of other properties that will be used to calculate the hash
}

@Entity
public class Bar{

    private List goodIds;
    private List badIds;
    
    public Bar(List goodIds,List badIds){
        this.goodIds = goodIds;
        this.badIds = badIds;
    }
}

@Repository
@Transactional(norollbackFor = PersistenceException.class)
public interface FooRepository extends JpaRepository<Foo,String> {
  Foo saveAndFlush(Foo f) throws PersistenceException;
}

@Repository
@Transactional(norollbackFor = PersistenceException.class)
public interface BarRepository extends JpaRepository<Bar,String> {
  Bar saveAndFlush(Bar b) throws PersistenceException;
}

SomeService

@Transactional(norollbackFor = PersistenceException.class)
public void doSomething(List<Foo> foos){

    List<String> goodIds = new ArrayList();
    List<String> badIds = new ArrayList();
        
    for (Foo foo : foos) {
      try {     
        fooRepository.saveAndFlush(foo);
        goodIds.add(foo.getId());
      } catch (PersistenceException e) {
        if (e.getCause() instanceof ConstraintViolationException) {
          badIds.add(foo.getId);
        } else {
          throw e;
        }
      }
    }
    barRepository.saveAndFlush(new Bar(goodIds,badIds));
}

解决方法

最后,我找到了一种实现预期行为的方法,甚至更好的是,我摆脱了这些“ noRollBackFor”属性。我只是重组了流程,并尝试将所有内容保存在事务中,如果失败,则在调用方法上捕获到异常,将“清除”输入,然后再次(递归)调用事务方法。这些重复情况很少见(每10k Foo实例发生一次),因此从性能角度来看,可以进行这些后续事务。这又是更改后的伪代码

@Entity
public class Foo{

    public String uniqueHash;
    
    // couple of other properties that will be used to calculate the hash
}

@Entity
public class Bar{

    private List goodIds;
    private List badIds;
    
    public Bar(List goodIds,List badIds){
        this.goodIds = goodIds;
        this.badIds = badIds;
    }
    
    public List getGoodIds(){
      return goodIds;
    }
    
    public List getBadIds(){
      return badIds;
    }
}

@Repository
public interface FooRepository extends JpaRepository<Foo,String> {
}

@Repository
public interface BarRepository extends JpaRepository<Bar,String> {
}

public class FooException extends RuntimeException {

  private final Foo foo;

  public FooException(String message,Foo foo) {
    super(message);
    this.foo = foo;
  }
  
  public getFoo(){
  return foo;
  }
}

SomeService

public void doSomething(List<Foo> foos,Bar bar){
        try{
        doSomethingTransactional(foos,bar);
        }
        catch (FooException e) {           
          bar.getBadIds().add(e.getFoo().getId());
          foos.remove(foo);
          doSomething(foos,bar);
        }
}


@Transactional
public void doSomethingTransactional(List<Foo> foos,Bar bar){
        
    for (Foo foo : foos) {
      try {     
        fooRepository.saveAndFlush(foo);
        bar.getGoodIds.add(foo.getId());
      } catch(DataAccessException e) {
        if (e.getCause() instanceof ConstraintViolationException 
        && ((ConstraintViolationException) e.getCause()).getConstraintName().contains("Some DB Message")) {
          throw new FooException("Foo already exists",foo);
        } else {
          throw e;
        }
      }
    }
    barRepository.saveAndFlush(bar);
}
,

为此,您可能可以使用自定义@SQLInsert来利用Oracle的MERGE语句。另请参见https://stackoverflow.com/a/64764412/412446

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。