2015-07-10 5 views
5

Ich versuche, Eclipse Texo in mein bestehendes Hibernate-Projekt zu integrieren. Ich habe mein Domain-Modell in ECore modelliert und von dort sowohl EMF- als auch POJO-Code mit Texo und der regulären EMF-Code-Generierung generiert.Eclipse Texo ModelEMFConverter und Hibernate-Proxies

Das Holen von Entitäten (POJOs), die in der Datenbank gespeichert sind, funktioniert ohne Probleme, jetzt möchte ich Texo's ModelEMFConverter verwenden, um das Hibernate-gemappte Datenmodell in das entsprechende EMF-Modell zu transformieren. Dieser Versuch schlägt jedoch fehl, da die von Hibernate zurückgegebenen Entitäten transparent proxiiert werden. Texo des ModelResolver ist nicht in der Lage, ein Modell-Descriptor für diese proxied Entitäten zu suchen, da sie die Klasse der Einheit vergleicht (die Proxy-Klasse ist) zu den abgebildeten Klassen und schlägt mit einer Ausnahme in meinem Fall:

Exception in thread "main" java.lang.IllegalStateException: Die Klasse Klasse foobar.Entity _ $$ _ jvst4f2_5 wird nicht verwaltet dieses ModelResolver bei org.eclipse.emf.texo.utils.Check.isNotNull (Check.java:66) um org.eclipse.emf.texo.model.ModelResolver.getModelDescriptor (ModelResolver.java:366) um org.eclipse.emf.texo.model.ModelResolver.getModelObject (ModelResol ver.java:298) bei org.eclipse.emf.texo.resolver.DefaultObjectResolver.toUri (DefaultObjectResolver.java:188) bei org.eclipse.emf.texo.resolver.DefaultObjectResolver.resolveToEObject (DefaultObjectResolver.java: 98) bei org.eclipse.emf.texo.converter.ModelEMFConverter.createTarget (ModelEMFConverter.java:146) bei org.eclipse.emf.texo.converter.ModelEMFConverter.convertSingleEReference (ModelEMFConverter.java:265) bei org.eclipse.emf.texo.converter.ModelEMFConverter.convertContent (ModelEMFConverter.java:189) bei org.eclipse.emf.texo.converter.ModelEMFConverter.convert (ModelEMFConverter.java:107) [...]

Die entsprechenden Codebits aus ModelResolver:

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

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

I versucht, die Einheiten manuell proxied Abwickeln bevor sie zu dem Modell-Wandler mit dem folgenden Code übergeben wird:

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

In der Theorie scheint dieser Ansatz zu funktionieren (ich überprüft durch Single-Stepping durch den Conversion-Code), aber es scheitert für Entitäten, die durch Assoziationen in meinem Datenmodell referenziert werden, die nicht contai sind ned in der ursprünglichen entities Liste. Ich möchte vermeiden, dass ich den gesamten Objektgraphen von Hand durchqueren muss, um die Proxies loszuwerden.

Gibt es eine Möglichkeit, unberechtigte Entitäten aus Hibernate abzurufen? Oder hat jemand vielleicht einen Vorschlag, wie ich diese Modelltransformation aus einem anderen Blickwinkel betrachten könnte?

Vielen Dank für Ihre Hilfe im Voraus!

Antwort

1

Sie können eine generische replacer schreiben, die den gesamten Graphen durchlaufen wird, und ersetzen Sie alle Proxy-Server für die gegebene Entitätsinstanz, etwa so:

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

Vergessen Sie nicht, die Transaktion rückgängig zu machen, in denen dies done (Hibernate könnte denken, dass das Objekt schmutzig ist, weil wir die Feldwerte manuell geändert haben). Oder machen Sie es schreibgeschützt (indem Sie den Spülmodus auf "Manuell" setzen). Oder löschen Sie die Sitzung explizit, ohne sie zu löschen, damit der entgaste Graph verschwindet.

Wenn dies ein Hindernis für Ihre Anforderungen darstellt, können Sie diesen Ansatz ändern, indem Sie die Werte aus der verwalteten Entitätsinstanz lesen und die deproxierten Werte auf eine andere Instanz festlegen. Auf diese Weise können Sie eine neue separate nicht verwaltete Entitätsinstanz erstellen, deren gesamter Graph ohne Proxy initialisiert wird.

Oder können Sie speichern nur die Informationen über die notwendigen Änderungen und wendet sie später aus der Transaktion auf der freistehende Instanz, zum Beispiel:

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