【发布时间】:2021-02-22 14:39:20
【问题描述】:
我有一个 Spring Boot 服务,它应该在事务中将两种类型的多个实体持久化到 Oracle DB。第一个实体类型的表很大(每天 3 个 Mio。条目,分区,...),我有一个问题,我需要对重复项做出反应。我使用一些字段来创建哈希,并且我在该字段的数据库中具有唯一约束。我认为按实体保存AndFlush 并对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));
}
【问题讨论】:
-
您可能想查看错误表,看看是否满足您的需求。错误表的一个问题是所有列值都转换为 VARCHAR2(4000)
-
为什么不在插入之前检查该实体是否存在?
-
@ggr 所说的,特别是考虑到唯一索引查找应该非常快
-
我有几个服务实例,并且担心会出现不利条件,即多个服务会收到相同的消息,并以具有重复的结尾。此外,我不想将数据库交互加倍。问题是我的方法是否完全不可行,因为我读过一些线程已经表明,无论你做什么,事务中的异常都会回滚(尽管我认为我的场景是一个非常可控的场景)
-
@ggr 我知道几个服务接收到相同的消息,这不在我的手中(我的意思是客户端不在我们的控制范围内)。我主要关心的是在罕见的情况下将数据库交互加倍。我可能每 10K 个实例都有一个重复的 Foo,所以我最终决定更改数据处理方式,而是像您或多或少地提议的那样重试事务。我添加了代码作为答案以作为参考。
标签: java oracle spring-boot hibernate spring-data-jpa