2011-04-05 27 views
15

Estoy usando transacciones de Spring por lo que la transacción aún está activa cuando ocurre la conversión de POJO a DTO.Evitar que Dozer active la carga diferida de Hibernate

Me gustaría evitar que Dozer active la carga diferida, de modo que las consultas sql ocultas nunca ocurran: todas las búsquedas deben hacerse explícitamente a través de HQL (para obtener el mejor control de las interpretaciones).

  1. ¿Es una buena práctica (no la puedo encontrar documentada en ningún lado)?

  2. ¿Cómo hacerlo con seguridad?

yo probamos este antes de la conversión DTO:

PlatformTransactionManager tm = (PlatformTransactionManager) SingletonFactoryProvider.getSingletonFactory().getSingleton("transactionManager"); 
tm.commit(tm.getTransaction(new DefaultTransactionDefinition())); 

no sé qué pasa con la transacción, pero la sesión de Hibernate no quede cerrado y la carga lenta persiste.

yo probamos este:

SessionFactory sf = (SessionFactory) SingletonFactoryProvider.getSingletonFactory().getSingleton("sessionFactory"); 
sf.getCurrentSession().clear(); 
sf.getCurrentSession().close(); 

y evita que la carga diferida, pero es una buena práctica para manipular la sesión directamente en la capa de aplicación (que se llama "fachada" en mi proyecto)? ¿Qué efectos secundarios negativos debería temer? (Ya he visto que las pruebas que implican conversiones de POJO -> DTO no podrían lanzarse más a través de las clases de prueba Spring de AbstractTransactionnalDatasource, porque estas clases intentan desencadenar una reversión en una transacción que ya no está vinculada a una sesión activa).

También intenté establecer la propagación en NOT_SUPPORTED o REQUIRES_NEW, pero reutiliza la sesión actual de Hibernate y no impide la carga diferida.

Respuesta

24

La única solución genérica que he encontrado para gestionar esto (después de buscar en Convertidores personalizados, Oyentes de eventos & Resoluciones de proxy) es mediante la implementación de un asignador de campo personalizado. Encontré esta funcionalidad escondida en la API de Dozer (no creo que esté documentada en la Guía del usuario).

Un ejemplo simple es el siguiente;

public class MyCustomFieldMapper implements CustomFieldMapper 
{ 
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) 
    {  
     // Check if field is a Hibernate collection proxy 
     if (!(sourceFieldValue instanceof AbstractPersistentCollection)) { 
      // Allow dozer to map as normal 
      return false; 
     } 

     // Check if field is already initialized 
     if (((PersistentSet) sourceFieldValue).wasInitialized()) { 
      // Allow dozer to map as normal 
      return false; 
     } 

     // Set destination to null, and tell dozer that the field is mapped 
     destination = null; 
     return true; 
    } 
} 

Esto devolverá ningún objeto no inicializado PersistentSet como nulo. Hago esto para que cuando se pasen al cliente pueda diferenciar entre una colección NULL (no cargada) y una colección vacía. Esto me permite definir el comportamiento genérico en el cliente para usar el conjunto precargado o realizar otra llamada de servicio para recuperar el conjunto (si es necesario). Además, si decide cargar ansiosamente cualquier colección dentro de la capa de servicio, entonces se asignarán como de costumbre.

me inyecto el asignador de campo personalizado utilizando la primavera:

<bean id="dozerMapper" class="org.dozer.DozerBeanMapper" lazy-init="false"> 
    <property name="mappingFiles"> 
     ... 
    </property> 
    <property name="customFieldMapper" ref="dozerCustomFieldMapper" /> 
</bean> 
<bean id="dozerCustomFieldMapper" class="my.project.MyCustomFieldMapper" /> 

espero que esto ayude a la búsqueda de una solución para esto, ya que no pude encontrar ningún ejemplo al buscar en Internet.

+0

Gracias, esto es genial, incluso puedo confirmar que no está documentado en otro lugar que no sea aquí: http://www.google.fr/search?q=CustomFieldMapper+PersistentSet – Tristan

+1

Además, en una versión reciente de Dozer (5.3.0), hay otra forma de hacerlo (http://sourceforge.net/tracker/?func=detail&aid=2993122&group_id=133517&atid=727370) – Tristan

+0

La línea 'destination = null' es una operación no operativa, no estoy seguro de por qué esta ahí... –

3

¿Ha considerado desactivar la carga diferida por completo?

No parece muy jive con los patrones que estado en el que desea utilizar:

me gustaría evitar la activación del dormilón de carga lenta, por lo que las consultas SQL ocultos no se dan nunca: todo ir a buscar tiene que hacerse explícitamente a través de HQL (para obtener el mejor control de las actuaciones).

Esto sugiere que nunca querrá utilizar la carga diferida.

Dozer y los granos con respaldo de hibernación que pasas a él son felizmente ignorantes el uno del otro; todo lo que sabe Dozer es que está accediendo a las propiedades en el bean, y el bean respaldado por Hibernate está respondiendo a las llamadas al get(), una colección con carga diferida tal como lo haría si estuviera accediendo a esas propiedades usted mismo.

Cualquier truco para hacer que Dozer tenga en cuenta los proxies de Hibernate en sus beans o viceversa, IMO, descompondrá las capas de su aplicación.

Si no desea que se activen "consultas SQL ocultas" en momentos inesperados, simplemente desactive la carga diferida.

+0

Por supuesto, me gustaría para "desactivar la carga lenta" si es posible, pero ¿cómo hacerlo? Quiero decir "default-lazy = false" significa que todas mis asociaciones serán buscadas ansiosamente, ¿no? – Tristan

+0

Sí, o puede especificar 'lazy =" false "' en la clase o propiedad, y sí, esto dará como resultado una búsqueda con ganas. Tienes que tener una búsqueda ansiosa o una carga lenta; no puede asignar una propiedad/colección con Hibernate y Hibernate debe cargarla solo algunas veces. –

+0

Ok, lo que me gustaría es: Hibernate nunca carga mis colecciones automáticamente cuando se llama a un getter. Hibernate carga mis colecciones solo cuando lo especifico con una "combinación" explícita en una consulta HQL (por ejemplo, "de órdenes de búsqueda de unión de persona") ¿puede confirmar que este comportamiento no es posible con Hibernate? (y ¿hay algún tipo de razón ... u otros marcos populares que se ajusten a este comportamiento?) – Tristan

5

No conseguí lo anterior para trabajar (probablemente diferentes versiones). Sin embargo, esto funciona bien

public class HibernateInitializedFieldMapper implements CustomFieldMapper { 
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) { 
     //if field is initialized, Dozer will continue mapping 
     return !Hibernate.isInitialized(sourceFieldValue)); 
    } 
} 
9

Una variante de la popular versión anterior, se asegura que coger dos PersistentBags, PersistentSets, lo que sea ...

public class LazyLoadSensitiveMapper implements CustomFieldMapper { 

public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) { 
    //if field is initialized, Dozer will continue mapping 

    // Check if field is derived from Persistent Collection 
    if (!(sourceFieldValue instanceof AbstractPersistentCollection)) { 
     // Allow dozer to map as normal 
     return false; 
    } 

    // Check if field is already initialized 
    if (((AbstractPersistentCollection) sourceFieldValue).wasInitialized()) { 
     // Allow dozer to map as normal 
     return false; 
    } 

    return true; 
} 

}

0

Versión corta de esta mapper habrá

return sourceFieldValue instanceof AbstractPersistentCollection && 
!((AbstractPersistentCollection) sourceFieldValue).wasInitialized(); 
+1

No estoy seguro de cómo volver a factorizar el código para que sea menos legible realmente agrega valor a esta respuesta. – JamieB

0

Uso CustomFieldMapper puede no ser una buena idea, ya que g onna invocar para todos los campos de su clase de origen, pero nuestra preocupación es única asociación de mapas perezoso (lista de objetos hijo), por lo que puede establecer el valor nulo en getter del objeto de entidad,

public Set<childObject> getChild() { 
if(Hibernate.isInitialized(child){ 
    return childObject; 
}else 
return null; 
} 
+0

Sin embargo, esto efectivamente está deshabilitando la carga diferida en todas las colecciones aplicables, no solo para la asignación de VO. Si tuviera alguna lógica interna de la aplicación que requiriera la carga diferida de esta colección, simplemente obtendría nulo. Esta pregunta es específicamente sobre la desactivación de la carga diferida para el mapeo de VO, no del todo. – JamieB

Cuestiones relacionadas