如何解决通过数据库中的唯一约束重复检查不起作用
我有一个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 举报,一经查实,本站将立刻删除。