Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic way to get JPA entity version

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?

like image 380
Oliv Avatar asked Jan 19 '26 02:01

Oliv


2 Answers

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;
}
like image 169
wdrai Avatar answered Jan 21 '26 14:01

wdrai


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:

  • methods must be checked too
  • interfaces must be checked too, as methods can be defined by interfaces...

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;
}
like image 38
ppeterka Avatar answered Jan 21 '26 15:01

ppeterka



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!