【发布时间】:2021-10-26 07:22:59
【问题描述】:
我在我的 java-springboot 项目中使用 spring data 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()repo 方法上添加了@Lock(PESSIMISTIC_WRITE) - 这有效,但冲突不会非常频繁,我担心它会对整体功能产生性能影响,以处理上述偶尔发生的冲突. - 使用 @Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW) 将书籍创建部分作为单独的方法 - 但我没有收到任何会话错误
- 使 BookInfo 存储库实现 JpaRepository 而不是 PagingAndSortingRepository 以使用 saveAndFlush() 而不是 save(),但看起来即使 saveAndFlush 也不会提交到 DB,提交只会隐式发生在外部方法的事务结束时
另外,随机注意到 Author 对象本身的资源创建存在冲突,由于并发,它不是将新遇到的 BookInfo 添加到现有作者,而是使用新书信息为作者创建一个新条目
一般来说,使 JPA 事务线程安全的最佳方法是什么?
【问题讨论】:
-
如果您想在代码中处理它,请锁定 bookId,创建一个具有 bookId 的对象,并且一次只能调用一个方法实例。方法也可以检查书是否已经存在。如果要在 DB 中处理,请在 DB 中使用并发级别。我认为你可以在代码中处理事情可能是。
-
我对锁的关注是性能,如果我的理解有误,请纠正我,但是放置一个悲观的写锁意味着不给任何其他线程/方法对表的任何读写访问权限,我的问题想要处理,不会那么普遍,但肯定会存在,所以为了解决这个问题,我不希望性能受到重大影响
-
为这三个字段添加唯一索引。
-
@zysaaa 必须已经存在唯一索引 - 否则不会抛出
DataIntegrtyViolation。 -
@zysaaa 我已经对实体的 3 个字段有唯一约束 - @UniqueConstraint(columnNames = { "publisherName","bookTitle", "authorName" })
标签: java spring-boot jpa concurrency spring-data-jpa