我正在使用Spring Boot,Spring Data REST,Spring HATEOAS,Hibernate,Spring Data Envers。 我正在审核我的实体,以便跟踪所有更改。我正在从Angular 4应用程序中使用REST服务。 我的目标是为每个修订版(envers)显示一个框的lix,仅显示修改后的属性。
我的实体注释了@Audited
。这是我的模特:
@Audited
public class LicensePlate extends AbstractEntity {
private static final long serialVersionUID = -6871697166535810224L;
@NotEmpty
@ColumnTransformer(read = "UPPER(licensePlate)", write = "UPPER(?)")
@Column(nullable = false, unique = true)
private String licensePlate;
@NotNull
@Range(min = 1, max = 99)
@Column(nullable = false, columnDefinition = "INTEGER default 50")
private int seats = 50;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
private Country country;
正如你所看到的,Country是LAZY,我不能把它设置为EAGER。
我发现这个类以递归方式检查模型中的延迟属性并初始化它们。
public class HibernateUtil {
public static byte[] hibernateCollectionPackage = "org.hibernate.collection".getBytes();
public static void initializeObject(Object o, String insidePackageName) {
Set<Object> seenObjects = new HashSet<Object>();
initializeObject(o, seenObjects, insidePackageName.getBytes());
seenObjects = null;
}
private static void initializeObject(Object o, Set<Object> seenObjects, byte[] insidePackageName) {
seenObjects.add(o);
Method[] methods = o.getClass().getMethods();
for (Method method : methods) {
String methodName = method.getName();
// check Getters exclusively
if (methodName.length() < 3 || !"get".equals(methodName.substring(0, 3)))
continue;
// Getters without parameters
if (method.getParameterTypes().length > 0)
continue;
int modifiers = method.getModifiers();
// Getters that are public
if (!Modifier.isPublic(modifiers))
continue;
// but not static
if (Modifier.isStatic(modifiers))
continue;
try {
// Check result of the Getter
Object r = method.invoke(o);
if (r == null)
continue;
// prevent cycles
if (seenObjects.contains(r))
continue;
// ignore simple types, arrays und anonymous classes
if (!isIgnoredType(r.getClass()) && !r.getClass().isPrimitive() && !r.getClass().isArray()
&& !r.getClass().isAnonymousClass()) {
// ignore classes out of the given package and out of the
// hibernate collection
// package
if (!isClassInPackage(r.getClass(), insidePackageName) && !isClassInPackage(r.getClass(), hibernateCollectionPackage)) {
continue;
}
// initialize child object
Hibernate.initialize(r);
// traverse over the child object
initializeObject(r, seenObjects, insidePackageName);
}
} catch (InvocationTargetException e) {
e.printStackTrace();
return;
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
} catch (IllegalAccessException e) {
e.printStackTrace();
return;
}
}
}
private static final Set<Class<?>> IGNORED_TYPES = getIgnoredTypes();
private static boolean isIgnoredType(Class<?> clazz) {
return IGNORED_TYPES.contains(clazz);
}
private static Set<Class<?>> getIgnoredTypes() {
Set<Class<?>> ret = new HashSet<Class<?>>();
ret.add(Boolean.class);
ret.add(Character.class);
ret.add(Byte.class);
ret.add(Short.class);
ret.add(Integer.class);
ret.add(Long.class);
ret.add(Float.class);
ret.add(Double.class);
ret.add(Void.class);
ret.add(String.class);
ret.add(Class.class);
ret.add(Package.class);
return ret;
}
private static Boolean isClassInPackage(Class<?> clazz, byte[] insidePackageName) {
Package p = clazz.getPackage();
if (p == null)
return null;
byte[] packageName = p.getName().getBytes();
int lenP = packageName.length;
int lenI = insidePackageName.length;
if (lenP < lenI)
return false;
for (int i = 0; i < lenI; i++) {
if (packageName[i] != insidePackageName[i])
return false;
}
return true;
}
}
这是REST控制器:
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = "/licensePlates/{id}/revisions/{revid}", method = RequestMethod.GET)
public ResponseEntity<?> findRevision(@PathVariable(value = "id") Long id, @PathVariable(value = "revid") Integer revId,
Pageable pageable) {
if (id != null) {
List<RestChange> changes = new ArrayList<>();
AuditReader reader = AuditReaderFactory.get(entityManager);
AuditQuery q = reader.createQuery().forEntitiesAtRevision(LicensePlate.class, revId);
LicensePlate current = (LicensePlate) q.getSingleResult();
HibernateUtil.initializeObject(current, "it.model");
//PREVIOUS
if (revId > 1) {
q = reader.createQuery().forEntitiesAtRevision(LicensePlate.class, revId - 1);
LicensePlate previous = (LicensePlate) q.getSingleResult();
HibernateUtil.initializeObject(previous, "it.model");
Diff diff = javers.compare(previous, current);
for (Change change : diff.getChanges()) {
log.debug("Change: " + change);
if (change instanceof ValueChange) {
ValueChange c = (ValueChange) change;
RestChange restChange = new RestChange();
restChange.setPropertyName(c.getPropertyName());
restChange.setPreviousValue(c.getLeft());
restChange.setValue(c.getRight());
changes.add(restChange);
} else if (change instanceof ReferenceChange) {
ReferenceChange c = (ReferenceChange) change;
RestChange restChange = new RestChange();
restChange.setPropertyName(c.getPropertyName());
restChange.setPreviousValue(c.getLeftObject().get().toString());
restChange.setValue(c.getRightObject().get().toString());
changes.add(restChange);
}
}
}
return new ResponseEntity<>(new Resources<>(changes), HttpStatus.OK);
} else {
throw new ResourceNotFoundException();
}
}
我调试了,即使在调用Hibernate.initialize之后,country
和current
bean中的属性previous
仍然是代理。我找到解决问题的唯一方法是调用Hibernate.initialize(current.getCountry()),然后重新设置licensePlate
bean中的值,但我觉得这不对。
我用类似的问题检查了所有其他问题,并且在所有情况下Hibernate.initialize都是问题的解决方案,但是这里似乎不起作用。 我做错了什么?