我有一个具有 boolean 值的实体,该 boolean 值作为“Y”或“N”字符保留在数据库中:
@Data
@Entity
@Table(name = "MYOBJECT", schema = "MYSCHEMA")
@EqualsAndHashCode(of = "id")
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MyObject {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MYOBJECT_SEQ")
@SequenceGenerator(name = "MYOBJECT_SEQ", sequenceName = "MYSCHEMA.MYOBJECT_SEQ")
private Long id;
@NotEmpty(message = "Name may not be empty")
@SafeHtml(whitelistType = WhiteListType.NONE, message = "Name may not contain html or javascript")
@Column(name = "NAME", nullable = false, length = 60)
private String name;
// Other fields...
@Type(type = "yes_no")
@Column(name = "ACTIVE", length = 1, nullable = false)
private Boolean isActive = Boolean.TRUE;
@SafeHtml(whitelistType = WhiteListType.NONE, message = "username may not contain html or javascript")
@Column(name = "USERNAME", nullable = false, length = 30)
private String username;
}
但是,当我尝试运行测试以将其保存到数据库时,尽管我将初始化为Boolean.TRUE ,但仍会收到activeIndicator 的 NULL约束违规错误,如您在代码中所见以上。当我打开Hibernate的调试日志时,我能够验证它在insert语句中发送的是NULL。下面是我的测试代码:
@Test
public void testSave() {
// Setup Data
final String NAME = "Test name";
final String USERNAME = "Test username";
MyObject stagedObject = MyObject.builder().name(NAME).username(USERNAME).build();
// Run test
MyObject savedObject = repo.save(stagedObject);
entityManager.flush(); // force JPA to sync with db
entityManager.clear(); // force JPA to fetch a fresh copy of the objects instead of relying on what's in memory
// Assert
MyObject fetchedObject = repo.findOne(savedObject.getId());
assertThat(fetchedObject.getIsActive(), is(Boolean.TRUE));
// other assertions
}
最佳答案
事实证明,问题不在于Hibernate,而在于Lombok。如果您使用的是@ Data,@ EqualsAndHashCodeOf,@ NoArgsConstructor,@ AllArgsConstructor或@Builder等注释,则您的域对象可能正在使用Lombok。 (Groovy具有一些类似的注释,只需检查您的导入是否使用 groovy.transform 或 lombok 作为软件包前缀)
导致我出现问题的代码是在我的测试课中:
MyObject.builder().name(NAME).username(USERNAME).build();
经过多次试验和错误,很明显,尽管我的JPA/Hibernate设置正确,但初始化默认设置为:
private Boolean activeIndicator = Boolean.TRUE;
没有被执行。
进行了一些挖掘,但显然 Lombok的Builder并未使用Java的初始化,而是使用了自己的创建对象的方式。这意味着当您调用build()时,会忽略初始化默认值,因此,如果未在构建器链中显式设置该字段,则该字段将保持NULL。 注意,普通的Java初始化和构造函数不受此影响;仅当使用Lombok的生成器创建对象时,该问题才会显现出来。
详细说明此问题的问题以及Lombok关于为何不实现此修复程序的解释在此处进行了详细说明:https://github.com/rzwitserloot/lombok/issues/663#issuecomment-121397262
有许多解决方法,您可能要使用哪种方法取决于您的需求。
@PrePersist
public void defaultIsActive() {
if(isActive == null) {
isActive = Boolean.TRUE;
}
}