使用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
相关产品推荐
相关产品推荐

