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

使用Instancio生成符合约束的PersonEntity测试对象遇长度问题求助

问题描述

我正在为遵循数据库表约束的PersonEntity实体类创建JUnit测试对象,实体类代码如下:

@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "persons", schema = "vetting_service")
@Getter
@Setter
@NoArgsConstructor
@Slf4j
@ToString(onlyExplicitlyIncluded = true)
public class PersonEntity extends AuditableEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @Null(groups = OnCreate.class)
    @NotNull(groups = OnUpdate.class)
    @ToString.Include
    private Long id;

    @Column(name = "source_person_id")
    @Size(max = Person.SOURCE_ID_MAXLENGTH)
    @ToString.Include
    private String sourcePersonId;

    @Column(name = "first_name")
    @NotBlank
    @Size(max = Person.FIRST_NAME_MAXLENGTH)
    private String firstName;

    @Column(name = "middle_name")
    @Size(max = Person.MIDDLE_NAME_MAXLENGTH)
    @NullOrNotBlank
    private String middleName;

    @Column(name = "last_name")
    @NotBlank
    @Size(max = Person.LAST_NAME_MAXLENGTH)
    private String lastName;

    /**
     * SSN. Not updatable!
     */
    @Column(name = "ssn", length = 72, updatable = false)
    @NotBlank
    @Size(min = Person.SSN_LENGTH, max = Person.SSN_LENGTH)
    @Pattern(regexp = "^\\d{9}$", message = "must be a nine digit string")
    @Convert(converter = AttributeEncryptor.class)
    private String ssn;

    @Column(name = "ssn_last_4", updatable = false)
    @NotBlank
    @Size(max = 4)
    private String ssnLast4;

    @Column(name = "dob")
    @NotNull
    @Past
    private LocalDate dateOfBirth;

    @ElementCollection
    @CollectionTable(name = "addresses", schema = "vetting_service", joinColumns = @JoinColumn(name = "PERSON_ID"))
    @MapKeyEnumerated(EnumType.STRING)
    @PhysicalAddressRequired
    private Map<AddressType, @Valid AddressEmbeddable> addresses = new HashMap<>();

    /**
     * The home phone of the individual to be vetted.
     */
    @ElementCollection
    @CollectionTable(name = "phones", schema = "vetting_service", joinColumns = @JoinColumn(name = "PERSON_ID"))
    @MapKeyEnumerated(EnumType.STRING)
    private Map<PhoneType, @Valid PhoneEmbeddable> phones = new HashMap<>();

    /**
     * The home phone of the individual to be vetted.
     */
    @ElementCollection
    @CollectionTable(name = "email_addresses", schema = "vetting_service", joinColumns = @JoinColumn(name = "PERSON_ID"))
    @MapKeyEnumerated(EnumType.STRING)
    private Map<EmailAddressType, @Valid EmailAddressEmbeddable> emailAddresses = new HashMap<>();

    @SuppressWarnings("checkstyle:magicnumber")
    public void setSsn(@NotNull final String ssn) {
        this.ssn = ssn;
        this.ssnLast4 = StringUtils.right(ssn, 4);
    }

    /**
     * Convenience method to get PHYSICAL address, which can never be null.
     *
     * @return The PHYSICAL address.
     */
    public AddressEmbeddable physicalAddress() {
        return this.addresses.get(AddressType.PHYSICAL);
    }
}

最初用以下代码生成测试对象:

final PersonEntity tstPerson = Instancio.of(PersonEntity.class).create();

但生成的数据不符合约束,比如firstName字段长度超出设定值。之后尝试开启相关设置:

final PersonEntity tstPerson = Instancio.of(PersonEntity.class)
                .withSetting(Keys.BEAN_VALIDATION_ENABLED, true)
                .withSetting(Keys.JPA_ENABLED, true)
                .create();

仍出现错误:

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: String or binary data would be truncated in table 'master.persons', column 'first_name'. Truncated value: 'JQHQJNFUMRWKUNRQQJEPHNGNREYILYFHHZNFZKABGAHZQZNIOESPEMQZVFYJCMPLTEYUVGAKPLLFDFEDJAVAPBDLFWQPINOEQVTA'.

请问如何生成符合该实体类约束的测试对象?

解决方法

1. 显式指定字段长度约束

Instancio可能无法自动识别Person类中定义的常量(如Person.FIRST_NAME_MAXLENGTH),需手动为字段指定长度范围:

final PersonEntity tstPerson = Instancio.of(PersonEntity.class)
        .withSetting(Keys.BEAN_VALIDATION_ENABLED, true)
        .withSetting(Keys.JPA_ENABLED, true)
        // 限制firstName长度不超过最大值
        .generate(field(PersonEntity::getFirstName), gen -> gen.string().maxLength(Person.FIRST_NAME_MAXLENGTH))
        // 限制lastName长度
        .generate(field(PersonEntity::getLastName), gen -> gen.string().maxLength(Person.LAST_NAME_MAXLENGTH))
        // 限制sourcePersonId长度
        .generate(field(PersonEntity::getSourcePersonId), gen -> gen.string().maxLength(Person.SOURCE_ID_MAXLENGTH))
        // 处理middleName:允许为空且符合长度要求
        .generate(field(PersonEntity::getMiddleName), gen -> gen.string().maxLength(Person.MIDDLE_NAME_MAXLENGTH).nullable())
        // 生成符合格式的SSN,利用实体类setSsn方法自动设置ssnLast4
        .generate(field(PersonEntity::getSsn), gen -> gen.string().pattern("^\\d{9}$"))
        .create();

2. 处理自定义校验注解

实体类中的@NullOrNotBlank和@PhysicalAddressRequired是自定义注解,Instancio默认不支持,需手动配置:

  • 对于middleName,用.nullable()允许为空,同时限制长度,满足@NullOrNotBlank规则;
  • 对于addresses,确保AddressType.PHYSICAL对应的地址存在且有效:
.supply(field(PersonEntity::getAddresses), () -> {
    Map<AddressType, AddressEmbeddable> addresses = new HashMap<>();
    // 生成符合约束的物理地址
    AddressEmbeddable physicalAddr = Instancio.of(AddressEmbeddable.class)
            .withSetting(Keys.BEAN_VALIDATION_ENABLED, true)
            .create();
    addresses.put(AddressType.PHYSICAL, physicalAddr);
    return addresses;
})

3. 手动验证对象合法性

生成对象后,用Bean Validation的Validator校验所有约束,确保数据合规:

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<PersonEntity>> violations = validator.validate(tstPerson);
if (!violations.isEmpty()) {
    violations.forEach(v -> System.out.println(v.getPropertyPath() + ": " + v.getMessage()));
    throw new AssertionError("生成的PersonEntity不符合约束");
}

4. 同步数据库表字段长度

错误提示数据库表master.persons的first_name字段截断数据,需确认数据库表字段长度是否与实体类@Size注解中的最大值一致。若数据库字段长度更小,即使生成符合实体类约束的数据,插入时仍会报错,需同步两者配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 20:40:55