Eclipse Texo ModelEMFConverter и Hibernate прокси

Я пытаюсь интегрировать Eclipse Texo в свой существующий проект Hibernate. Я смоделировал свою модель предметной области в ECore и сгенерировал оттуда код EMF и POJO, используя Texo и обычную генерацию кода EMF.

Извлечение сущностей (POJO), хранящихся в базе данных, работает без проблем, теперь я хотел бы использовать Texo ModelEMFConverter для преобразования модели данных, отображаемой в Hibernate, в соответствующую модель EMF. Однако эта попытка терпит неудачу из-за того, что объекты, возвращаемые Hibernate, прозрачно проксируются. ModelResolver Texo не может найти дескриптор модели для этих прокси-сущностей, поскольку он сравнивает класс сущности (который является прокси-классом) с сопоставленными классами и в моем случае терпит неудачу с исключением:

Исключение в потоке "main" java.lang.IllegalStateException: класс класса foobar.Entity_$$_jvst4f2_5 не управляется этим ModelResolver в org.eclipse.emf.texo.utils.Check.isNotNull(Check.java:66) в org .eclipse.emf.texo.model.ModelResolver.getModelDescriptor(ModelResolver.java:366) в org.eclipse.emf.texo.model.ModelResolver.getModelObject(ModelResolver.java:298) в org.eclipse.emf.texo.resolver .DefaultObjectResolver.toUri(DefaultObjectResolver.java:188) в org.eclipse.emf.texo.resolver.DefaultObjectResolver.resolveToEObject(DefaultObjectResolver.java:98) в org.eclipse.emf.texo.converter.ModelEMFConverter.createTarget(ModelEMFConverter.java :146) в org.eclipse.emf.texo.converter.ModelEMFConverter.convertSingleEReference(ModelEMFConverter.java:265) в org.eclipse.emf.texo.converter.ModelEMFConverter.convertContent(ModelEMFConverter.java:189) в org.eclipse. emf.texo.converter.ModelEMFConverter.convert(ModelEMFConverter.java:1 07) [...]

Соответствующие биты кода из ModelResolver:

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

    final ModelDescriptor modelDescriptor = getModelDescriptor(target.getClass(), true);
    return modelDescriptor.createAdapter(target);
  }

Я попытался вручную развернуть прокси-сущности, прежде чем передавать их преобразователю модели, используя следующий код:

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

Теоретически этот подход, кажется, работает (я проверял пошагово через код преобразования), однако он терпит неудачу для сущностей, на которые ссылаются ассоциации в моей модели данных, которые не содержатся в исходном списке entities. Я бы хотел избежать обхода всего графа объектов вручную, чтобы избавиться от прокси.

Есть ли способ получить непрокси-объекты из Hibernate? Или у кого-нибудь может быть предложение относительно того, как я мог бы подойти к этому преобразованию модели под другим углом?

Заранее спасибо за помощь!


person AdrianoKF    schedule 10.07.2015    source источник


Ответы (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;
    }
}

Не забудьте откатить транзакцию, в которой это делается (Hibernate может подумать, что объект грязный, потому что мы вручную изменили значения полей). Или сделайте его доступным только для чтения (установив ручной режим очистки). Или явно очистить сеанс, не сбрасывая его, чтобы депроксированный граф стал отсоединенным.

Если это является препятствием для ваших требований, вы можете изменить этот подход, прочитав значения из экземпляра управляемого объекта и установив депроксированные значения для другого экземпляра. Таким образом, вы можете создать новый отдельный экземпляр неуправляемой сущности, весь граф которого инициализируется без какого-либо прокси.

Либо можно просто сохранить информацию о необходимых изменениях и применить их позже вне транзакции на отсоединенном инстансе, например:

commands.add(new ReplaceFieldCommand(field, entity, deproxiedObject));
commands.add(new ReplaceCollectionCommand(collection, entity, deproxiedCollection));
person Dragan Bozanovic    schedule 13.07.2015