当前位置: 首页 > 知识库问答 >
问题:

我如何防止JPA将可嵌入对象设置为null,仅仅因为它的所有字段都是null?

相野
2023-03-14

尽管违反直觉,而且显然不是JPA标准所要求的,但Eclipselink和Hibernate都竭尽全力创建了以下NullPointerExceptions的源代码:当嵌入实体中的对象的所有字段都是null时,它们会将字段本身替换为null。下面是一个简化的例子:

@Embeddable
public class Period {
    private Date start;
    public Date getStart() {
        return start;
    }
    private Date end;
    public Date getEnd() {
        return end;
    }
    public boolean equals(Period other) { // TODO: implement hashCode()!
        return Objects.equals(start, other.start) && Objects.equals(end, other.end);
    }
}

@Entity
public class PeriodOwner {
    @Embedded @AttributeOverrides({...})
    private Period period;
    public Period getPeriod() {
        return period;
    }
}

我们的想法是,我们可以编写类似于的东西po1.get周期(). equals(po2.get周期())。当直接访问Date字段时,我们会为此付出额外的间接代价:po1.get周期(). getStart()。这通常是一个很好的权衡,但当我们必须编写po1.get周期()==空?因为我们的JPA提供者喜欢null,所以po1.get。

如何确保getPeriod()从不返回null?不幸的是,在标准JPA中,既没有Java注释也没有XML设置来解决这个问题,无论是全局性的,还是针对特定的嵌入或可嵌入的。


共有2个答案

田兴怀
2023-03-14

JPA规范完全忽略了对空嵌入对象的处理,并将其留给实现来做它们喜欢做的事情(很好,是吗?)。JPA2.2已经有人要求它,但谁知道甲骨文是否会费心提供。

为嵌入式字段/属性提供2个扩展属性

@Extension(key="null-indicator-column", value="MY_COL")
@Extension(key="null-indicator-value", value="SomeValue")

因此,当嵌入对象为null时,此列将设置为该值,同样,当读取对象时,它可以检测到null嵌入对象并将其正确返回给用户。

爱炯
2023-03-14

日食的官方解决方案

对于每个受影响的实体,按照此处所述编写一个单独的ExplptorCustomzer实现,并使用此处所述的@Customzer注释使其处于活动状态。如果没有技巧,我们不能对每个受影响的类使用相同的定制程序,因为定制程序需要知道它要调用的可嵌入字段的名称。

下面是一个通用的描述符自html" target="_blank">定义器实现,可用于使固定类列表中的每一个可嵌入的类不可为空:

public class MyUniversalDescriptorCustomizer implements DescriptorCustomizer {

    private static final ImmutableSet<Class> NON_NULLABLE_EMBEDDABLES = ImmutableSet.of(Period.class);

    @Override
    public void customize(ClassDescriptor cd) throws Exception {
        Class entityClass = cd.getJavaClass();
        for (Field field : entityClass.getDeclaredFields()) {
            if (NON_NULLABLE_EMBEDDABLES.contains(field.getType())) {
                System.out.println(field.getName());
                AggregateObjectMapping aom = (AggregateObjectMapping) cd.getMappingForAttributeName(field.getName());
                aom.setIsNullAllowed(false);
            }
        }
    }
}

要解决特定示例中的空问题,我们必须修改PeriodOwner,如下所示:

@Entity
@Customizer(MyUniversalCustomizer.class)
public class PeriodOwner {
    @Embedded @AttributeOverrides({...})
    private Period period = new Period();
    public Period getPeriod() {
        return period;
    }
}

注意,除了@Customizer注释外,我们还使用new period()初始化字段period,因为否则新实体仍然会有空的period字段。

Hibernate的官方解决方案

显然,自Hibernate 5.1以来,有一个设置hibernate.create_empty_composites.enabled。(因为我没有使用Hibernate,所以我没有尝试找出这个设置的位置。)

行人工作区

下面的代码在不太污染代码的情况下解决了这个问题,但仍然很混乱。

@Embeddable
public class Period {
    private Date start;
    public Date getStart() {
        return start;
    }
    private Date end;
    public Date getEnd() {
        return end;
    }
    public boolean equals(Period other) { // TODO: implement hashCode()!
        return Objects.equals(start, other.start) && Objects.equals(end, other.end);
    }

    public static Period orNew(Period period) {
        return period != null ? period : new Period();
    }
}

@Entity
public class PeriodOwner {
    @Embedded @AttributeOverrides({...})
    private Period period;

    public synchronized Period getPeriod() {
        return period = Period.orNew(period);
    }
}

请注意,线程安全需要同步

Hibernate的简单破解

将一个未使用的私有非空字段添加到期间,而不是对期间期间所有者进行上述更改。例如:

@Formula("1")
private int workaroundForBraindeadJpaImplementation;

通过使用@Formula(Hibernate扩展),我们可以避免在数据库中为该字段添加额外的列。TomášZáluský在这里描述了这一解决方案。这可能对那些只想在某些情况下改变行为的人有用。

 类似资料:
  • 这个问题现有两种变体: 仅当字段不为空时验证:有条件地处理字符串的解决方案 Hibernate验证-仅在对象不为null时进行验证:唯一的答案演示了该行为,并提示您在手动触发验证时如何处理该行为 我的问题添加了限定符,即在可能为空的对象上使用@Valid时如何执行此操作。 这里的用例是,我们有两个字段,其中一个或另一个需要不为null(包含字段的类上的自定义验证器)。当一个不为null时,我需要它

  • 大家好! 我正在Spring应用程序中实现Mapstruct映射器。 有两个相关实体:和。只是为了简化->用户可以有许多订单。 OrderMapper类: 好吧,我的问题是: 如果我使用以下命令传递OrderDTO: 前面描述的情况在我的应用程序中造成了巨大的问题。如果有任何帮助,我将不胜感激。

  • 我正在尝试做一些清理方法。其中我有几个字段,我想调用它们各自的清理方法,然后将它们设置为NULL。 我将所有对象添加到ArrayList中,然后将其传递给该方法: 然而,这并不起作用,我的单元测试显示,在调用此方法后,对象不为null。 如何将列表中的所有对象设置为NULL?

  • 我有一个Java对象,我想序列化为JSON。但是,在此之前,我为该对象的字段设置了一些属性。现在我想将这个对象序列化为JSON。如何仅序列化显式赋值的字段,而基本上排除所有其他字段? 对我来说,在对象类上添加这些注释是不起作用的:和。原因是我有一些基本数据类型的字段。通过添加上述两个注释之一,我并没有阻止原始数据类型字段被序列化。例如,我有几个布尔字段,它们将被序列化为默认值。但是,我不希望这些字

  • 我正在使用Jackson,我有一些JSON模式对象设置如下: 我试图忽略所有为空或空的字段,这工作正常。但我也想忽略字段全部为空或空的对象。例如: 生成的 JSON 字符串是 {“name”:“John Doe”,“child”:{},“sibling”:{}},但我希望它是 。创建 时需要初始化和,所以我不想更改它。有没有办法让杰克逊使用自定义序列化程序将具有空字段的对象视为空?我已经看到了对特

  • 我有一个对象,我想检查这个对象或嵌套字段是否为空。我想打印这个网状字段,但我应该检查某个级别是否为空,否则我会得到空指针异常。 我知道我可以做到: 但时间太长了。你知道更好的检查方法吗?