Spring Boot 1.5.21中Hibernate Validation在Tomcat与Jetty的表现差异
首先得明确:这个问题和Tomcat/Jetty容器本身没有直接关系,核心是Hibernate Validator的校验触发逻辑在不同环境下的差异,刚好切换容器时暴露了这个问题。
为什么Tomcat下没报错,Jetty下却触发了?
你提到@NotEmpty注解已经用了两年,只在Web层校验@RequestBody参数,Tomcat下保存telephone=null的实体没问题,但Jetty下保存就触发ConstraintViolation——本质是Hibernate在实体持久化时的校验开关被打开了。
在Spring Boot 1.5.x中,Hibernate的Bean Validation触发由配置spring.jpa.properties.javax.persistence.validation.mode控制:
- 默认值
AUTO:Hibernate会自动检测是否启用校验,但这个逻辑可能因为依赖版本、配置文件加载顺序等因素出现差异 NONE:完全关闭JPA层面的实体校验CALLBACK:在pre-persist/pre-update等实体生命周期回调时触发校验
你之前Tomcat环境中,这个配置大概率是被隐式设置为NONE,或者AUTO模式下Hibernate没有启用持久化校验;而切换到Jetty后,配置被重置,校验模式变成了CALLBACK,所以userRepository.save()时触发了@NotEmpty的校验。
另外要纠正一个认知:org.hibernate.validator.constraints.NotEmpty的规则是校验对象不为null且长度/大小大于0,所以你给telephone设为null时,确实违反了这个约束——Jetty下的报错是符合校验规则的,之前Tomcat下的“正常”其实是因为校验没被触发。
两种解决思路,按需选择
1. 关闭JPA层面的实体校验(快速匹配旧行为)
如果只想保持和Tomcat下一样的逻辑,不在持久化时触发Bean Validation,直接在application.properties里加配置:
spring.jpa.properties.javax.persistence.validation.mode=NONE
这样Hibernate在保存/更新实体时就不会触发任何校验,和你之前的运行逻辑完全一致。
2. 替换注解(更规范的长期方案)
既然你的需求是禁止空字符串,但允许null(数据库字段也允许null),那@NotEmpty其实不是合适的注解——应该用javax.validation.constraints.NotBlank。
@NotBlank的规则是:字符串不为null,且去除首尾空白后长度大于0,刚好符合你“禁止空字符串,允许null”的需求。修改实体字段:
@Column(name = "telephone") @NotBlank(message = "*Please provide a telephone number") private String telephone;
这样不管是Web层的@Valid校验,还是JPA持久化时的校验,逻辑都和你的需求匹配,不会再出现null触发报错的问题。
额外提醒
数据库字段的null约束和Bean Validation是两层独立的逻辑:
- 数据库的null约束是存储层面的规则
- Bean Validation是应用层的校验规则
之前Tomcat下的“正常”其实是应用层校验没被触发,并非逻辑正确,Jetty下的报错反而帮你发现了注解使用的小问题。
内容的提问来源于stack exchange,提问作者sebasira

