I have an unknown JPA entity and need to know it's version. I could not find a way to do this generically.
I tried the metamodel, but don't know what to pass to getVersion() method:
Object entity = ...;
Metamodel metamodel = entityManager.getMetamodel();
IdentifiableType<?> metaClass =
(IdentifiableType<?>)metamodel.managedType(entity.getClass());
metaClass.getVersion(<what to put here?>);
Same pattern is used in metaClass.getId(), but there is a complementary method getIdType() -- getVersionType() is missing.
Secondly, there is entityManagerFactory.getPersistenceUnitUtil().getIdentifier(entity) method, but not getVersion(entity) method.
Is there some way to get version from an unknown entity?
Just a bit cleaner than the answer with try/catch:
public static SingularAttribute<?, ?> getVersionAttribute(ManagedType<?> managedType) {
if (!(managedType instanceof IdentifiableType<?>))
return null;
IdentifiableType<?> identifiableType = (IdentifiableType<?>)managedType;
if (!identifiableType.hasVersionAttribute())
return null;
for (SingularAttribute<?, ?> attribute : identifiableType.getSingularAttributes()) {
if (attribute.isVersion())
return attribute;
}
return null;
}
IMHO, you should put the type of the field annotated with the @Version annotation in that class. I think this will work with inheritance too, out of the box.
If you don't know the type beforehand, I'd go with standard reflection. I'd iterate through all the declared fields, and check annotations if they contain the @Version annotation. If not found, check superclass, until found...
public static Field getVersionField(Object entity) {
Class<?> myClass = entity.getClass();
do {
Field[] fields = myClass.getDeclaredFields();
for(Field field:fields) {
if(field.getAnnotation(Version.class)!=null) {
return field;
}
}
} while((myClass=myClass.getSuperclass())!=null);
return null;
}
Beware While the Class.getFields() seems to be a nicer alternative, as it only returns the public fields, it would not work:
Returns an array containing Field objects reflecting all the accessible public fields of the class or interface represented by this Class object.
EDIT The above works only in a case, when the @Version annotation is strictly used on Fields. However, as Konstantin Pribluda pointed out, this annotation can be put on methods too, which means two things:
This also means that our simple cycle must be transformed into a recursive graph traversal...
public static Method getVersionMethod(Class<?> myClass) {
Method[] methods = myClass.getDeclaredMethods();
for(Method method : methods) {
if(method.getAnnotation(Version.class)!=null) {
return method;
}
}
if(myClass.getSuperclass()!=null) {
Class<?> returned = getVersionMethod(myClass.getSuperclass());
if(returned!=null) {
return returned;
}
}
for(Class<?> interfaceToCheck : myClass.getInterfaces()) {
Class<?> returned = getVersionMethod(interfaceToCheck);
if(returned!=null) {
return returned;
}
}
return null;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With