Hi All I am trying to implement basic CRUD functionality however what bothers me is that CrudRepository is not throwing any exception while saving duplicate Entity, can you tell me what I am missing here?
The test that I cannot pass looks like:
@Test
void onDuplicate() {
ReviewEntity duplicated = savedReviewEntity;
assertEquals(savedReviewEntity.getId(), duplicated.getId());
assertThrows(DataIntegrityViolationException.class, () -> {
reviewRepository.save(duplicated);
});
}
This is how Entity looks like:
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@Entity
@Table(name = "reviews", indexes = { @Index(name = "reviews_unique_idx", unique = true, columnList = "movieId,reviewId") })
public class ReviewEntity {
@Id
@GeneratedValue
private Integer id;
@Version
private Integer version;
private Integer movieId;
private Integer reviewId;
private String author;
private String subject;
private String content;
private String serviceAddress;
}
I am using CrudRepository
public interface ReviewRepository extends CrudRepository<ReviewEntity, Integer> {
@Transactional(readOnly = true)
List<ReviewEntity> findByMovieId(int movieId);
}
I you want to look at whole test class it looks like:
@ExtendWith(SpringExtension.class)
@DataJpaTest
@Transactional(propagation = NOT_SUPPORTED)
class ReviewRepositoryTest {
public static final int BASE_MOVIE_ID = 1;
public static final int BASE_REVIEW_ID = 2;
@Autowired
ReviewRepository reviewRepository;
ReviewEntity savedReviewEntity;
@Autowired
EntityManager entityManager;
@BeforeEach
void setUp() {
reviewRepository.deleteAll();
ReviewEntity reviewEntity = ReviewEntity.builder()
.movieId(BASE_MOVIE_ID)
.reviewId(BASE_REVIEW_ID)
.author("Fake Author")
.content("Fake Content")
.subject("Fake Subject")
.serviceAddress("Fake Service Address")
.build();
savedReviewEntity = reviewRepository.save(reviewEntity);
assertReview(reviewEntity, savedReviewEntity);
}
@Test
void create() {
reviewRepository.deleteAll();
ReviewEntity reviewEntity = ReviewEntity.builder()
.movieId(BASE_MOVIE_ID)
.reviewId(BASE_REVIEW_ID)
.author("Fake Author")
.content("Fake Content")
.subject("Fake Subject")
.serviceAddress("Fake Service Address")
.build();
ReviewEntity saved = reviewRepository.save(reviewEntity);
assertReview(reviewEntity, saved);
}
@Test
void update() {
String updatedContent = "Updated Content";
savedReviewEntity.setContent(updatedContent);
reviewRepository.save(savedReviewEntity);
ReviewEntity updated = reviewRepository.findById(savedReviewEntity.getId()).get();
assertEquals(updatedContent, updated.getContent());
}
@Test
void delete() {
reviewRepository.delete(savedReviewEntity);
assertEquals(0, reviewRepository.count());
}
@Test
void findByMovieId() {
List<ReviewEntity> reviewEntities = reviewRepository.findByMovieId(savedReviewEntity.getMovieId());
ReviewEntity firstEntity = reviewEntities.get(0);
assertThat(reviewEntities, hasSize(1));
assertReview(savedReviewEntity, firstEntity);
}
@Test
void onDuplicate() {
ReviewEntity duplicated = savedReviewEntity;
assertEquals(savedReviewEntity.getId(), duplicated.getId());
assertThrows(DataIntegrityViolationException.class, () -> {
reviewRepository.save(duplicated);
});
}
@Test
void optimisticLockVerification() {
String r1ConcurrentContent = "r1ConcurrentContent";
String r2ConcurrentContent = "r2ConcurrentContent";
ReviewEntity r1 = reviewRepository.findById(savedReviewEntity.getId()).get();
ReviewEntity r2 = reviewRepository.findById(savedReviewEntity.getId()).get();
r1.setContent(r1ConcurrentContent);
reviewRepository.save(r1);
try {
r2.setContent(r2ConcurrentContent);
reviewRepository.save(r2);
fail("Expected an OptimisticLockingFailureException");
} catch (OptimisticLockingFailureException e) {
System.out.println("OptimisticLockingFailureException should be throw.");
}
ReviewEntity updated = reviewRepository.findById(savedReviewEntity.getId()).get();
assertEquals(1, (int) updated.getVersion());
assertEquals(r1ConcurrentContent, updated.getContent());
}
private void assertReview(ReviewEntity expected, ReviewEntity actual) {
assertAll("Executing assertReview(..)", () -> {
assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getVersion(), actual.getVersion());
assertEquals(expected.getMovieId(), actual.getMovieId());
assertEquals(expected.getReviewId(), actual.getReviewId());
assertEquals(expected.getContent(), actual.getContent());
assertEquals(expected.getAuthor(), actual.getAuthor());
assertEquals(expected.getSubject(), actual.getSubject());
assertEquals(expected.getServiceAddress(), actual.getServiceAddress());
});
}
}
DataIntegrityViolationException is thrown when an insert or update in the database violates any of the integrity constraints. In your method, you are trying to overwrite an entry in the table with itself which does not create any such violation. Hibernate does not create any new entry in the table, it simply updates it(with the same object here since there are no changes).