You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@Size注解测试未抛出DataIntegrityViolationException问题排查

问题分析与解决方案

核心问题根源

你混淆了Bean Validation注解(如@Size、@NotBlank)和数据库约束的作用边界:

  • @Size、@NotBlank属于JSR-380规范的应用层校验注解,仅在触发Bean Validation逻辑时生效,不会直接转换为数据库约束。
  • DataIntegrityViolationException是数据库层面抛出的异常,仅当违反数据库约束(如@Column(unique=true)、nullable=false)时才会触发。
  • Spring Data JPA的save()方法默认不会自动触发Bean Validation,因此直接调用repository.save()时,@Size注解的校验逻辑根本没执行。

另外你的依赖版本存在兼容性问题:Spring Boot 3.x基于Jakarta EE 9,需要搭配Hibernate Validator 8.x(而非你手动指定的6.0.12.Final),旧版本无法正常适配。


具体修复步骤

1. 修正Maven依赖

移除手动添加的hibernate-validator依赖,Spring Boot的spring-boot-starter-web或spring-boot-starter-data-jpa已经默认引入了适配版本的校验依赖:

<!-- 移除这一段 -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.12.Final</version>
</dependency>

2. 开启JPA自动校验

在src/test/resources/application-test.properties中添加配置,让JPA在保存/更新实体时自动触发Bean Validation:

spring.jpa.properties.javax.persistence.validation.mode=auto

3. 修改测试用例

针对@Size的校验,需要捕获应用层异常ConstraintViolationException,而非数据库层面的DataIntegrityViolationException,同时调整测试数据(空字符串会先触发@NotBlank校验,无法测试@Size):

@Test
void givenShortTopicName_whenSaveTopic_thenThrowConstraintViolationException() {
    // 用长度为2的字符串触发@Size校验
    TopicEntity topic = TopicEntity.builder()
        .name("ab")
        .build();

    assertThrows(ConstraintViolationException.class,
        () -> repository.save(topic));
}

校验最佳实践

1. 分层校验策略

  • Controller层:用@Valid或@Validated注解触发请求参数校验,提前拦截无效请求:

    @PostMapping("/topics")
    public ResponseEntity<TopicEntity> createTopic(@Valid @RequestBody TopicEntity topic) {
        return ResponseEntity.ok(topicService.save(topic));
    }
    

    此时会抛出MethodArgumentNotValidException,可通过全局异常处理器统一返回错误信息。

  • Service层:手动调用Validator做业务逻辑校验,确保进入数据库前数据合法:

    @Service
    public class TopicService {
        private final Validator validator;
        private final TopicRepository repository;
    
        public TopicService(Validator validator, TopicRepository repository) {
            this.validator = validator;
            this.repository = repository;
        }
    
        public TopicEntity saveTopic(TopicEntity topic) {
            Set<ConstraintViolation<TopicEntity>> violations = validator.validate(topic);
            if (!violations.isEmpty()) {
                throw new ConstraintViolationException(violations);
            }
            return repository.save(topic);
        }
    }
    

2. 高效测试Bean Validation

不需要启动Spring上下文,直接测试Validator逻辑,提升测试速度:

class TopicEntityValidationTest {
    private Validator validator;

    @BeforeEach
    void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Test
    void whenTopicNameTooShort_thenValidationFails() {
        TopicEntity topic = TopicEntity.builder().name("ab").build();
        Set<ConstraintViolation<TopicEntity>> violations = validator.validate(topic);
        
        assertThat(violations).hasSize(1);
        assertThat(violations.iterator().next().getMessage())
            .isEqualTo("Topic name must be between 3 and 200 characters");
    }
}

3. 数据库约束作为最后防线

数据库层面的约束(如@Column(unique=true)、nullable=false)仅作为兜底机制,不要依赖它做业务校验,应用层提前拦截能减少数据库交互开销。

内容的提问来源于stack exchange,提问作者Ernesto Orozco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 13:45:15