AEv*_*ans 6 java hibernate jpa default lombok
我有一个具有布尔值的实体,该值作为“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;
}
Run Code Online (Sandbox Code Playgroud)
然而,当我尝试运行测试以将其保存到数据库时,尽管我将其初始化为 Boolean.TRUE,但我会收到一个NULL Constraint Violation 错误,尽管我将其初始化为 Boolean.TRUE,如您在上面的代码中所见。当我打开 Hibernate 的调试日志时,我能够验证它是否在插入语句中发送 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
}
Run Code Online (Sandbox Code Playgroud)
事实证明,问题不在于 Hibernate,而在于 Lombok。如果您使用的是@Data、@EqualsAndHashCodeOf、@NoArgsConstructor、@AllArgsConstructor或@Builder 等注释,则您的域对象可能正在使用 Lombok。(Groovy中有一些类似的注解,只是检查,如果你进口使用groovy.transform或龙目岛作为包前缀)
导致我的问题的代码位在我的测试类中:
MyObject.builder().name(NAME).username(USERNAME).build();
Run Code Online (Sandbox Code Playgroud)
经过多次反复试验,很明显,虽然我的 JPA/Hibernate 设置是正确的,但初始化默认值:
private Boolean activeIndicator = Boolean.TRUE;
Run Code Online (Sandbox Code Playgroud)
没有被处决。
进行了一些挖掘,但显然Lombok 的 Builder 没有使用 Java 的初始化,而是使用它自己的方法来创建对象。这意味着当您调用 build() 时会忽略初始化默认值,因此如果您没有在构建器链中明确设置该字段,则该字段将保持为 NULL。请注意,正常的 Java 初始化和构造函数不受此影响;该问题仅在使用 Lombok 的构建器创建对象时才会出现。
详细说明此问题的问题以及 Lombok 关于他们为何不实施修复的解释在此处详细说明:https : //github.com/rzwitserloot/lombok/issues/663#issuecomment-121397262
对此有许多变通方法,您可能想要使用的方法取决于您的需要。
JPA PrePersist Annotation:如果您对默认值的要求是针对数据库约束,并且您正在使用 JPA 或 Hibernate,则可以使用此选项。此注释将在每次插入和更新实体之前运行它所附加的方法,而不管它是如何初始化的。这很好,这样您就不必像使用构建覆盖策略那样在两个地方设置默认值。我选择了这个选项,因为我的业务需求在这个领域有很强的默认要求。我将下面的代码添加到我的实体类中以解决问题。我还删除了 isActive 字段声明的“= Boolean.TRUE”部分,因为它不再需要。
@PrePersist
public void defaultIsActive() {
if(isActive == null) {
isActive = Boolean.TRUE;
}
}
Run Code Online (Sandbox Code Playgroud)