0

Is there any way to skip the @NotNull or @NotEmpty validation for fields whose setter methods weren't called (and thus, have null values) when calling JpaRepository.save?

Removing the @NotNull annotation from these fields makes JpaRepository.save behave like EntityManager.merge, so I expected that the @NotNull validation for these won't kick in.

Here are the first lines of the validation exception raised:

javax.validation.ConstraintViolationException: Validation failed for classes [com.example.demo.model.db.AppUser] during update time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=loginName, rootBeanClass=class com.example.demo.model.db.AppUser, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
    ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=pwd, rootBeanClass=class com.example.demo.model.db.AppUser, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
    ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=statusCd, rootBeanClass=class com.example.demo.model.db.AppUser, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
]
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:138) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreUpdate(BeanValidationEventListener.java:86) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.action.internal.EntityUpdateAction.preUpdate(EntityUpdateAction.java:244) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:118) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:465) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2963) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2339) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:485) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:61) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517) ~[spring-orm-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) ~[spring-data-jpa-1.11.6.RELEASE.jar:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) ~[spring-data-commons-1.13.6.RELEASE.jar:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at com.sun.proxy.$Proxy113.save(Unknown Source) ~[na:na]
    at com.example.demo.repository.RepositoryWrapperImpl.save(RepositoryWrapperImpl.java:124) ~[classes/:na]
    at com.example.demo.service.AbstractTransactionalService.save(AbstractTransactionalService.java:20) ~[classes/:na]
    at com.example.demo.service.AbstractTransactionalService$$FastClassBySpringCGLIB$$f1966d61.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:669) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at com.example.demo.service.Pads010020AppUserService$$EnhancerBySpringCGLIB$$f988b3b1.save(<generated>) ~[classes/:na]
    at com.example.demo.controller.ServiceDispatcherController.handleAllPut(ServiceDispatcherController.java:115) ~[classes/:na]
user3367701
  • 823
  • 1
  • 8
  • 17

2 Answers2

1

You could write your own @NotNullOnFirstSave validation constraint which only checked for not null if the primary key of the object is null, i.e. the object is not yet persistant.

See https://docs.jboss.org/hibernate/validator/5.0/reference/en-US/html/validator-customconstraints.html#validator-customconstraints-constraintannotation

Do you think this could be a solution?

:)

Kaj Hejer
  • 955
  • 4
  • 18
0

@NotNull is a JSR 303 Bean Validation annotation. It has nothing to do with database constraints itself. As Hibernate is the reference implementation of JSR 303, however, it intelligently picks up on these constraints and translates them into database constraints for you. @Column(nullable = false) is the JPA way of declaring a column to be not-null. See Confusion: @NotNull vs @Column(nullable = false)

I think you should also ensure that you check for nulls in code as well, in addition to JPA @Column(nullable =false ) annotation to be sure.

nyulan
  • 311
  • 3
  • 12