Eclipse Texo ModelEMFConverter and Hibernate Proxies

I am trying to integrate Eclipse Texo into my existing Hibernate project. I simulated my domain model in ECore and created the EMF and POJO code there using Texo and EMF code generation.

Capturing objects (POJOs) stored in the database work without problems, now I would like to use Texo ModelEMFConverter to convert the data model with Hibernate mapping to the corresponding EMF model. However, this attempt fails because the objects returned by Hibernate are transparently proxied. Texo ModelResolver cannot find the model descriptor for these proxied objects, since it compares the class of the object (which is a proxy class) with the associated classes and does not work with an exception in my case:

An exception in the thread "main" java.lang.IllegalStateException: class class foobar.Entity _ $$ _ jvst4f2_5 is not controlled by this ModelResolver at org.eclipse.emf.texo.utils.Check.isNotNull (Check.java:66) in org. eclipse.emf.texo.model.ModelResolver.getModelDescriptor (ModelResolver.java{66) in org.eclipse.emf.texo.model.ModelResolver.getModelObject (ModelResolver.java:298) in org.eclipse.em. DefaultObjectResolver.toUri (DefaultObjectResolver.java:188) in org.eclipse.emf.texo.resolver.DefaultObjectResolver.resolveToEObject (DefaultObjectResolver.java:98) in org.eclipse.emf.texlem.converter.converter.converter.FC 146) in org.eclipse.emf.texo.converter.ModelEMFConverter.convertSingleEReference (ModelEMFConverter.java:265) in org.eclipse.emf.texo.converter.ModelEMFConverter.convertContent (ModelEMFConverter.java:189) in org.ecll .texo.converter.ModelEMFConverter.convert (ModelEMFConverter.java : 107) [...]

Corresponding code bits from ModelResolver :

  public ModelObject<?> getModelObject(final Object target) { /* ... snip ... */ final ModelDescriptor modelDescriptor = getModelDescriptor(target.getClass(), true); return modelDescriptor.createAdapter(target); } 

I tried to manually deploy proxied objects before passing them to the model converter using the following code:

  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 theory, this approach seems to work (I checked by passing through the conversion code once), however, it is not suitable for objects referenced by associations in my data model that are not in the original entities list. I would like you to not have to move the entire graph of the object manually to get rid of the proxy.

Is there a way to extract unrelated objects from Hibernate? Or can anyone have a suggestion as to how I could approach the transformation of this model from a different angle?

Thanks for your help in advance!

+5
source share
1 answer

You can write a generic substitute that traverses the entire graph and replaces all proxies for a given entity instance, something like this:

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

Do not forget to cancel the transaction in which this is done (Hibernate might think that the object is dirty, because we manually changed the field values). Or make it read-only (by setting the flash mode to manual). Or, explicitly clear the session without flushing it so that the freed graph is disconnected.

If this is an obstacle to your requirements, you can change this approach by reading the values ​​from the managed entity instance and setting the deproxed values ​​to another instance. Thus, you can create a new separate instance without managed entities, whose entire schedule is initialized without any proxy.

Or you can just save the information about the necessary changes and apply them later in the transaction of a single instance, for example:

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

All Articles