Question

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

Therefore I wrote some unittests that execpects this: @Test(expect=ValidationException.class

A minimal mavinized example to reproduce the problem I uploaded on github here:

I regonized that it works well if the @Id is a generated value. But if the @Id is given by the system, the validation is ignored.

This classes will show the minimal setup to reproduce the problem:

The two entities (one with generated value, one withouth:

@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;
}

The 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 the boilerplate Service and Repository

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);
    }
}

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

Any suggestion way the validation got skipped?

Edit 1:

I tried it without lombok on both Entities, still the error occurs.

Était-ce utile?

La solution

If you want to force the persistence provider to flush the EntityManager before the transaction commits or rolls back you either need to manually flush it or use saveAndFlush(…) on the JpaRepository.

The reason for that is that in the auto-generated ID case, the persistence provider has to flush to be able to bind the ID to the Java object. In the case of manually assigned IDs there's simply no need to flush at any earlier point in time than on transaction end, hence the persistence provider avoids the database interaction.

Besides these technical details, I'd argue that relying on the persistence provider to do this kind of validation is architecturally problematic anyway. If the provider detects a violation you've essentially piped an invalid object through all kinds of business logic. To make sure you don't have to code that defensively (null checks each and everywhere) you could simply enforce the property to not be nullable by checking the value handed into a constructor or the setter. This way, you essentially know that, whenever you get an instance of the object, the value will never be null, no matter if some third framework has been invoked, or some developer accidentally forgot to invoke it.

Autres conseils

Forcing a flush make validation works:

@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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top