gpt4 book ai didi

java - 当在字段上设置默认值时,JPA/Hibernate 插入 NULL

转载 作者:行者123 更新时间:2023-11-30 07:58:53 31 4
gpt4 key购买 nike

我有一个具有 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;
}

然而,当我尝试运行测试以将其保存到数据库时,我会收到 activeIndicator 的 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
}

最佳答案

事实证明,问题不在于 Hibernate,而在于 Lombok。如果您使用诸如 @Data@EqualsAndHashCodeOf@NoArgsConstructor@AllArgsConstructor 或 < em>@Builder,您的域对象可能正在使用 Lombok。 (Groovy 有一些类似的注释,只需检查您的导入是否使用 groovy.transformlombok 作为包前缀)

导致我出现问题的代码位在我的测试类中:

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

有许多解决方法,您可能想要使用的解决方法取决于您的需要。

  • Custom Constructor(s):可以创建自定义构造函数来代替构建器或与构建器一起创建。当您需要设置默认值时,请使用您的构造函数而不是构建器。 - 这个解决方案让你看起来甚至不应该为 builder 而烦恼。您可能希望保留构建器的唯一原因是构建器是否能够将创建委托(delegate)给您的构造函数,从而在构建时获得初始化的默认值。我不知道这是否可行,因为我自己还没有测试过,所以我决定走另一条路。如果您进一步研究了此选项,请务必回复。
  • 覆盖构建方法:如果您打算广泛使用构建器并且不想编写一堆构造函数,那么此解决方案适合您。您可以在自己的构建方法实现中设置默认值,并委托(delegate)给 super 来进行实际构建。请务必妥善记录您的代码,以便 future 的维护者知道在对象和构建方法中设置默认值,这样无论对象如何初始化,它们都会被设置。
  • JPA PrePersist Annotation:如果您对默认值的要求是针对数据库约束,并且您使用的是 JPA 或 Hibernate,则可以使用此选项。此注释将在实体的每次插入和更新之前运行它所附加的方法,无论它是如何初始化的。这很好,因此您不必像使用构建覆盖策略那样在两个地方设置默认值。我选择了这个选项,因为我的业务需求对该字段有很强的默认要求。我将下面的代码添加到我的实体类中以解决问题。我还删除了 isActive 字段声明的“= Boolean.TRUE”部分,因为它不再是必需的。

    @PrePersist
    public void defaultIsActive() {
    if(isActive == null) {
    isActive = Boolean.TRUE;
    }
    }

关于java - 当在字段上设置默认值时,JPA/Hibernate 插入 NULL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39979698/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com