我在我的 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
}
}
我嘗試了以下 -
- Added @Lock(PESSIMISTIC_WRITE) on the
findFirstByPublisherNameAndBookTitleAndAuthorName()repo method - this works, but collisions will not be very frequent and I am concerned about the performance impact it will have on the overall functionality, to handle occasional collisions like mentioned above. - made the book creation part as a separate method with @Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW) - but I am getting no session error
- Made the BookInfo repo implement JpaRepository instead of PagingAndSortingRepository to use saveAndFlush() instead of save(), but it looks like even saveAndFlush will not commit to DB, the commit will implicitly happen only at the end of the outer method's Transaction
Also, noticed randomly there is a clash in resource creation for Author object itself, due to concurreny, where instead of adding the newly encountered BookInfo to an existing author, it creates a new entry for the author with the new book info
In general what is the best way to make JPA transactions thread safe?
uj5u.com熱心網友回復:
使用 @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 例外。另外,即使沒有處理例外,只有第二個事務應該回滾,而主事務仍然可以執行(盡管需要測驗)。
參考 - https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth#transactional-pitfalls
uj5u.com熱心網友回復:
您可以簡單地再次捕獲DataIntegrityViolation并呼叫findFirstByPublisherNameAndBookTitleAndAuthorName(或createAutoBiography遞回)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/341628.html
標籤:java spring-boot jpa concurrency spring-data-jpa
下一篇:超過AppEngine最大實體數
