Eclipse Texo ModelEMFConverter和Hibernate代理

Adr*_*oKF 5 java hibernate emf

我正在尝试将Eclipse Texo集成到我现有的Hibernate项目中.我已经在ECore中建模了我的域模型,并使用Texo和常规的EMF代码生成从那里生成了EMF和POJO代码.

存储在数据库中的获取实体(POJO)可以正常工作,现在我想使用Texo ModelEMFConverter将Hibernate映射的数据模型转换为相应的EMF模型.但是,由于Hibernate返回的实体被透明代理,此尝试失败.Texo ModelResolver无法为这些代理实体查找模型描述符,因为它将实体的类(它是代理类)与映射的类进行比较,并在我的情况下失败并出现异常:

线程"main"中的异常java.lang.IllegalStateException:类org.eclipse.emf.texo.utils.Check.isNotNull(Check.java:66)中的ModelResolver不管理类class foobar.Entity _ $$ _ jvst4f2_5位于org.eclipse.emf.texo.resolver的org.eclipse.emf.texo.model.ModelResolver.getModelObject(ModelResolver.java:298)的.eclipse.emf.texo.model.ModelResolver.getModelDescriptor(ModelResolver.java:366) .defaultObjectResolver.toUri(DefaultObjectResolver.java:188)org.eclipse.emf.texo.resolver.DefaultObjectResolver.resolveToEObject(DefaultObjectResolver.java:98)at org.eclipse.emf.texo.converter.ModelEMFConverter.createTarget(ModelEMFConverter.java) :146)org.eclipse上的org.eclipse.emf.texo.converter.ModelEMFConverter.convertSingleEReference(ModelEMFConverter.java:265)org.eclipse.emf.texo.converter.ModelEMFConverter.convertContent(ModelEMFConverter.java:189). emf.texo.converter.ModelEMFConverter.convert(ModelEMFConverter.java:107)[...]

相关代码位来自ModelResolver:

  public ModelObject<?> getModelObject(final Object target) {
    /* ... snip ... */

    final ModelDescriptor modelDescriptor = getModelDescriptor(target.getClass(), true);
    return modelDescriptor.createAdapter(target);
  }
Run Code Online (Sandbox Code Playgroud)

我尝试手动展开代理实体,然后使用以下代码将它们传递给模型转换器:

    final List<Object> objects = entities
            .stream()
            .map(o ->
                o instanceof HibernateProxy ?
                    (Entity) ((HibernateProxy) o).getHibernateLazyInitializer().getImplementation() : o)
            .collect(Collectors.toList());

    final ModelEMFConverter converter = new ModelEMFConverter();
    final Collection<EObject> eObjects = converter.convert(objects);
Run Code Online (Sandbox Code Playgroud)

从理论上讲,这种方法似乎有效(我通过单步执行转换代码进行检查),但是对于我的数据模型中的关联引用的实体来说,它没有包含在原始entities列表中.我想避免必须手动遍历整个对象图以摆脱代理.

有没有办法从Hibernate中检索未经过代理的实体?或者,是否有人可能会建议如何从不同的角度进行此模型转换?

感谢您的帮助!

Dra*_*vic 1

您可以编写一个通用替换器,它将遍历整个图并替换给定实体实例的所有代理,如下所示:

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;

public class HibernateProxyReplacer {

    @SuppressWarnings("unchecked")
    public <T extends Entity> T replaceProxies(T entity) {
        try {
            return (T) replaceProxies(entity, new ArrayList<Object>());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    private Object replaceProxies(Object entity, List<Object> processedObjects) throws Exception {
        entity = getImplementation(entity);
        if (isProcessed(entity, processedObjects)) {
            return entity;
        }
        processedObjects.add(entity);

        for (Field field : getDeclaredFields(entity)) {
            if (isStaticOrFinal(field)) {
                continue;
            }
            field.setAccessible(true);
            Object value = field.get(entity);
            if (value == null) {
                continue;
            }
            Hibernate.initialize(value);
            if (value instanceof Collection) {
                replaceProxiesInCollection((Collection<Object>) value, processedObjects);
            } else if (value instanceof Entity) {
                field.set(entity, replaceProxies(value, processedObjects));
            }
        }

        return entity;
    }

    private Object getImplementation(Object object) {
        return object instanceof HibernateProxy ? ((HibernateProxy) object).getHibernateLazyInitializer().getImplementation() : object;
    }

    private boolean isStaticOrFinal(Field field) {
        return ((Modifier.STATIC | Modifier.FINAL) & field.getModifiers()) != 0;
    }

    private List<Field> getDeclaredFields(Object object) {
        List<Field> result = new ArrayList<Field>(Arrays.asList(object.getClass().getDeclaredFields()));
        for (Class<?> superclass = object.getClass().getSuperclass(); superclass != null; superclass = superclass.getSuperclass()) {
            result.addAll(Arrays.asList(superclass.getDeclaredFields()));
        }
        return result;
    }

    private void replaceProxiesInCollection(Collection<Object> collection, List<Object> processedObjects) throws Exception {
        Collection<Object> deproxiedCollection = new ArrayList<Object>();
        for (Object object : collection) {
            deproxiedCollection.add(replaceProxies(object, processedObjects));
        }
        collection.clear();
        collection.addAll(deproxiedCollection);
    }

    private boolean isProcessed(Object object, List<Object> processedObjects) {
        for (Object processedObject : processedObjects) {
            // Intentional comparison by reference to avoid relying on equals/hashCode
            if (processedObject == object) {
                return true;
            }
        }
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

不要忘记回滚完成此操作的事务(Hibernate 可能认为该对象是脏的,因为我们手动更改了字段值)。或者将其设置为只读(通过将刷新模式设置为手动)。或者显式清除会话而不刷新它,以便去代理图变得分离。

如果这对您的要求构成障碍,那么您可以通过从托管实体实例读取值并将去代理值设置到另一个实例来更改此方法。通过这种方式,您可以构建一个新的单独的非托管实体实例,其整个图在没有任何代理的情况下进行初始化。

或者,您可以只存储有关必要更改的信息,并稍后在分离实例上的事务中应用它们,例如:

commands.add(new ReplaceFieldCommand(field, entity, deproxiedObject));
commands.add(new ReplaceCollectionCommand(collection, entity, deproxiedCollection));
Run Code Online (Sandbox Code Playgroud)