如果Person
没有Address
的问题被保留并稍后加载,则Person
包含Address
,所有字段都设置为null
。
这个(修改过的)示例来自coderanch,其中报告了类似的问题。
@Embeddable
public class Address {
private String street;
private String postalCode;
}
@Entity
public class Person {
private String name;
@Embedded
private Address home;
}
我该如何规避这个问题?如果所有字段都是@Embedded
,是否可以指示Hibernate不实例化null
对象?
更改每个@Embedded
字段的getter似乎很麻烦且容易出错。另一个繁琐的替代方法是@PostLoad
的使用,但仅针对@Entity
调用,而不是@Embeddable
s。
答案 0 :(得分:0)
@PostLoad
添加到@Entity
s @Embeddable
s:
public class NullableEmbeddedCleanerImpl implements NullableEmbeddedCleaner {
private final static Logger LOGGER = LoggerFactory.getLogger(NullableEmbeddedCleaner.class);
Map<Class<?>, Predicate<Object>> classFilterForNullables = new HashMap<>();
Map<Class<?>, Predicate<Object>> collectionMap = new HashMap<>();
Set<Class<?>> primitiveArrayClasses = new HashSet<Class<?>>();
public NullableEmbeddedCleanerImpl() {
fillPredicates();
fillCollectionMap();
fillPrimitiveArrayClasses();
}
/**
* B C D F I J S Z
*/
private void fillPrimitiveArrayClasses() {
try {
primitiveArrayClasses.addAll(Arrays.asList(Class.forName("[B"), Class.forName("[B"), Class.forName("[C"),
Class.forName("[D"), Class.forName("[F"), Class.forName("[I"), Class.forName("[J"), Class.forName("[S"),
Class.forName("[Z")));
} catch (ClassNotFoundException e) {
LOGGER.error("Class not found", e);
}
}
@SuppressWarnings("unchecked")
private void fillCollectionMap() { // misses Lists, Maps, ...
collection(Set.class, s -> {
final Set<Object> toRemove = new HashSet<>();
for (Object i : s) {
try {
if (clean(i)) {
toRemove.add(i);
}
} catch (Exception e) {
LOGGER.warn("Error cleaning embeddable {} : {}", e, i);
}
}
s.removeAll(toRemove);
return s.isEmpty();
});
}
@Override
public final void embeddables(Object... arg) {
for (Object i : arg) {
try {
clean(i);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOGGER.warn("Error cleaning embeddable {} : {}", e, i);
}
}
}
@Override
public final boolean clean(Object arg) throws IllegalArgumentException, IllegalAccessException {
if (arg == null) {
return true;
}
boolean cleanThis = true;
Vector<Field> fields = new Vector<>();
for (Class<?> clazz = arg.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
}
for (Field field : fields) {
if (!fieldLoop(field, arg)) {
cleanThis = false;
}
}
return cleanThis;
}
private boolean fieldLoop(Field field, Object arg) throws IllegalArgumentException, IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) { // skip static fields
return true;
}
field.setAccessible(true);
Object fieldValue = field.get(arg);
if (fieldValue == null) {
return true;
}
Class<?> fieldsClass = field.getType();
if (fieldsClass.isPrimitive() || fieldsClass.isEnum()) {
return false; // can not clean primitives nor enums
}
if (fieldsClass.isArray()) {
if (primitiveArrayClasses.contains(fieldsClass)) {
return false; // ignore primitive arrays
} else {
throw new UnsupportedOperationException("Do something useful here"); // object
// arrays
}
}
for (Class<?> clazz : collectionMap.keySet()) {
if (clazz.isAssignableFrom(fieldsClass)) {
boolean emptyCollection = collectionMap.get(clazz).test(fieldValue);
if (!emptyCollection) {
return false;
} else {
field.set(arg, null);
}
return true;
}
}
// test primitives. just classes, no interfaces >>
for (Class<?> fieldClass = fieldsClass; fieldClass != null; fieldClass = fieldClass.getSuperclass()) {
if (classFilterForNullables.containsKey(fieldClass)) {
Predicate<Object> handle = classFilterForNullables.get(fieldClass);
if (handle.test(fieldValue)) {
return true;
} else {
return false;
}
}
}
if (clean(field.get(arg))) { // decent to contained objects
field.set(arg, null);
} else {// non clean-able child exists
return false;
}
return true;
}
private void fillPredicates() {
nullableFilters(String.class, Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class,
Float.class, Double.class, Void.class, LocalDateTime.class, LocalDate.class, LocalTime.class,
OffsetDateTime.class, OffsetTime.class);
classFilterForNullables.put(NullableEmbeddedCleanerImpl.class, n -> true); // always
// filter
}
private void nullableFilters(Class<?>... cs) {
for (Class<?> c : cs) {
classFilterForNullables.put(c, o -> false);
}
}
@SuppressWarnings("unchecked")
final private <T> void collection(Class<T> c, Predicate<T> fun) {
collectionMap.put((Class<?>) c, ((Predicate<Object>) fun));
}
}