JSR303 validation fails if @Id is not @GeneratedValue with Spring Data Jpa and Hibernate

In a simple project, I like to test @NotNull validation (and some other custom ones).

So I wrote a few unittests that do this: @Test(expect=ValidationException.class

The minimum mavinized example to reproduce the problem I uploaded to github here:

I registered that it works well if @Id is a generated value. But if @Id is set by the system, the check is ignored.

These classes will show the minimum setting to reproduce the problem:

Two objects (one with a generated value, one with an address:

 @Data @NoArgsConstructor @AllArgsConstructor @Entity public class GeneratedId { @Id @GeneratedValue private Long id; @NotNull private String content; } @Data @NoArgsConstructor @AllArgsConstructor @Entity public class GivenId { @Id private Long id; @NotNull private String content; } 

unit test:

 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath*:/applicationContext.xml") @Transactional @ActiveProfiles("embedded") public class MyEntityTest { @Autowired GeneratedIdService generatedIdService; @Autowired GivenIdService givenIdService; // This test will pass @Test(expected = ValidationException.class) public void shouldNotAllowNullValues1() { this.generatedIdService.save(new GeneratedId()); } // This test will fail @Test(expected = ValidationException.class) public void shouldNotAllowNullValues2() { this.givenIdService.save(new GivenId(1L, null)); } } 

This is a service and storage

 public interface GeneratedIdRepository extends JpaRepository<GeneratedId, Long> { } public interface GivenIdRepository extends JpaRepository<GivenId, Long> { } @Service public class GeneratedIdService { @Autowired GeneratedIdRepository repository; public GeneratedId save(final GeneratedId entity) { return this.repository.save(entity); } } @Service public class GivenIdService { @Autowired GivenIdRepository repository; public GivenId save(final GivenId entity) { return this.repository.save(entity); } } 

I am currently using Spring 3.1.4, Spring-Data 1.3.4, Hibernate 4.1.10 and Hibernate-Validator 4.2.0.

Any suggestion on how the check was skipped?

Change 1:

I tried this without lombok on both entities, still an error occurs.

+4
source share
2 answers

If you want to force the save provider to erase the EntityManager before the transaction completes or rolls back, you need to either manually clear it or use saveAndFlush(…) on the JpaRepository .

The reason for this is that in the case of an auto-generated identifier, the persistence provider is stored to bind the identifier to a Java object. In the case of manually assigned identifiers, you simply do not need to hide at any earlier time than when the transaction was completed, so the persistence provider avoids interacting with the database.

Besides these technical details, I would say that relying on a persistence provider to do such validation is in any case architecturally problematic. If the provider detects a violation, you essentially filed the invalid object through all kinds of business logic. To make sure that you do not need to protect the code (everyone checks for zero everywhere), you can simply force the property so that it is not NULL by checking the value passed to the constructor or setter. Thus, you, in fact, know that whenever you receive an instance of an object, the value will never be null , regardless of whether any third structure was involved or some developer accidentally forgot to call it.

+5
source

Force flash check:

 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath*:/applicationContext.xml") @Transactional @ActiveProfiles("embedded") public class MyEntityTest { @PersistenceContext private EntityManager entityManager; @Autowired GeneratedIdService generatedIdService; @Autowired GivenIdService givenIdService; // This test will pass @Test(expected = ValidationException.class) public void shouldNotAllowNullValues1() { this.generatedIdService.save(new GeneratedId()); } // This test will fail @Test(expected = ValidationException.class) public void shouldNotAllowNullValues2() { this.givenIdService.save(new GivenId(1L, null)); entityManager.flush(); } } 

see also: this question

+2
source

Source: https://habr.com/ru/post/1497473/


All Articles