hibernate

网友投稿 260 2022-08-14


hibernate

目录一、前言二、常用注解说明三、定义校验分组四、定义校验Bean五、validator基本使用六、借助BindingResult七、全局拦截校验器八、借助ValidatorUtils工具类九、自定义校验器

一、前言

高效、合理的使用hibernate-validator校验框架可以提高程序的可读性,以及减少不必要的代码逻辑。接下来会介绍一下常用一些使用方式。

二、常用注解说明

限制说明@Null限制只能为null@NotNull限制必须不为null@AssertFalse限制必须为false@AssertTrue限制必须为true@DecimalMax(value)限制必须为一个不大于指定值的数字@DecimalMin(value)限制必须为一个不小于指定值的数字@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction@Future限制必须是一个将来的日期@Max(value)限制必须为一个不大于指定值的数字@Min(value)限制必须为一个不小于指定值的数字@Past限制必须是一个过去的日期@Pattern(value)限制必须符合指定的正则表达式@Size(max,min)限制字符长度必须在min到max之间@Past验证注解的元素值(日期类型)比当前时间早@NotEmpty验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)@NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

三、定义校验分组

public class ValidateGroup {

public interface FirstGroup {

}

public interface SecondeGroup {

}

public interface ThirdGroup {

}

}

四、定义校验Bean

@Validated

@GroupSequence({ValidateGroup.FirstGroup.class, BaseMessageRequestBean.class})

public class BaseMessageRequestBean {

//渠道类型

@NotNull(message = "channelType为NULL", groups = ValidateGroup.FirstGroup.class)

private String channelType;

//消息(模板消息或者普通消息)

@NotNull(message = "data为NUll", groups = ValidateGroup.FirstGroup.class)

@Valid

private Object data;

//业务类型

@NotNull(message = "bizType为NULL", groups = ValidateGroup.FirstGroup.class)

private String bizType;

//消息推送对象

@NotBlank(message = "toUser为BLANK", groups = ValidateGroup.FirstGroup.class)

private String toUser;

private long createTime = Instant.now().getEpochSecond();

......

}

请自行参考:@Validated和@Valid区别

五、validator基本使用

@RestController

public class TestValidatorController {

@RequestMapping("/test/validator")

public void test(@Validated BaseMessageRequestBean bean){

...

}

}

这种使用方式有一个弊端,不能自定义返回异常。spring如果验证失败,则直接抛出异常,一般不可控。

六、借助BindingResult

@RestController

public class TestValidatorController {

@RequestMapping("/test/validator")

public void test(@Validated BaseMessageRequestBean bean, BindingResult result){

result.getAllErrors();

...

}

}

如果方法中有BindingResult类型的参数,spring校验完成之后会将校验结果传给这个参数。通过BindingResult控制程序抛出自定义类型的异常或者返回不同结果。

七、全局拦截校验器

当然了,需要在借助BindingResult的前提下...

@Aspect

@Component

public class ControllerValidatorAspect {

@Around("execution(* com.*.controller..*.*(..)) && args(..,result)")

public Object doAround(ProceedingJoinPoint pjp, result result) {

result.getFieldErrors();

...

}

}

这种方式可以减少controller层校验的代码,校验逻辑统一处理,更高效。

八、借助ValidatorUtils工具类

@Bean

public Validator validator() {

return new LocalValidatorFactoryBean();

}

LocalValidatorFactoryBean官方示意

LocalValidatorFactoryBean是Spring应用程序上下文中javax.validation(jsR-303)设置的中心类:它引导javax.validation.ValidationFactory并通过Spring Validator接口以及JSR-303 Validator接口和ValidatorFactory公开它。界面本身。通过Spring或JSR-303 Validator接口与该bean的实例进行通信时,您将与底层ValidatorFactory的默认Validator进行通信。这非常方便,因为您不必在工厂执行另一个调用,假设您几乎总是会使用默认的Validator。这也可以直接注入Validator类型的任何目标依赖项!从Spring 5.0开始,这个类需要Bean Validation 1.1+,特别支持Hibernate Validator 5.x(参见setValidationMessageSource(org.springframework.context.MessageSource))。这个类也与Bean Validation 2.0和Hibernate Validator 6.0运行时兼容,有一个特别说明:如果你想调用BV 2.0的getClockProvider()方法,通过#unwrap(ValidatorFactory.class)获取本机ValidatorFactory,在那里调用返回的本机引用上的getClockProvider()方法。Spring的MVC配置命名空间也使用此类,如果存在javax.validation API但未配置显式Validator。

public class ValidatorUtils implements ApplicationContextAware {

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

ValidatorUtils.validator = (Validator) applicationContext.getBean("validator");

}

private static Validator validator;

public static Optional validateResultProcess(Object obj) {

Set> results = validator.validate(obj);

if (CollectionUtils.isEmpty(results)) {

return Optional.empty();

}

StringBuilder sb = new StringBuilder();

for (Iterator> iterator = results.iterator(); iterator.hasNext(); ) {

sb.append(iterator.next().getMessage());

if (iterator.hasNext()) {

sb.append(" ,");

}

}

return Optional.of(sb.toString());

}

}

为什么要使用这个工具类呢?

1、controller方法中不用加入BindingResult参数

2、controller方法中需要校验的参数也不需要加入@Valid或者@Validated注解

怎么样是不是又省去了好多代码,开不开心。

具体使用,在controller方法或者全局拦截校验器中调用ValidatorUtils.validateResultProcess(需要校验的Bean) 直接获取校验的结果。

请参考更多功能的ValidatorUtils工具类。

九、自定义校验器

定义一个MessageRequestBean,继承BaseMessageRequestBean,signature字段需要我们自定义校验逻辑。

@Validated

@GroupSequence({VIRaASresalidateGroup.FirstGroup.class, ValidateGroup.SecondeGroup.class, MessageRequestBean.class})

@LogicValidate(groups = ValidateGroup.SecondeGroup.class)

public class MessageRequestBean extends BaseMessageRequestBean {

//签名信息(除该字段外的其他字段按照字典序排序,将值顺序拼接在一起,进行md5+Base64签名算法)

@NotBlank(message = "signature为BLANK", groups = ValidateGroup.FirstGroup.class)

private String signature;

...

}

实现自定义校验逻辑也很简单......

1、自定义一个带有 @Constraint注解的注解@LogicValidate,validatedBy 属性指向该注解对应的自定义校验器

@Target({TYPE})

@Retention(RUNTIME)

//指定验证器

@Constraint(validatedBy = LogicValidator.class)

@Documented

public @interface LogicValidate {

String message() default "校验异常";

//分组

Class>[] groups() default {};

Class extends Payload>[] payload() default {};

}

2、自定义校验器LogicValidator,泛型要关联上自定义的注解和需要校验bean的类型

public class LogicValidator implements ConstraintValidator {

@Override

public void initialize(LogicValidate logicValidate) {

}

@Override

public boolean isValid(MessageRequestBean messageRequestBean, ConstraintValidatorContext context) {

String toSignature = StringUtils.join( messageRequestBean.getBizType()

, messageRequestBean.getChannelType()

, messageRequestBean.getData()

, messageRequestBean.getToUser());

String signature = new Base64().encodeAsString(DigestUtils.md5(toSignature));

if (!messageRequestBean.getSignature().equals(signature)) {

context.disableDefaultConstraintViolation();

context.buildConstraintViolationWithTemplate("signature校验失败")

.addConstraintViolation();

return false;

}

return true;

}

}

可以通过ConstraintValidatorContext禁用掉默认的校验配置,然后自定义校验配置,比如校验失败后返回的信息。

十、springboot国际化信息配置

@Configuration

@ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)

@Conditional(ResourceBundleCondition.class)

@EnableConfigurationProperties

@ConfigurationProperties(prefix = "spring.messages")

public class MessageSourceAutoConfiguration {

private static final Resource[] NO_RESOURCES = {};

/**

* Comma-separated list of basenames, each following the ResourceBundle convention.

* Essentially a fully-qualified classpath location. If it doesn't contain a package

* qualifier (such as "org.mypackage"), it will be resolved from the classpath root.

*/

private String basename = "messages";

/**

* Message bundles encoding.

*/

private Charset encoding = Charset.forName("UTF-8");

/**

* Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles

* are cached forever.

*/

private int cacheSeconds = -1;

/**

* Set whether to fall back to the system Locale if no files for a specific Locale

* have been found. if this is turned off, the only fallback will be the default file

* (e.g. "messages.properties" for basename "messages").

*/

private boolean fallbackToSystemLocale = true;

/**

* Set whether to always apply the MessageFormat rules, parsing even messages without

* arguments.

*/

private boolean alwaysUseMessageFormat = false;

@Bean

public MessageSource messageSource() {

ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();

if (StringUtils.hasText(this.basename)) {

messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(

StringUtils.trimAllWhitespace(this.basename)));

}

if (this.encoding != null) {

messageSource.setDefaultEncoding(this.encoding.name());

}

messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);

messageSource.setCacheSeconds(this.cacheSeconds);

messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);

return messageSource;

}

public String getBasename() {

return this.basename;

}

public void setBasename(String basename) {

this.basename = basename;

}

public Charset getEncoding() {

return this.encoding;

}

public void setEncoding(Charset encoding) {

this.encoding = encoding;

}

public int getCacheSeconds() {

return this.cacheSeconds;

}

public void setCacheSeconds(int cacheSeconds) {

this.cacheSeconds = cacheSeconds;

}

public boolean isFallbackToSystemLocale() {

return this.fallbackToSystemLocale;

}

public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {

this.fallbackToSystemLocale = fallbackToSystemLocale;

}

public boolean isAlwaysUseMessageFormat() {

return this.alwaysUseMessageFormat;

}

public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) {

this.alwaysUseMessageFormat = alwaysUseMessageFormat;

}

protected static class ResourceBundleCondition extends SpringBootCondition {

private static ConcurrentReferenceHashMap cache = new ConcurrentReferenceHashMap();

@Override

public ConditionOutcome getMatchOutcome(ConditionContext context,

AnnotatedTypeMetadata metadata) {

String basename = context.getEnvironment()

.getProperty("spring.messages.basename", "messages");

ConditionOutcome outcome = cache.get(basename);

if (outcome == null) {

outcome = getMatchOutcomeForBasename(context, basename);

cache.put(basename, outcome);

}

return outcome;

}

private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,

String basename) {

ConditionMessage.Builder message = ConditionMessage

.forCondition("ResourceBundle");

for (String name : StringUtils.commaDelimitedListToStringArray(

StringUtils.trimAllWhitespace(basename))) {

for (Resource resource : getResources(context.getClassLoader(), name)) {

if (resource.exists()) {

return ConditionOutcome

.match(message.found("bundle").items(resource));

}

}

}

return ConditionOutcome.noMatch(

message.didNotFind("bundle with basename " + basename).atAll());

}

private Resource[] getResources(ClassLoader classLoader, String name) {

try {

return new PathMatchingResourcePatternResolver(classLoader)

.getResources("classpath*:" + name + ".properties");

}

catch (Exception ex) {

return NO_RESOURCES;

}

}

}

}

从上面的MessageSource自动配置可以看出,可以通过spring.message.basename指定要配置国际化文件位置,默认值是“message”。spring boot默认就支持国际化的,默认会去resouces目录下寻找message.properties文件。

这里就不进行过多关于国际化相关信息的介绍了,肯定少不了区域解析器。springboot国际化相关知识请参考:Spring Boot国际化(i18n)


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Quartz与Spring集成的两种方法示例
下一篇:spring
相关文章

 发表评论

暂时没有评论,来抢沙发吧~