[英]How to handle Concurrent create if not exists with spring data JPA
我在我的 java-springboot 项目中使用 spring 数据 JPA。 我有一种方法可以处理许多相互关联的实体(其中一些是延迟加载的),并且该方法由多个线程并发运行。 我的问题是 - 当说线程 T1 来并试图为作者创建一本新书时,它会检查书信息是否已存在于数据库中并开始创建新记录,同时线程 T2 也尝试相同并且开始创建相同的资源,但是在持久化时,它会收到 DataIntegrtyViolation 异常。
@Entity
@Table(name = "BookInfo", uniqueConstraints = @UniqueConstraint(columnNames = {
"publisherName",
"bookTitle", "authorName" }), indexes = {
@Index(name = "BookInfoProviderIndex", columnList = "publisherName")
})
public class BookInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY/* , generator="table_gen" */)
private Long id;
@Column(name = "publisherName", nullable = false)
private String publisherName;
@Column(name = "bookTitle")
private String bookTitle;
@Column(name = "authorName")
private String authorName;
存储库 -
@Repository
public interface BookInfoRepository extends PagingAndSortingRepository<BookInfo, Long> {
public BookInfo findFirstByPublisherNameAndBookTitleAndAuthorName(String publisherName,
String bookTitle, String authorName);
}
以及导致问题的方法 -
@Service
public class AuthorService{
@Autowired
BookInfoRepository bookRepo;
@Transactional
public createAutoBiography(AuthorDTO dto){
BookInfo book = null;
book = bookRepo.findFirstByPublisherNameAndBookTitleAndAuthorName(dto.getPubName(), dto.getTitle(), dto.author());
if(book != null){
book = buildBookInfo(dto);
book = bookRepo.save(book);
}
...//other methods to create dependent resources for Author class and save the entities using the respective repositories
}
}
我尝试了以下 -
findFirstByPublisherNameAndBookTitleAndAuthorName()
库方法上添加了 @Lock(PESSIMISTIC_WRITE) - 这有效,但冲突不会很频繁,我担心它对整体功能的性能影响,以处理上述偶尔的冲突。此外,随机注意到 Author 对象本身的资源创建冲突,由于并发,它不是将新遇到的 BookInfo 添加到现有作者,而是使用新书信息为作者创建一个新条目
一般来说,使 JPA 事务线程安全的最佳方法是什么?
您可以简单地捕获DataIntegrtyViolation
并再次调用findFirstByPublisherNameAndBookTitleAndAuthorName
(或递归地createAutoBiography
)。
使用 @Transactional(propagation = Propagation.REQUIRES_NEW) 找到了解决方案(您需要 spring 框架提供的 @Transactional 而不是 javax.transaction.Transactional)。 它对我不起作用的原因是由于 Spring Data JPA 如何管理事务代理的一个非常不直观的警告。 考虑这个——
@Service
public class AuthorService {
@Transactional
public void createAutoBiography() {
createBook();
// process other entities
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createBook() {
// ...
}
}
在这里,您会期望创建两个事务,一个通过 createAutoBiography() 被 createBook() 方法中创建的另一个事务挂起 - 这就是我的假设错误的地方。
Spring 创建了一个事务性 AuthorService 代理,但是一旦我们进入 AuthorService 类并调用其他内部方法,就不再涉及代理了。 这意味着不会创建新事务。 这意味着第二种方法中的任何异常都是关闭整个服务的事务,并且在尝试下一个 jpa 操作时我没有收到会话错误。
因此,通过将 createBook() 方法放在单独的服务类中并添加类级别 @Transactional(propagation = Propagation.REQUIRES_NEW) 注释来解决此问题。
现在 createBook() 执行发生在一个单独的事务中,一旦这个方法返回(事务结束),它就会被写入数据库,而 createAutoBiography() 中的原始事务被挂起。 因此,现在如果我们捕获异常(如上面@slauth 所述)或使用某种数据库锁定,我们可以分别处理/避免 DataInegrityViolation 异常。 另外,即使没有处理异常,只有第二个事务应该回滚,而主事务仍然可以执行(尽管需要测试)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.