Sto creando un sito Web utilizzando Spring MVC e per la persistenza sto utilizzando Spring Data JPA con Hibernate 4 come provider JPA. La convalida viene attualmente gestita con Hibernate Validator. Ho un problema per cui i miei validatori vengono chiamati due volte e non riesco a capire perché. Il motivo principale per cui questo è un problema è perché la seconda volta, le dipendenze non vengono autowired nel validatore e sto ricevendo un'eccezione di puntatore nullo. che segue è la sequenza di chiamate che portano al fallimento:JSR303 convalidatori personalizzati chiamati due volte
- La scheda di iscrizione è presentata e il primo la NotDefaultSectValidator si chiama e completa con successo per il campo 'whereDidYouHearAboutUs' per l'oggetto utente.
- L'UniqueUsernameValidator viene chiamato next e viene completato correttamente per la convalida del campo 'username'.
- Il metodo 'addUserFromForm' sul controller si avvia e non rileva errori nell'oggetto bindingResults.
- Il metodo 'addUser' viene quindi chiamato sulla classe UserService. Questo metodo raggiunge la riga 'userRepository.save (utente);' ma non esegue mai la riga 'print.ln' immediatamente dopo. Superando questa linea si ritorna al punto di interruzione 'NotDefaultSectValidator'. Questo si completa per la seconda volta e reinserisco il secondo validatore 'UniqueUsernameValidator'. Qui ottengo un'eccezione di puntatore nullo perché, per qualche motivo, Spring non riesce a eseguire l'Autowire nel DAO per la seconda volta.
Qualcuno può far luce sul perché i validatori sono chiamati due volte e in particolare, perché scavalcando linea 'userRepository.save (utente);' torna in questi validatori?
Molte grazie
Ecco la mia classe user.java
Il metodo rilevante nel mio controller di registrazione:
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String addUserFromForm(@Valid User user,
BindingResult bindingResult, RedirectAttributes ra) {
if (bindingResult.hasErrors()) {
return "user/register";
}
userService.addUser(user);
// Redirecting to avoid duplicate submission of the form
return "redirect:/user/" + user.getUsername();
}
La mia classe di servizio:
package com.dating.service.impl;
import javax.transaction.Transactional;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.dating.domain.Role;
import com.dating.domain.User;
import com.dating.repository.RoleRepository;
import com.dating.repository.UserRepository;
import com.dating.repository.specification.UserSpecifications;
import com.dating.service.UserService;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Transactional
@Override
public void addUser(User user) {
user.setJoinDate(new LocalDate());
user.setEnabled(true);
Role role = roleRepository.findByName(Role.MEMBER);
if (role == null) {
role = new Role();
role.setName(Role.MEMBER);
}
user.addRole(role);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
user.setPassword(encoder.encode(user.getPassword()));
userRepository.save(user);
System.out.println("User Saved");
}
@Override
public User getUserByUsername(String username) {
return userRepository.findByUsername(username);
}
@Override
public Iterable<User> getAllUsers() {
return userRepository.findAll();
}
@Override
public void updateDetails(User user) {
userRepository.save(user);
}
@Override
public Iterable<User> lastNameIsLike(String searchTerm) {
return userRepository.findAll(UserSpecifications
.lastNameIsLike(searchTerm));
}
}
Il mio no tDefaultSelect validatore:
package com.dating.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.dating.annotation.NotDefaultSelect;
public class NotDefaultSelectValidator implements
ConstraintValidator<NotDefaultSelect, String> {
@Override
public void initialize(NotDefaultSelect constraint) {
}
@Override
public boolean isValid(String selectedValue, ConstraintValidatorContext ctx) {
if (selectedValue == null) {
return false;
}
if (selectedValue.equals("") || selectedValue.equals("0")
|| selectedValue.equalsIgnoreCase("default")
|| selectedValue.equalsIgnoreCase("please select")) {
return false;
}
return true;
}
}
mio uniqueUsername validatore:
package com.dating.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import com.dating.annotation.UniqueUsername;
import com.dating.repository.UserRepository;
public class UniqueUsernameValidator implements
ConstraintValidator<UniqueUsername, String> {
@Autowired
private UserRepository userRepository;
@Override
public void initialize(UniqueUsername constraint) {
}
@Override
public boolean isValid(String username, ConstraintValidatorContext ctx) {
if (username == null || userRepository.findByUsername(username) == null) {
return true;
}
return false;
}
}
mio UserRepository:
package com.dating.repository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import com.dating.domain.User;
//Spring Data JPA Marker interfaces being extended for automatic CRUD repository creation
public interface UserRepository extends CrudRepository<User, Long>, JpaSpecificationExecutor<User> {
//Automatic query creation from method name
public User findByUsername(String username);
}
Infine la mia persistenza-context.xml file di
<!-- Data source properties -->
<util:properties id="dataSourceSettings" location="classpath:datasource.properties" />
<!-- Pooled data source using BoneCP -->
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
destroy-method="close">
<property name="driverClass" value="#{dataSourceSettings['jdbc.driverClass']}" />
<property name="jdbcUrl" value="#{dataSourceSettings['jdbc.url']}" />
<property name="username" value="#{dataSourceSettings['jdbc.username']}" />
<property name="password" value="#{dataSourceSettings['jdbc.password']}" />
<property name="idleConnectionTestPeriodInMinutes" value="60" />
<property name="idleMaxAgeInMinutes" value="240" />
<property name="maxConnectionsPerPartition" value="30" />
<property name="minConnectionsPerPartition" value="10" />
<property name="partitionCount" value="3" />
<property name="acquireIncrement" value="5" />
<property name="statementsCacheSize" value="100" />
<property name="releaseHelperThreads" value="3" />
</bean>
<!-- JPA entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.dating.domain" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">#{dataSourceSettings['hibernate.dialect']}</prop>
<prop key="hibernate.hbm2ddl.auto">#{dataSourceSettings['hibernate.hbm2ddl.auto']}
</prop>
<prop key="hibernate.show_sql">#{dataSourceSettings['hibernate.show_sql']}</prop>
<prop key="hibernate.format_sql">#{dataSourceSettings['hibernate.format_sql']}</prop>
<prop key="hibernate.use_sql_comments">#{dataSourceSettings['hibernate.use_sql_comments']}
</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<context:annotation-config />
<jpa:repositories base-package="com.dating.repository"/>
Riduci il codice qui. –
Prima l'istanza, se convalidata da Spring, viene convalidata dalla sospensione prima del salvataggio. O fissa i tuoi validatori in modo che le dipendenze vengano iniettate (o tirate) correttamente o disabiliti la convalida per hibernate/jpa. –
Si noti inoltre che l'autowiring di un campo privato lo rende solo iniettabile tramite riflessione. Dovresti fornire anche un argomento setter o costruttore da iniettare. Non tutte le iniezioni devono essere fatte da Spring sai. – Bart