gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 04:27:23 25 4
gpt4 key购买 nike

尽管违反直觉并且显然 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.getPeriod().equals(po2.getPeriod()) 这样的东西。 .在访问 Date 时,我们会通过额外的间接方式为此付出代价。直接字段: po1.getPeriod().getStart() .这通常是一个很好的权衡,但当我们必须写 po1.getPeriod() == null ? null : po1.getPeriod().getStart() 时就不行了。而是因为我们的 JPA 提供者喜欢 null。

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

最佳答案

Eclipselink官方解决方案

为每个受影响的实体写一个单独的 DescriptorCustomizer实现如所述here并使用 @Customizer 使其处于事件状态注释如所述here .如果没有技巧,我们就不能对每个受影响的类使用相同的定制器,因为定制器需要知道要为其调用的可嵌入字段的名称setIsNullAllowed(false)。 .

这是一个通用的DescriptorCustomizer可用于使固定类列表中的每个可嵌入类不可为空的实现:

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注解,我们还初始化字段 periodnew Period()因为否则新实体仍将具有 null 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);
}
}

请注意 synchronized是线程安全所必需的。

Hibernate 的简单 hack

而不是上述对 Period 的更改和 PeriodOwner , 将一个未使用的私有(private)非空字段添加到 Period .例如:
@Formula("1")
private int workaroundForBraindeadJpaImplementation;

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

关于hibernate - 如何防止 JPA 将可嵌入对象设置为 null 只是因为它的所有字段都为 null?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40979957/

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