Ich habe ein kleines Beispielprojekt erstellt, um zwei Probleme zu zeigen, die bei der Konfiguration der Spring Boot-Validierung und ihrer Integration in Hibernate auftreten. Ich habe bereits andere Antworten ausprobiert, die ich über das Thema gefunden habe, aber leider funktionierten sie nicht für mich oder die Hibernate-Validierung wurde deaktiviert.Inject Repository in ConstraintValidator mit Spring 4 und Nachrichteninterpolationskonfiguration
Ich möchte einen benutzerdefinierten Validator implementieren ConstraintValidator<ValidUser, User>
und injizieren Sie es in meine UserRepository
. Gleichzeitig möchte ich das Standardverhalten von Hibernate beibehalten, das bei Update/Persist nach Validierungsfehlern sucht.
Ich schreibe hier zur Vollständigkeit Hauptabschnitte der App.
Individuelle Konfiguration In dieser Klasse I mit einem benutzerdefinierten MessageSource
einen benutzerdefinierten Validator gesetzt, so Frühling Nachrichten aus der Datei 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;
}
}
Die Bohne Nichts Besonderes hier, wenn nicht der Brauch lesen Prüfer @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;
}
}
Benutzerdefinierter Validator Hier versuche ich, das Repository zu injizieren. Das Repository ist immer null, wenn ich die Hibernate-Validierung nicht deaktiviere.
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)
In meinen Tests, können Sie aus dem Quellcode versuchen, habe ich zwei Probleme:
- Das injizierte Repository innerhalb
UserValidator
null ist wenn ich die Hibernate-Validierung nicht deaktiviere (spring.jpa.properties.javax.persistence.validation.mode = none) - Auch wenn ich den Hibernate-Validator deaktiviere, schlagen meine Testfälle fehl, weil Spring die Standard-Stringinterpolation für Validierungsnachrichten wie [Constraint] verwendet. [Klassenname Kleinbuchstaben]. [PropertyName]. Ich möchte die Constraint-Annotation nicht mit dem Wertelement wie diesem
@NotBlank(message="{mycustom.message}")
verwenden, weil ich nicht den Punkt sehe, der seine eigene Konvetion für die Interpolation hat, und ich kann das ausnutzen ... das bedeutet weniger Codierung.
I attach the code; Sie können nur Junit-Tests ausführen und Fehler anzeigen (die Hibernate-Validierung ist aktiviert, überprüfen Sie application.properties).
Was mache ich falsch? Was könnte ich tun, um diese beiden Probleme zu lösen?
====== ====== UPDATE
Nur um zu klären, Frühlings- und Validierungsdokumentation Lesen https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation-beanvalidation-spring-constraints sie sagen:
standardmäßig die LocalValidatorFactoryBean eine SpringConstraintValidatorFactory konfiguriert, dass Spring verwendet ConstraintValidator-Instanzen erstellen. Dadurch können Ihre benutzerdefinierten ConstraintValidators wie alle anderen Spring-Bean von der Abhängigkeitsinjektion profitieren.
Wie Sie sehen können, kann eine ConstraintValidator-Implementierung ihre Abhängigkeiten @Autowired wie jede andere Spring-Bean haben.
In meiner Konfigurationsklasse habe ich meine LocalValidatorFactoryBean
erstellt, während sie schreiben. Eine weitere interessante Fragen sind this und this, aber ich hatte kein Glück mit ihnen.
====== ====== UPDATE 2
Nach vielen reseach scheint mit Hibernate Validator der Injektion nicht vorgesehen ist.
fand ich ein paar Art und Weise können Sie das tun:
erste Weg
diese Konfiguration Klasse erstellen:
@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);
}
}
2. Art und Weise
ein Dienstprogramm erstellen Bohne
@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);
}
}
und dann in der Validator Initialisierung:
@Override
public void initialize(ValidUser constraintAnnotation) {
userRepository = BeanUtil.getBean(UserRepository.class);
em = BeanUtil.getBean(EntityManager.class);
}
sehr wichtig
In beiden Fällen, um Ihnen die es funktioniert machen auf diese Weise die Einheit-Manager auf "reset" :
Wie auch immer, ich weiß nicht, ob das wirklich ein sicherer Weg ist. Probably it's not a good practice access to the persistence layer at all.
Danke für die Antwort. Ich aktualisierte meine Frage und wies darauf hin, dass die Abhängigkeitsinjektion ohne einen speziellen Trick funktionieren sollte, basierend auf dem, was sie sagen. Ich versuche jedoch, Ihren Weg mit EnableSpringConfigured und EnableLoadTimeWeaving zu verfolgen, aber das injizierte Repository ist immer Null. – drenda