Announcement Announcement Module
Collapse
No announcement yet.
Hibernate method level validation ignored ? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hibernate method level validation ignored ?

    I'm trying to apply Hibernate validations to repository methods. To validate in- and output of repositories from the start. For example:

    Code:
    public interface MyRepository<T extends MyEntity> extends JpaRepository<T, Long> {
    
      ...
    
      @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Throwable.class)
      @Override
      @NotEmpty
      <S extends T> List<S> save(@NotEmpty Iterable<S> entities);
    
      ...
    
    }
    Invoking the method gives mixed results. But usually no validation is done at all. Once is a while it works and very occasionally it complains no validator could be found for Iterable<S> (javax.validation.UnexpectedTypeException: HV000030: No validator could be found for type: java.lang.Iterable<S>). Rebuilding the project is sometimes all it takes to cause another error. Too much accumulated AOP magic maybe ? :-)

    HOWEVER ... I'm getting consistent working, behavior, when restricting the validation to javax.validation.constrains.* annotations. So maybe there's something wrong with custom annotations ?

    Are there any known problems with JSR 303 / Hibernate Validator method validation and Spring Data repositories ?

    Can a blessed soul in here point me in the right direction ? :-)

    TIA !

    ----------

    The specs:
    • Spring 3.2.1
    • Hibernate 4.1.10
    • Hibernate Validator 4.3.1
    • Spring Data 1.3.0

    The application context definitions:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:data="http://www.springframework.org/schema/data/jpa"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
              http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    
      <!-- Enable method bean validation / that is on the arguments and return value -->
      <bean id="validatorFactory" class="javax.validation.Validation" factory-method="buildDefaultValidatorFactory"/>
      <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
        <property name="validatorFactory" ref="validatorFactory"/>
      </bean>
    
      <!-- Tell Spring Data where to find the interfaces -->
      <data:repositories base-package="com.bar.persistence.dao" factory-class="com.bar.persistence.customdao.DaoFactory"/>
    
    
      <!-- include the definitions of the 'upstream' modules -->
      <import resource="common-beans.xml"/>
    
      <!-- The exception translator service-->
      <bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/>
    
      <!-- Use the classic Spring JPA Transaction Manager -->
      <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
      </bean>
    
      <!-- Describes how to connect to the database -->
      <!-- Describes to reuse database connections as making them is expensive -->
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="user" value="${database.user.name}"/>
        <property name="password" value="${database.user.password}"/>
        <property name="driverClass" value="${database.driver}"/>
        <property name="jdbcUrl" value="${database.url}"/>
        <property name="initialPoolSize" value="1"/>
        <property name="maxPoolSize" value="${database.max.pool.size}"/>
        <property name="minPoolSize" value="${database.min.pool.size}"/>
        <property name="acquireIncrement" value="1"/>
        <property name="acquireRetryAttempts" value="3"/>
        <property name="checkoutTimeout" value="10000"/>
        <property name="autoCommitOnClose" value="false"/>
      </bean>
    
      <!-- Describes how to create hibernate sessions -->
      <!-- "persistenceUnitName" points to META-INF/persistence.xml -->
      <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
        <property name="persistenceUnitName" value="bar"/>
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaProperties">
          <props>
            <prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop>
            <prop key="hibernate.cache.use_second_level_cache">${hibernate.second.level.cache}</prop>
            <prop key="hibernate.cache.use_query_cache">${hibernate.query.cache}</prop>
            <prop key="hibernate.generate_statistics">true</prop>
            <prop key="hibernate.cache.default_cache_concurrency_strategy">NONSTRICT_READ_WRITE</prop>
            <prop key="javax.persistence.sharedCache.mode">ENABLE_SELECTIVE</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>
            <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            <prop key="hibernate.dialect">${database.hibernate.dialect}</prop>
            <prop key="hibernate.search.default.directory_provider">filesystem</prop>
            <prop key="hibernate.search.default.indexBase">${hibernate.search.dir}</prop>
            <prop key="hibernate.ejb.naming_strategy">com.bar.persistence.OptimusNamingStrategy</prop>
            <!-- Disable Hibernate Search
            <prop key="hibernate.search.indexing_strategy">manual</prop>
            <prop key="hibernate.search.autoregister_listeners">false</prop> -->
          </props>
        </property>
      </bean>
    
    
      <!-- Publish statistics over JMX -->
    
      <bean id="sessionFactory" class="com.bar.persistence.util.PersistenceUtils" factory-method="getSessionFactory">
        <constructor-arg>
          <bean factory-bean="entityManagerFactory" factory-method="createEntityManager"/>
        </constructor-arg>
      </bean>
    
      <bean id="cacheManager" class="com.bar.persistence.util.PersistenceUtils" factory-method="getCacheManager">
        <constructor-arg>
          <bean factory-bean="entityManagerFactory" factory-method="createEntityManager"/>
        </constructor-arg>
      </bean>
    
      <!-- expose EHCache statistics over JMX -->
      <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
        <property name="locateExistingServerIfPossible" value="true"/>
      </bean>
    
      <bean id="ehCacheMBeanRegistration" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="net.sf.ehcache.management.ManagementService.registerMBeans"/>
        <property name="arguments">
          <list>
            <ref bean="cacheManager"/>
            <ref bean="mbeanServer"/>
            <value>true</value>
            <value>true</value>
            <value>true</value>
            <value>true</value>
            <value>true</value>
          </list>
        </property>
      </bean>
    
    
      <bean id="hibernateStats" class="org.hibernate.jmx.StatisticsService">
        <property name="sessionFactory">
          <ref bean="sessionFactory"/>
        </property>
      </bean>
    
      <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
          <map>
            <entry key="Hibernate:name=statistics">
              <ref bean="hibernateStats"/>
            </entry>
          </map>
        </property>
        <property name="assembler">
          <bean class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler">
            <property name="methodMappings">
              <props>
                <prop key="bean:name=hibernateStatsMBean">
                </prop>
              </props>
            </property>
          </bean>
        </property>
      </bean>
    
      <!-- Code to be run when the application stops -->
      <bean class="com.bar.persistence.ShutdownHandler" destroy-method="shutdown"/>
    
    </beans>
    The repository factory:

    Code:
    public class DaoFactory<R extends JpaRepository<MyEntity, Long>> extends JpaRepositoryFactoryBean<R, MyEntity, Long> {
    
      protected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) {
        return new MyRepositoryFactory(entityManager);
      }
    
      private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
    
        private EntityManager entityManager;
    
        public MyRepositoryFactory(final EntityManager entityManager) {
          super(entityManager);
    
          this.entityManager = entityManager;
        }
    
        protected Object getTargetRepository(final RepositoryMetadata metadata) {
          return new MyDaoImpl<MyEntity>((Class<MyEntity>) metadata.getDomainType(), entityManager);
        }
    
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
          return MyDao.class;
        }
      }
    }
Working...
X