2012-07-15 30 views
7

Estoy trabajando en una aplicación Spring MVC + Hibernate + JPA con un formulario de registro de usuario y decidí usar un validador JSR-303 para verificar si el nombre de usuario ya existía en el PP:@Autowired Bean funciona con @Valid en el controlador pero falla con el repositorio de CRUD

public class UniqueUsernameValidator implements ConstraintValidator<VerifyUniqueUsername, String> { 

    @Autowired 
    UserService userService; 

    @Override 
    public void initialize(VerifyUniqueUsername constraintAnnotation) {  
    } 

    @Override 
    public boolean isValid(String username, ConstraintValidatorContext context) {      

     return username!=null && userService.findByUsername(username) == null;   
    } 
} 

es muy sencillo y validación funcionó muy bien en mi controlador:

.... 
    public String signup(@Valid @ModelAttribute("newUser") User user, BindingResult newUserBeanResult) 
..... 

el problema actual que estoy enfrentando es que después de obtener mi objetivo User validado y yo llame al:

userService.save(user); 

Que implementa CrudRepository, obtengo un NullPointerException. Por alguna razón, UserService se inyecta durante la validación en el controlador pero no cuando llamo al CrudRepository.save().

vi mensajes similares como este: @Autowired bean null in ConstraintValidator when invoked by Sessionfactory.getCurrentSession.merge Y esto: hibernate validator without using autowire pero me preguntaba si alguien se ha topado con esto antes. Creo que inyectar frijoles para acceder a la base de datos en un validador es bastante común.

Como una solución agregué un cheque para null en userService pero no se siente bien.

  1. ¿Este comportamiento es esperado? ¿Se supone que estas validaciones se dispararán antes de llamar al CrudRepository.save()?
  2. ¿Se supone que debo manejar eventos de Hibernación "manualmente"? En este caso pre-insert

Respuesta

3

que terminó la solución de este problema dando instrucciones de primavera EntityManagerFactoryBean usar mi grano de validador (más exactamente, hibernación ahora se utiliza validador de primavera):

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
     <property name="dataSource" ref="dataSource" /> 
     <property name="jpaVendorAdapter"> 
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> 
     </property>   
     <property name="packagesToScan" value="some.packages"/> 
     <property name="jpaPropertyMap"> 
      <map> 
       <entry key="javax.persistence.validation.factory" value-ref="validator" />   
      </map> 
     </property> 
     <property name="jpaProperties"> 
      <props> 
       <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> 
       <prop key="hibernate.max_fetch_depth">3</prop> 
       <prop key="hibernate.jdbc.fetch_size">50</prop> 
       <prop key="hibernate.jdbc.batch_size">10</prop> 
       <prop key="hibernate.show_sql">true</prop>    
      </props>   
     </property> 
    </bean> 

Sin embargo, esta arrojó un error de StackOverflow :)

Aparentemente, la causa de este problema fue que mi validador usaba un método buscador (findByUsername) y los métodos del buscador desencadenan una descarga de hibernación, que a su vez desencadena una validación. Esto se repite indefinidamente hasta que obtienes la excepción más famosa que existe.

Así que ... Lo solucioné al cambiar los validadores para usar el EntityManager directamente (en lugar del repositorio CRUD) y cambiar temporalmente el FlushModeType a COMMIT. Aquí está el ejemplo:

public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> { 

    @PersistenceContext 
    private EntityManager em; 

    @Autowired 
    UserService userService; 

    @Override 
    public void initialize(UniqueUsername constraintAnnotation) {  
    } 

    @Override 
    public boolean isValid(String username, ConstraintValidatorContext context) { 
     try { 
      em.setFlushMode(FlushModeType.COMMIT);   
      return userService.findByUsername(username) == null; 

      } finally { 
      em.setFlushMode(FlushModeType.AUTO); 
      }  
    } 
} 

Esto resuelve el problema por el que el validador estaba usando la función de buscador que provocó un rubor de hibernación que a su vez era desencadenar el validador causando una StackOverflowError.

2

Cuando se invoca la lógica de validación en respuesta al método de guardar, hibernación. El objeto validador se crea por hibernación, por lo que la primavera @AutoWired no funcionará.

Una opción para solucionar esto es utilizar la anotación @Configurable y habilitar el entrelazado del tiempo de carga para asegurarse de que incluso cuando el hibernación ejemplifica el objeto del validador, el resorte puede inyectar dependencias en él.

+0

gracias por la sugerencia, soy bastante nuevo en Spring. Voy a buscar en Google para @Configurable y me pondré en contacto con ustedes. Gracias. – Ulises

+0

No he tenido mucha suerte, si me puedes indicar dónde podría leer más acerca de esto sería genial. ¡Gracias! – Ulises

+0

http://stackoverflow.com/questions/4703206/spring-autowiring-using-configurable - esta pregunta tiene más detalles sobre cómo configurar @Configurable. Se usa para entidades: lo mismo debería funcionar también para los Validadores. – gkamal

Cuestiones relacionadas