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

使用javax.validation.Validator未检测到约束违规?对象未赋值问题

为何使用javax.validation.Validator时未检测到约束违规?

问题描述

当对象未被完整赋值时,使用javax.validation.Validator未检测到预期的约束违规。测试场景中,配置文件缺少数据库密码字段,但校验结果显示0个约束违规,与预期不符。

配置文件 multi-tenant-database-config-invalid-no-schema.yml

tenants:
  "001":
    tenantId: "001"
    configuration:
      database:
        url: "jdbc:postgresql://localhost:5432/postgres?currentSchema=001"
        username: "postgres"

JUnit测试类

public class AppTest {

  @Test
  public void testInvalid() throws Exception {

    Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

    ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());

    File multiTenantConfigurationFile = getMultiTenantTestConfiguration("config/multi-tenant-database-config-invalid-no-schema.yml");

    DatabaseEnabledTenants databaseEnabledTenants = objectMapper.readValue(multiTenantConfigurationFile, DatabaseEnabledTenants.class);

    Set<ConstraintViolation<DatabaseEnabledTenants>> constraintViolations = validator
            .validate(databaseEnabledTenants);
    Assertions.assertEquals(0, constraintViolations.size()); // <- 预期此处失败!
  }

  private File getMultiTenantTestConfiguration(String filename) throws URISyntaxException {
    ClassLoader classLoader = getClass().getClassLoader();
    URL resource = classLoader.getResource(filename);
    File file = new File(resource.toURI());
    return file;
  }
}

相关实体类

DatabaseEnabledTenants.java

@Valid
@Component
@ConfigurationProperties(prefix = "multi-tenancy")
public class DatabaseEnabledTenants {

  @Valid
  @NotNull private Map<String, DatabaseEnabledTenant> tenants = new HashMap<>();

  public Map<String, DatabaseEnabledTenant> getTenants() {
    return tenants;
  }

  public void setTenants(Map<String, DatabaseEnabledTenant> tenants) {
    this.tenants = tenants;
  }
}

DatabaseEnabledTenant.java

@Valid
public class DatabaseEnabledTenant extends TenantDetails {

  @Valid
  @NotNull private Configuration configuration;

  /** 空构造函数,后续赋值。 */
  public DatabaseEnabledTenant() {}

  public Configuration getConfiguration() {
    return configuration;
  }

  public void setConfiguration(Configuration configuration) {
    this.configuration = configuration;
  }
}

Configuration.java

public class Configuration {

  @NotNull private Database database;

  public Configuration() {}

  public Database getDatabase() {
    return database;
  }

  public void setDatabase(Database database) {
    this.database = database;
  }
}

Database.java

public class Database {

  @JsonProperty("url")
  @NotBlank(message = "URL不得在租户配置中为空")
  @SchemaNameInJdbcUrlConstraint
  private String jdbcUrl;

  @NotBlank(message = "数据库用户名不得在租户配置中为空")
  private String username;

  @NotNull(message = "数据库密码不得在租户配置中为null")
  private String password;

  public Database() {}

  public String getUrl() {
    return url;
  }

  public void setUrl(String url) {
    this.url = url;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }
}

原因分析

1. 嵌套对象未添加@Valid注解触发递归校验

Configuration类的database字段仅标注了@NotNull,缺少@Valid注解。Bean Validation需要通过@Valid来触发嵌套对象的递归校验,因此只会检查database是否不为null,不会深入校验Database类内部的password、jdbcUrl等字段的约束。

2. Database类字段与getter/setter不匹配

Database类中字段定义为jdbcUrl,但对应的getter/setter为getUrl()/setUrl(),且方法内部操作未定义的url变量(存在编译错误):

  • ObjectMapper依赖@JsonProperty("url")能将配置值映射到jdbcUrl字段,但Bean Validation默认基于JavaBean属性(getter方法对应的属性名url)校验,字段jdbcUrl上的约束无法关联到属性url,导致@NotBlank和自定义约束失效。
  • 代码存在语法错误,未定义url变量,无法正常编译运行。

3. @ConfigurationProperties前缀在测试场景未生效

测试中使用ObjectMapper直接反序列化YAML,未启用Spring的@ConfigurationProperties绑定机制,因此@ConfigurationProperties(prefix = "multi-tenancy")不生效。不过当前配置文件根节点tenants与类字段名匹配,反序列化正常,并非校验失败的直接原因。


解决方案

1. 为Configuration类的database字段添加@Valid注解

修改Configuration.java,添加@Valid以触发嵌套校验:

public class Configuration {

  @Valid
  @NotNull private Database database;

  // 原getter/setter保持不变
}

2. 修正Database类的字段与getter/setter匹配问题

方案一:统一字段名与getter/setter
将字段名改为url,确保与getter/setter一致:

public class Database {

  @JsonProperty("url")
  @NotBlank(message = "URL不得在租户配置中为空")
  @SchemaNameInJdbcUrlConstraint
  private String url;

  @NotBlank(message = "数据库用户名不得在租户配置中为空")
  private String username;

  @NotNull(message = "数据库密码不得在租户配置中为null")
  private String password;

  public Database() {}

  public String getUrl() {
    return url;
  }

  public void setUrl(String url) {
    this.url = url;
  }

  // 原username、password的getter/setter不变
}

方案二:调整getter/setter适配字段名
保持字段名为jdbcUrl,修改getter/setter并调整@JsonProperty位置:

public class Database {

  @NotBlank(message = "URL不得在租户配置中为空")
  @SchemaNameInJdbcUrlConstraint
  private String jdbcUrl;

  @NotBlank(message = "数据库用户名不得在租户配置中为空")
  private String username;

  @NotNull(message = "数据库密码不得在租户配置中为null")
  private String password;

  public Database() {}

  @JsonProperty("url")
  public String getJdbcUrl() {
    return jdbcUrl;
  }

  @JsonProperty("url")
  public void setJdbcUrl(String jdbcUrl) {
    this.jdbcUrl = jdbcUrl;
  }

  // 原username、password的getter/setter不变
}

3. (可选)测试中启用Spring配置绑定

若需模拟Spring的@ConfigurationProperties绑定,可使用@SpringBootTest配合@TestPropertySource,或通过ConfigurationPropertiesBindingPostProcessor完成绑定,确保前缀配置生效。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 11:45:36