2017-10-05 5 views
4

Springブート検証の設定とHibernateとの統合の2つの問題を表示するための小さなサンプルプロジェクトを作成しました。 私はすでに他の回答を試みましたが、残念ながら彼らは私のために働かなかったし、Hibernate検証を無効にするように求めました。Spring 4とメッセージ補間の設定でConstraintValidator内にリポジトリを注入

ConstraintValidator<ValidUser, User>を実装しているカスタムバリデータを使用し、それに私のUserRepositoryを挿入します。 同時に、update/persist中の検証エラーをチェックするHibernateのデフォルト動作を維持したいと思います。

私はアプリの完全なメインセクションのためにここに書いています。

春はファイルからメッセージを読み込みますので、私は、カスタムMessageSourceとカスタムバリデータを設定し、このクラスで カスタム構成resources/messages.properties

@Configuration 
public class CustomConfiguration { 

    @Bean 
    public MessageSource messageSource() { 
     ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 
     messageSource.setBasenames("classpath:/messages"); 
     messageSource.setUseCodeAsDefaultMessage(false); 
     messageSource.setCacheSeconds((int) TimeUnit.HOURS.toSeconds(1)); 
     messageSource.setFallbackToSystemLocale(false); 
     return messageSource; 
    } 

    @Bean 
    public LocalValidatorFactoryBean validator() { 
     LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); 
     factoryBean.setValidationMessageSource(messageSource()); 
     return factoryBean; 
    } 

    @Bean 
    public MethodValidationPostProcessor methodValidationPostProcessor() { 
     MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor(); 
     methodValidationPostProcessor.setValidator(validator()); 
     return methodValidationPostProcessor; 
    } 

} 

ここ 何も特別な豆ではないカスタム場合バリデーター@ValidUser

@ValidUser 
@Entity 
public class User extends AbstractPersistable<Long> { 
    private static final long serialVersionUID = 1119004705847418599L; 

    @NotBlank 
    @Column(nullable = false) 
    private String name; 

    /** CONTACT INFORMATION **/ 

    @Pattern(regexp = "^\\+{1}[1-9]\\d{1,14}$") 
    private String landlinePhone; 

    @Pattern(regexp = "^\\+{1}[1-9]\\d{1,14}$") 
    private String mobilePhone; 

    @NotBlank 
    @Column(nullable = false, unique = true) 
    private String username; 

    @Email 
    private String email; 

    @JsonIgnore 
    private String password; 

    @Min(value = 0) 
    private BigDecimal cashFund = BigDecimal.ZERO; 

    public User() { 

    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getLandlinePhone() { 
     return landlinePhone; 
    } 

    public void setLandlinePhone(String landlinePhone) { 
     this.landlinePhone = landlinePhone; 
    } 

    public String getMobilePhone() { 
     return mobilePhone; 
    } 

    public void setMobilePhone(String mobilePhone) { 
     this.mobilePhone = mobilePhone; 
    } 

    public String getUsername() { 
     return username; 
    } 

    public void setUsername(String username) { 
     this.username = username; 
    } 

    public String getEmail() { 
     return email; 
    } 

    public void setEmail(String email) { 
     this.email = email; 
    } 

    public String getPassword() { 
     return password; 
    } 

    public void setPassword(String password) { 
     this.password = password; 
    } 

    public BigDecimal getCashFund() { 
     return cashFund; 
    } 

    public void setCashFund(BigDecimal cashFund) { 
     this.cashFund = cashFund; 
    } 

} 

カスタムバリデータ ここで私はリポジトリを注入しようとしています。リポジトリは、Hibernate検証を無効にしていない場合は常にnullです。

public class UserValidator implements ConstraintValidator<ValidUser, User> { 
    private Logger log = LogManager.getLogger(); 

    @Autowired 
    private UserRepository userRepository; 

    @Override 
    public void initialize(ValidUser constraintAnnotation) { 
    } 

    @Override 
    public boolean isValid(User value, ConstraintValidatorContext context) { 
     try { 
      User foundUser = userRepository.findByUsername(value.getUsername()); 

      if (foundUser != null && foundUser.getId() != value.getId()) { 
       context.disableDefaultConstraintViolation(); 
       context.buildConstraintViolationWithTemplate("{ValidUser.unique.username}").addConstraintViolation(); 

       return false; 
      } 
     } catch (Exception e) { 
      log.error("", e); 
      return false; 
     } 
     return true; 
    } 

} 

messages.properties私のテストでは

#CUSTOM VALIDATORS 
ValidUser.message = I dati inseriti non sono validi. Verificare nuovamente e ripetere l'operazione. 
ValidUser.unique.username = L'username [${validatedValue.getUsername()}] è già stato utilizzato. Sceglierne un altro e ripetere l'operazione. 

#DEFAULT VALIDATORS 
org.hibernate.validator.constraints.NotBlank.message = Il campo non può essere vuoto 

# === USER === 
Pattern.user.landlinePhone = Il numero di telefono non è valido. Dovrebbe essere nel formato E.123 internazionale (https://en.wikipedia.org/wiki/E.123) 

、あなたがソースコードから試すことができ、Iきた二つの問題:UserValidator内部

  1. 注入されたリポジトリはnullですHibernate検証を無効にしない場合(spring.jpa.properties.javax.persistence.validation.mode = none)
  2. Hibernateバリデータを無効にしたとしても、Springが[Constraint]。[class name lowercase]。[propertyName]のような検証メッセージに対してデフォルトの文字列補間を使用しないため、テストケースが失敗する。私は補間のための彼自身のconvetionを持っていると考えている点を見ていないので、@NotBlank(message="{mycustom.message}")のようなvalue要素で制約注釈を使用したくないので、それを利用することができます。

​​;あなたはJunitテストを実行してエラーを見ることができます(Hibernate検証が有効で、application.propertiesを確認してください)。

私は間違っていますか?これら2つの問題を解決するために私は何ができますか?ただ、明確にする

====== ====== UPDATE

、彼らが言う春の検証ドキュメントhttps://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation-beanvalidation-spring-constraintsを読み込む:デフォルトでは

、LocalValidatorFactoryBeanは春を使用していますSpringConstraintValidatorFactoryを設定ConstraintValidatorインスタンスを作成します。これにより、カスタムConstraintValidatorsは他のSpring Beanと同様に依存性注入の恩恵を受けることができます。あなたが見ることができるように

、ConstraintValidatorの実装はその依存関係は、他のSpring Beanのように@Autowiredている可能性があります。

自分のコンフィグレーションクラスでは、自分の書いたとおりにLocalValidatorFactoryBeanを作成しました。

もう1つの興味深い質問はthisthisですが、私はそれらと運がなかったです。

====== UPDATE研究内容の多くは後2 ======

は、注入が提供されていないHibernateのバリデータでいるようです。

私はあなたがそれを行うことができる方法のカップルが見つかりました:

第一の方法

が、この構成クラスを作成します。

@Configuration 
public class HibernateValidationConfiguration extends HibernateJpaAutoConfiguration { 

    public HibernateValidationConfiguration(DataSource dataSource, JpaProperties jpaProperties, 
      ObjectProvider<JtaTransactionManager> jtaTransactionManager, 
      ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { 
     super(dataSource, jpaProperties, jtaTransactionManager, transactionManagerCustomizers); 
    } 

    @Autowired 
    private Validator validator; 

    @Override 
    protected void customizeVendorProperties(Map<String, Object> vendorProperties) { 
     super.customizeVendorProperties(vendorProperties); 
     vendorProperties.put("javax.persistence.validation.factory", validator); 
    } 
} 

第二の方法

ユーティリティを作成します。豆

012それはあなたがこの方法でエンティティマネージャを「リセット」する必要が働くようにするために、両方のケースで

@Override 
public void initialize(ValidUser constraintAnnotation) { 
userRepository = BeanUtil.getBean(UserRepository.class); 
em = BeanUtil.getBean(EntityManager.class); 
} 

非常に重要

、:

@Service 
public class BeanUtil implements ApplicationContextAware { 

    private static ApplicationContext context; 

    @Override 

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 

     context = applicationContext; 

    } 

    public static <T> T getBean(Class<T> beanClass) { 

     return context.getBean(beanClass); 

    } 

} 

、その後、バリデータの初期化中:

@Override 
public boolean isValid(User value, ConstraintValidatorContext context) { 
    try { 
     em.setFlushMode(FlushModeType.COMMIT); 
     //your code 
    } finally { 
     em.setFlushMode(FlushModeType.AUTO); 
    } 
} 

とにかく、これが本当に安全な方法かどうかはわかりません。 Probably it's not a good practice access to the persistence layer at all。あなたが本当にあなたのバリデータで注入を使用する必要がある場合

答えて

0

それに@Configurable注釈を追加してみてください:

@Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true) 
public class UserValidator implements ConstraintValidator<ValidUser, User> { 
    private Logger log = LogManager.getLogger(); 

    @Autowired 
    private UserRepository userRepository; 

    // this initialize method wouldn't be needed if you use HV 6.0 as it has a default implementation now 
    @Override 
    public void initialize(ValidUser constraintAnnotation) { 
    } 

    @Override 
    public boolean isValid(User value, ConstraintValidatorContext context) { 
     try { 
      User foundUser = userRepository.findByUsername(value.getUsername()); 

      if (foundUser != null && foundUser.getId() != value.getId()) { 
       context.disableDefaultConstraintViolation(); 
       context.buildConstraintViolationWithTemplate("{ValidUser.unique.username}").addConstraintViolation(); 

       return false; 
      } 
     } catch (Exception e) { 
      log.error("", e); 
      return false; 
     } 
     return true; 
    } 

} 

をその注釈のドキュメントから:

はSpring-資格としてクラスをマーク方式の構成

これはあなたのヌル問題を解決する必要があります。それを動作させるには、AspectJを設定する必要があります...(そのために@ConfigurableをSpringで使用する方法を確認してください)

+0

お返事ありがとうございます。私は依存性注入が彼らが言うことに基づいて特別なトリックなしで動作する必要があることを指摘私の質問を更新しました。しかし、私はEnableSpringConfiguredとEnableLoadTimeWeavingを使用してあなたの方法に従おうとしていますが、注入されたリポジトリは常にnullです。 – drenda

関連する問題