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
内部
- 注入されたリポジトリはnullですHibernate検証を無効にしない場合(spring.jpa.properties.javax.persistence.validation.mode = none)
- 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つの興味深い質問はthisとthisですが、私はそれらと運がなかったです。
====== 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。あなたが本当にあなたのバリデータで注入を使用する必要がある場合
お返事ありがとうございます。私は依存性注入が彼らが言うことに基づいて特別なトリックなしで動作する必要があることを指摘私の質問を更新しました。しかし、私はEnableSpringConfiguredとEnableLoadTimeWeavingを使用してあなたの方法に従おうとしていますが、注入されたリポジトリは常にnullです。 – drenda