我创建了一个小的示例项目来展示我在Spring Boot验证的配置及其与Hibernate的集成中遇到的两个问题。我已经尝试了我找到的关于这个主题的其他回复,但不幸的是,它们对我不起作用,或者要求禁用Hibernate验证。
我想使用一个实现constraintvalidator
的自定义验证器,并在其中注入我的userrepository
。同时,我希望保持Hibernate的默认行为,即在更新/持久期间检查验证错误。
我在这里写完整的主要部分的应用程序。
自定义配置在该类中,我使用自定义messageSource
设置了一个自定义验证器,因此Spring将从文件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
,bean在这里没有什么特别之处
@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;
}
}
消息.属性
#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)
在我的测试中,你可以从源代码中尝试,我有两个问题:
UserValidator
中注入的存储库为空
@NotBlank(Message=“{MyCustom.Message}”)
,因为考虑到它有自己的插值传递,而且我可以利用它,所以我看不出有什么意义...这意味着更少的编码。我附上密码;您可以只运行Junit测试并看到错误(启用Hibernate验证,检查Application.Properties)。
我做错了什么?我能做些什么来解决这两个问题呢?
=======更新=======
默认情况下,LocalValidatorFactoryBean配置一个使用Spring创建ConstraintValidatorFactory实例的SpringConstraintValidatorFactory。这允许您的自定义ConstraintValidators像任何其他Spring bean一样受益于依赖注入。
正如您所看到的,ConstraintValidator实现可能像任何其他Spring bean一样具有其依赖关系@Autowired。
在我的配置类中,我创建了LocalValidatorFactoryBean
。
另一个有趣的问题是这个和这个,但我没有运气。
=======更新2=======
经过大量研究,似乎与Hibernate验证器没有提供注入。
我找到了几种方法:
第1路
创建此配置类:
@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);
}
}
@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 void initialize(ValidUser constraintAnnotation) {
userRepository = BeanUtil.getBean(UserRepository.class);
em = BeanUtil.getBean(EntityManager.class);
}
非常重要
在这两种情况下,为了使it工作,您必须以以下方式“重置”实体管理器:
@Override
public boolean isValid(User value, ConstraintValidatorContext context) {
try {
em.setFlushMode(FlushModeType.COMMIT);
//your code
} finally {
em.setFlushMode(FlushModeType.AUTO);
}
}
不管怎样,我不知道这是否真的是一个安全的方法。很可能这根本不是一个访问持久层的好做法。
如果您确实需要在验证器中使用注入,请尝试在其上添加@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驱动配置的条件
我用玩Web应用程序(没什么不寻常的)。Guice用于依赖注入。如何在类构造函数中注入属性值?代码如下。 在当前配置中,如果不运行应用程序,就无法测试。我希望能够在单元测试中实例化。完美的解决方案[从我的角度来看]应该是这样的: 不幸的是,这段代码不起作用,因为Guice没有“my”。bar绑定: 没有绑定使用 @com.google.inject.name.Named(value=my.bar)
我试图从WSO2 ESB填充一个JMS消息存储。 [2014-07-29 16:43:45,633]错误-JmsStore Store[JMSMS]。无法创建到代理的新连接。初始上下文工厂:[org.apache.qpid.jn di.PropertiesFileInitialContextFactory];提供程序URL:[repository/conf/jndi.properties];连接工
我正在使用WSO2ESB4.7.0和ActiveMQ5.8.0。我希望将消息存储在队列中,为此我尝试使用这段代码,但它不工作。我创建了存储库,这将有助于存储消息,我的conumeing过程已经在JAVA代码中完成,所以我不必担心消费,如果我这样做了,我的消息将出现在acivemq UI中。我的配置如下所示 我试图通过代理传递消息,为此我编写了简单的代理服务 在这种情况下,我的队列没有在Active
我试图使用-@runwith(SpringJUnit4ClassRunner.class)@ContextConfiguration({“classpath:some.xml”})运行spring JUnit测试用例 xml在内存db详细信息中定义了bean
尝试使用配置单元HPL/SQL存储过程将数据插入配置单元表。但无法插入时间戳值。我的表定义如下: 我使用配置单元cli插入值没有任何问题。 知道为什么吗? 谢谢,
问题内容: 我正在尝试配置Jenkins(在Windows Server2008上运行)以连接到GitLab6.0存储库(在Linux机器上)。 在本地窗口框中,我可以使用SSH URL在GitLab服务器上克隆项目。我在使用HTTP URL时遇到了一些问题,但是我确实设法使用Eclipse插件通过HTTP与身份验证进行连接。 在Jenkins项目配置屏幕上,我尝试过: http://myserv