2

我有一个未知的 JPA 实体,需要知道它的版本。我找不到一般的方法来做到这一点。

我尝试了元模型,但不知道将什么传递给 getVersion() 方法:

Object entity = ...;
Metamodel metamodel = entityManager.getMetamodel();
IdentifiableType<?> metaClass = 
    (IdentifiableType<?>)metamodel.managedType(entity.getClass());
metaClass.getVersion(<what to put here?>);

中使用了相同的模式metaClass.getId(),但有一种补充方法getIdType()——getVersionType()缺失。

其次,有entityManagerFactory.getPersistenceUnitUtil().getIdentifier(entity)方法,但没有getVersion(entity)方法。

有没有办法从未知实体获取版本?

4

5 回答 5

2

比 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;
}
于 2014-07-07T17:01:28.383 回答
1

恕我直言,您应该将使用 @Version 注释注释的字段类型放在该类中。我认为这也适用于继承,开箱即用。

如果您事先不知道类型,我会使用标准反射。我将遍历所有声明的字段,并检查注释是否包含 @Version 注释。如果没有找到,检查 superclass,直到找到...

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;
}

当心虽然Class.getFields()似乎是一个更好的选择,因为它只返回公共字段,但它不起作用:

返回一个包含 Field 对象的数组,该数组反映了此 Class 对象表示的类或接口的所有可访问公共字段

编辑以上仅适用于在字段上严格使用@Version 注释的情况。然而,正如 Konstantin Pribluda 指出的那样,这个注释也可以放在方法上,这意味着两件事:

  • 方法也必须检查
  • 接口也必须检查,因为方法可以由接口定义......

这也意味着我们的简单循环必须转化为递归图遍历......

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;
}
于 2012-11-07T07:52:00.093 回答
1

我错过了什么吗?我将通过在公开版本字段的可版本化实体上实现一个接口来解决此问题。无需反思。

但这是假设您当然可以修改实体类。

于 2012-11-07T09:19:30.707 回答
1

如果您使用的是 EclipseLink,则可以使用以下方式获取版本,

Session session = em.unwrap(Session.class);
session.getDescriptor(Employee.class).getOptimisticLockingPolicy().getWriteLockValue(object, id, session);
于 2012-11-07T14:35:34.357 回答
1

我通过以下方式解决了它:javax.persistence.Version该类指定了支持的数据类型,仅支持七种,因此我一个接一个地尝试所有这些。JPA 规范显然错过了该getVersionType()方法,我认为这是一个错误。

/**
 * Finds the version attribute for ManagedType instance.
 * @param managedType The metaobject
 * @return The version attribute or null, if it does not have one
 * @throws UnsupportedOperationException
 * If it has a version attribute, but is not one of supported types, as specified in {@link Version} documentation
 * (currently int, Integer, short, Short, long, Long, Timestamp).
 */
private static <T> SingularAttribute<? super T, ?> findVersionAttribute(ManagedType<T> managedType) {
    if ( !(managedType instanceof IdentifiableType))
        return null;

    IdentifiableType<T> identifiableType = (IdentifiableType<T>)managedType;
    if ( ! identifiableType.hasVersionAttribute())
        return null;

    try {
        return identifiableType.getVersion(int.class);
    } catch (IllegalArgumentException e) {
        try {
            return identifiableType.getVersion(Integer.class);
        } catch (IllegalArgumentException e1) {
            try {
                return identifiableType.getVersion(long.class);
            } catch (IllegalArgumentException e2) {
                try {
                    return identifiableType.getVersion(Long.class);
                } catch (IllegalArgumentException e3) {
                    try {
                        return identifiableType.getVersion(short.class);
                    } catch (IllegalArgumentException e4) {
                        try {
                            return identifiableType.getVersion(Short.class);
                        } catch (IllegalArgumentException e5) {
                            try {
                                return identifiableType.getVersion(Timestamp.class);
                            } catch (IllegalArgumentException e6) {
                                throw new UnsupportedOperationException("The version attribute is not of supported type");
                            }
                        }
                    }
                }
            }
        }
    }
}
于 2012-11-07T15:24:21.880 回答