Hibernate 不会在重复时抛出异常
Hibernate does not throw exception on duplicate
大家好我正在尝试实现基本的 CRUD 功能,但是让我困扰的是 CrudRepository 在保存重复实体时没有抛出任何异常,你能告诉我我在这里遗漏了什么吗?
我无法通过的测试如下:
@Test
void onDuplicate() {
ReviewEntity duplicated = savedReviewEntity;
assertEquals(savedReviewEntity.getId(), duplicated.getId());
assertThrows(DataIntegrityViolationException.class, () -> {
reviewRepository.save(duplicated);
});
}
实体是这样的:
@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;
}
我正在使用 CrudRepository
public interface ReviewRepository extends CrudRepository<ReviewEntity, Integer> {
@Transactional(readOnly = true)
List<ReviewEntity> findByMovieId(int movieId);
}
我想看看整个测试 class 它看起来像:
@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 当数据库中的插入或更新违反任何完整性约束时抛出。在您的方法中,您试图用自身覆盖 table 中的一个条目,而不会造成任何此类违规。 Hibernate 不会在 table 中创建任何新条目,它只是更新它(这里使用相同的对象,因为没有更改)。
大家好我正在尝试实现基本的 CRUD 功能,但是让我困扰的是 CrudRepository 在保存重复实体时没有抛出任何异常,你能告诉我我在这里遗漏了什么吗?
我无法通过的测试如下:
@Test
void onDuplicate() {
ReviewEntity duplicated = savedReviewEntity;
assertEquals(savedReviewEntity.getId(), duplicated.getId());
assertThrows(DataIntegrityViolationException.class, () -> {
reviewRepository.save(duplicated);
});
}
实体是这样的:
@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;
}
我正在使用 CrudRepository
public interface ReviewRepository extends CrudRepository<ReviewEntity, Integer> {
@Transactional(readOnly = true)
List<ReviewEntity> findByMovieId(int movieId);
}
我想看看整个测试 class 它看起来像:
@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 当数据库中的插入或更新违反任何完整性约束时抛出。在您的方法中,您试图用自身覆盖 table 中的一个条目,而不会造成任何此类违规。 Hibernate 不会在 table 中创建任何新条目,它只是更新它(这里使用相同的对象,因为没有更改)。