Eclipselink官方解决方案
为每个受影响的实体编写单独的DescriptorCustomizer
实施如所描述的here http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Embeddable#Nullable_embedded_values并使用@Customizer
注释如所描述here http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_customizer.htm。如果没有技巧,我们就无法对每个受影响的类使用相同的定制器,因为定制器需要知道它要调用的可嵌入字段的名称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);
}
}
}
}
为了解决我们具体示例中的 null 问题,我们必须修改PeriodOwner
如下:
@Entity
@Customizer(MyUniversalCustomizer.class)
public class PeriodOwner {
@Embedded @AttributeOverrides({...})
private Period period = new Period();
public Period getPeriod() {
return period;
}
}
请注意,除了@Customizer
注解,我们还初始化该字段period
with new Period()
因为否则新实体仍然为空period
fields.
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 的简单破解
而不是上述更改为Period
and PeriodOwner
,添加一个未使用的私有非空字段到Period
。例如:
@Formula("1")
private int workaroundForBraindeadJpaImplementation;
通过使用@Formula
(Hibernate 扩展)我们避免在数据库中为此字段添加额外的列。 Tomáš Záluský 描述了该解决方案here https://stackoverflow.com/questions/1324266/can-i-make-an-embedded-hibernate-entity-non-nullable。对于那些只想在某些情况下改变行为的人来说可能很有用。