transaction manager example enabletransactionmanagement java hibernate spring dozer spring-transactions

java - manager - Evita que Dozer active la carga perezosa de Hibernate



spring jpa commit (6)

Estoy utilizando transacciones Spring, por lo que la transacción aún está activa cuando se produce la conversión de POJO a DTO.

Me gustaría evitar que Dozer active la carga diferida, para que las consultas de SQL ocultas nunca ocurran: todas las capturas deben realizarse de forma explícita a través de HQL (para obtener el mejor control sobre el rendimiento).

  1. ¿Es una buena práctica (no puedo encontrarla documentada en ninguna parte)?

  2. ¿Cómo hacerlo de forma segura?

Intenté esto 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 se cierra y la carga perezosa todavía ocurre.

Intenté esto:

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

Y evita la carga lenta, pero ¿es una buena práctica manipular la sesión directamente en la capa de aplicación (que se denomina "fachada" en mi proyecto)? ¿Qué efectos secundarios negativos debo temer? (Ya he visto que las pruebas que involucran conversiones de POJO -> DTO ya no pueden iniciarse a través de las clases de prueba AbstractTransactionnalDatasource Spring, porque estas clases intentan desencadenar una reversión en una transacción que ya no está vinculada a una sesión activa).

También he intentado establecer la propagación en NOT_SUPPORTED o REQUIRES_NEW, pero reutiliza la sesión actual de Hibernate, y no evita la carga lenta.


¿Has considerado desactivar la carga perezosa por completo?

Realmente no parece coincidir con los patrones que indicas que te gustaría usar:

Me gustaría evitar que Dozer active la carga diferida, para que las consultas de SQL ocultas nunca ocurran: todas las capturas deben realizarse de forma explícita a través de HQL (para obtener el mejor control sobre el rendimiento).

Esto sugiere que nunca querrá utilizar la carga perezosa.

Dozer y los frijoles con respaldo de Hibernate que le pasas son felizmente ignorantes el uno del otro; Todo lo que Dozer sabe es que está accediendo a las propiedades en el bean, y el bean respaldado por Hibernate está respondiendo a las llamadas para get() una colección de carga lenta como lo haría si estuviera accediendo a esas propiedades usted mismo.

Cualquier truco para que Dozer sea consciente de los proxies de Hibernate en sus beans o viceversa, IMO, rompería las capas de su aplicación.

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


El uso de CustomFieldMapper puede no ser una buena idea, ya que se invocará para cada campo de su clase fuente, pero nuestra preocupación es solo la asociación de asociación perezosa (lista de objetos secundarios), por lo que podemos establecer el valor nulo en la obtención del objeto de la entidad.

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


La única solución genérica que he encontrado para gestionar esto (después de analizar convertidores personalizados, escuchas de eventos y resoluciones de proxy) es mediante la implementación de un asignador de campos 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á cualquier objeto PersistentSet no inicializado como nulo. Hago esto para que cuando se pasan al cliente puedo 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 hacer otra llamada de servicio para recuperar el conjunto (si es necesario). Además, si decide cargar con entusiasmo cualquier colección dentro de la capa de servicio, se asignarán como de costumbre.

Yo inyecto el mapeador de campo personalizado usando spring:

<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 cualquiera que busque una solución para esto, ya que no pude encontrar ningún ejemplo al buscar en Internet.


La versión corta de este mapper será

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


No conseguí que lo anterior funcionara (probablemente versiones diferentes). 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)); } }


Una variación en la versión popular de arriba, se asegura de capturar tanto las bolsas persistentes, los conjuntos persistentes, como que la nombras ...

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

}