2015-07-10 3 views
5

Próbuję zintegrować Eclipse Texo z moim istniejącym projektem Hibernate. Modelowałem swój model domeny w ECore i generowałem z niego kod EMF i POJO, używając Texo i regularnego generowania kodu EMF.Eclipse Texo ModelEMFConverter i hibernacji proxy

Pobrane obiekty (POJO) przechowywane w bazie danych działają bezproblemowo, teraz chciałbym użyć Texo's ModelEMFConverter, aby przekształcić model danych odwzorowany Hibernacja w odpowiedni model EMF. Jednak próba ta nie powiedzie się, ponieważ elementy zwrócone przez hibernację są przezroczystym serwerem proxy. Texo na ModelResolver nie jest w stanie sprawdzić model deskryptora dla tych serwer proxy podmiotów, ponieważ porównuje klasy podmiotu (który jest klasa proxy) do odwzorowanych klas i kończy się niepowodzeniem z wyjątek w moim przypadku:

Exception w wątku "głównym" java.lang.IllegalStateException: Klasa klasy foobar.Entity _ $$ _ jvst4f2_5 nie jest zarządzany przez tego ModelResolver na org.eclipse.emf.texo.utils.Check.isNotNull (Check.java:66) pod adresem org.eclipse.emf.texo.model.ModelResolver.getModelDescriptor (ModelResolver.java:366) pod adresem org.eclipse.emf.texo.model.ModelResolver.getModelObject (ModelResol ver.java:298) w org.eclipse.emf.texo.resolver.DefaultObjectResolver.toUri (DefaultObjectResolver.java:188) w org.eclipse.emf.texo.resolver.DefaultObjectResolver.resolveToEObject (DefaultObjectResolver.java: 98) w org.eclipse.emf.texo.converter.ModelEMFConverter.createTarget (ModelEMFConverter.java:146) w org.eclipse.emf.texo.converter.ModelEMFConverter.convertSingleEReference (ModelEMFConverter.java:265) w org.eclipse.emf.texo.converter.ModelEMFConverter.convertContent (ModelEMFConverter.java:189) w org.eclipse.emf.texo.converter.ModelEMFConverter.convert (ModelEMFConverter.java:107) [...]

Odpowiednie bity kodu z ModelResolver:

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

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

Próbowałem ręcznie rozpakowaniu proxy podmioty przed przekazaniem ich do modelu konwertera za pomocą następującego kodu:

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

Teoretycznie to podejście wydaje się działać (sprawdziłem poprzez pojedynczy krok za pomocą kodu konwersji), ale nie działa w przypadku elementów przywoływanych przez asocjacje w moim modelu danych, które nie należą do zbioru ned na oryginalnej liście entities. Chciałbym uniknąć konieczności przechodzenia przez cały wykres obiektów ręcznie, aby pozbyć się proxy.

Czy istnieje sposób pobierania niepowiązanych elementów z trybu hibernacji? Czy może ktoś może mieć sugestię, w jaki sposób mogę podejść do tego modelu transformacji pod innym kątem?

Dzięki za pomoc z góry!

Odpowiedz

1

Można napisać rodzajowe zamiennik, który będzie przechodzić przez cały wykres i zastąpić wszystkie serwery proxy dla danego podmiotu, np coś takiego:

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

Nie zapomnij wycofać transakcję, w której to done (Hibernate może pomyśleć, że obiekt jest brudny, ponieważ ręcznie zmieniliśmy wartości pól). Lub uczynić go tylko do odczytu (ustawiając tryb płukania na ręczny). Lub wyraźnie wyczyść sesję bez jej przepłukiwania, aby odszyfrowany wykres został odłączony.

Jeśli przeszkadza to w spełnieniu wymagań, można zmienić to podejście, odczytując wartości z instancji zarządzanej jednostki i ustawiając wyzerowane wartości na inną instancję. W ten sposób możesz zbudować nową, oddzielną instancję nie zarządzaną, której cały wykres jest inicjowany bez żadnego proxy.

Albo można po prostu przechowywać informacje na temat koniecznych zmian i zastosować je później z transakcji na odłączonej przykład, na przykład:

commands.add(new ReplaceFieldCommand(field, entity, deproxiedObject)); 
commands.add(new ReplaceCollectionCommand(collection, entity, deproxiedCollection));