2014-07-25 6 views
6

Spring MVC를 사용하여 웹 사이트를 만들고 지속성을 유지하기 위해 JPA 공급자로 Spring Data JPA와 Hibernate 4를 사용하고 있습니다. 검증은 현재 Hibernate Validator와 함께 처리되고있다. 저는 유효성 검사기가 두 번 호출되는 문제가있어서 그 이유를 알 수 없습니다. 주된 이유는 이것이 문제이기 때문에 두 번째 시간, 종속성이 유효성 검사기에 자동 추가되지 않고 널 포인터 예외가 발생하기 때문입니다. 다음은 실패에 이르기까지의 호출 순서는 다음과 같습니다JSR303 사용자 정의 유효성 검사기를 두 번 호출 중

  1. 등록 양식을 제출 먼저 NotDefaultSectValidator가 호출되고 사용자 개체의 'whereDidYouHearAboutUs'필드에 성공적으로 완료됩니다.
  2. UniqueUsernameValidator가 다음에 호출되고 '사용자 이름'필드 유효성 검사를 위해 성공적으로 완료됩니다.
  3. 컨트롤러의 'addUserFromForm'메서드가 시작되어 bindingResults 개체에서 오류가 발견되지 않습니다.
  4. 그러면 'addUser'메소드가 UserService 클래스에서 호출됩니다. 이 메소드는 'userRepository.save (user);'행에 도달합니다. 그러나 그 후에 'print.ln'라인을 실행하지 않습니다. 이 줄을 밟으면 'NotDefaultSectValidator'중단 점으로 돌아갑니다. 이 작업은 두 번째 완료되며 두 번째 유효성 검사기 'UniqueUsernameValidator'를 다시 입력합니다. 몇 가지 이유로 Spring이 DAO에서 Autowire에 실패하기 때문에 null 포인터 예외가 발생합니다.

왜 유효성 검사기가 두 번 호출되는지, 특히 'userRepository.save (user);'행을 건너 뛰는 이유에 대해 알려줄 수 있습니까? 이 유효성 검사기로 돌아 간다 고요?

많은 감사

다음

내 user.java 클래스입니다

내 등록 컨트롤러 관련 방법 :

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

내 서비스 클래스 :

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

내 번호 tDefaultSelect 검사기 :

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

} 

내 uniqueUsername 검사기 :

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

} 

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

마지막으로 내 지속성-의 context.xml 파일

<!-- 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"/> 
+2

여기에서 코드를 좁히십시오. –

+2

먼저 인스턴스가 Spring에 의해 유효성이 확인되면 저장하기 전에 최대 절전 모드로 유효성이 검사됩니다. 의존성이 정확하게 주입 (또는 잡아 당겨 지도록)하거나 hibernate/jpa에 대한 검증을 사용하지 않도록 validator를 수정하십시오. –

+0

개인 필드를 autowiring하면 리플렉션을 통해서만 주입 할 수 있습니다. 또한 setter 또는 생성자 인수를 제공하여 주입해야합니다. 모든 주사가 ​​여러분이 알고있는 봄에 의해 이루어져야하는 것은 아닙니다. – Bart

답변

5

어쩌면 두 번째 검증 나는 빈을 데이터 저장소에 보낼 때 최대 절전 모드로 수행됩니다. 를 끄려면 당신의 persistence.xml이 추가 :

<property name="javax.persistence.validation.mode" value="none"/> 

https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/configuration.html는 말한다 :

기본적으로

, 콩 검증 (및 최대 절전 모드 검사기)가 활성화됩니다.엔티티가 생성되고 업데이트 (선택적으로 삭제)되면 데이터베이스에 전송되기 전에 유효성이 검사됩니다. Hibernate에 의해 생성 된 데이터베이스 스키마는 엔티티에 선언 된 제약 조건을 반영한다.

당신은 미세 조정을 할 수 필요한 경우 :

AUTO : 콩 검증 클래스 경로, 콜백에 존재하고 DDL이 활성화 된 경우.

콜백 : 엔티티는 생성, 업데이트 및 삭제시 유효성이 검사됩니다. Bean 검증 공급자가 없으면 초기화시 예외가 발생합니다.

DDL : (아래 표 참조) 데이터베이스 스키마는 생성, 업데이트 및 삭제시 유효성이 검사됩니다. Bean 검증 공급자가 없으면 초기화시 예외가 발생합니다.

NONE : 콩 검증이 첫 번째가 분명히 있기 때문에 @Valid 주석의 당신의 봄 컨트롤러에 의해 수행되는 모든

에서 사용되지 않습니다.

+0

신난다, 고맙다. 내 문제가 고쳐졌다. 호기심에서, Autowired로 설정 했는데도 두 번째 호출 중에 userRepository가 null 인 이유를 알 수 있습니까? 유효성 검사기가 Spring 컨텍스트 외부에서 호출 되었기 때문입니까? 이 문제를 어떻게 해결할 것이라고 제안 하시겠습니까? –

+0

아마 맞을 것입니다 : 당신은 봄 상황에서 벗어났습니다. 그러나 필자는 생각하기에 bean 컨 스트레인 트 유효성 검사 중에 영속성 연산을 수행하는 것이 좋다. 좀 더 숙련 된 사용자에게 좋은지 물어보아야합니다. 내 이해에서는 nulls, 크기 형식, 응용 프로그램 논리가 아닌 일부 체크섬 맞춤법 검사 만 수행됩니다. 하지만 이것은 내 느낌입니다. junit 테스트, 트랜잭션 관리 및 다른 종류의 문제에 대해서도 생각해보십시오. –

+0

@MarekRaszewski 어떻게 Hibernate 메시지를 BindingResult에 넣을 수 있습니까? –

관련 문제