2015-07-10 33 views
5

我試圖將Eclipse Texo集成到我現有的Hibernate項目中。我在ECore中建模了我的領域模型,並使用Texo和常規的EMF代碼生成從那裏生成了EMF和POJO代碼。Eclipse Texo ModelEMFConverter和Hibernate代理

存儲在數據庫中的抓取實體(POJO)沒有問題,現在我想用Texo的ModelEMFConverter將Hibernate映射的數據模型轉換爲相應的EMF模型。但是,由於Hibernate返回的實體被透明地代理,所以此嘗試失敗。 TEXO的ModelResolver無法查找一個模型描述了這些代理機構,因爲它的類的實體(這是代理類)進行比較,以映射類和失敗,在我的情況例外:

異常在線程「主」java.lang.IllegalStateException:類 類 foobar.Entity _ $$ _ jvst4f2_5 不受此ModelResolver管理在 org.eclipse.emf.texo.utils.Check.isNotNull(Check.java:66 )at org.eclipse.emf.texo.model.ModelResolver.getModelDescriptor(ModelResolver.java:366) at org.eclipse.emf.texo.model.ModelResolver.getModelObject(ModelResol ver.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:107) [...]

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

理論上這種方法似乎可行(我通過單步執行轉換代碼檢查),但是在我的數據模型中由關聯引用的實體失敗, ned在原來的entities列表中。我想避免必須手動遍歷整個對象圖,以擺脫代理。

有沒有辦法從Hibernate檢索未加載的實體?或者,有沒有人可能會對我如何從不同角度進行模型轉換提出建議?

感謝您的幫助提前!

回答

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可能認爲對象是髒的,因爲我們手動更改了字段值)。或者使其成爲只讀(通過將刷新模式設置爲手動)。或者在不清除會話的情況下明確清除會話,以便將deproxied圖表分離。

如果這是您的需求的障礙,那麼您可以通過讀取來自管理實體實例的值並將deproxied值設置爲另一個實例來更改此方法。通過這種方式,您可以構建一個新的單獨的非託管實體實例,其整個圖形在沒有任何代理的情況下被初始化

或者,你可以存儲大約需要變化的信息,後來申請出來的交易對脫管的實例,例如:

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