I am using spring data JPA in my java-springboot project. I have one method which processes a lot of inter related entities(some of which are lazy loaded) and this method is run by concurrently by multiple threads. My issue is - when say Thread T1 comes and tries to create a new Book for the author, it checks if the book info already exists in the DB or not and starts creating a new record, in the meanwhile thread T2 also tries the same and starts creating the same resource, however when persisting, it gets DataIntegrtyViolation exception.
@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;
the repository -
@Repository
public interface BookInfoRepository extends PagingAndSortingRepository<BookInfo, Long> {
public BookInfo findFirstByPublisherNameAndBookTitleAndAuthorName(String publisherName,
String bookTitle, String authorName);
}
and the method which is causing the issue -
@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
}
}
I tried the following -
- 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?