[英]JPA / Hibernate is Inserting NULL When Default Value is Set on Field
我有一个具有布尔值的实体,该布尔值作为“ 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,你的域对象可通过龙目岛。 (Groovy中有一些类似的注解,只是检查,如果你进口使用groovy.transform或龙目岛作为包前缀)
导致我出现问题的代码是在我的测试类中:
MyObject.builder().name(NAME).username(USERNAME).build();
经过多次试验和错误,很明显,尽管我的JPA / Hibernate设置正确,但初始化默认设置为:
private Boolean activeIndicator = Boolean.TRUE;
没有被执行。
进行了一些挖掘,但是显然Lombok的Builder不是使用Java的初始化 ,而是使用自己的方法来创建对象。 这意味着在调用build()时会忽略初始化默认值 ,因此,如果未在构建器链中显式设置该字段,则该字段将保持NULL。 注意,普通的Java初始化和构造函数不受此影响。 仅当使用Lombok的生成器创建对象时,该问题才会显现出来。
此处详细介绍了详细说明此问题的问题以及龙目岛为何不实施修复程序的解释: https : //github.com/rzwitserloot/lombok/issues/663#issuecomment-121397262
有很多解决方法,您可能要使用哪种取决于您的需求。
JPA PrePersist批注:如果默认要求是数据库约束,并且您正在使用JPA或Hibernate,则可以使用此选项。 该注释将在每次实体插入和更新之前运行它所附加的方法,而不管其初始化方式如何。 很好,这样您就不必像在构建覆盖策略中那样必须在两个地方设置默认值。 我选择了此选项,因为我的业务要求对此字段具有很强的默认要求。 我将以下代码添加到我的实体类中,以解决此问题。 我也删除了isActive字段声明的“ = Boolean.TRUE”部分,因为不再需要它。
@PrePersist public void defaultIsActive() { if(isActive == null) { isActive = Boolean.TRUE; } }
在具有默认值的字段上使用@ Builder.Deafult批注。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.