Tiendo a usar Hibernate en combinación con el marco Spring y sus capacidades de demarcación de transacciones declarativas (por ejemplo, @Transactional ).
Como todos sabemos, hibernate intenta ser lo más no invasivo y transparente posible, sin embargo, esto resulta un poco más desafiante cuando se emplean lazy-loaded
relaciones.
Veo una serie de alternativas de diseño con diferentes niveles de transparencia.
- Haga relaciones no cargadas de pereza (p. Ej.,
fetchType=FetchType.EAGER)
- Esto viola toda la idea de carga diferida.
- Inicializar colecciones usando
Hibernate.initialize(proxyObj);
- Esto implica un acoplamiento relativamente alto al DAO
- Aunque podemos definir una interfaz con
initialize
, no se garantiza que otras implementaciones proporcionen un equivalente.
- Agregue el comportamiento de la transacción a los
Model
propios objetos persistentes (utilizando un proxy dinámico o@Transactional
)- No probé el enfoque de proxy dinámico, aunque nunca pareció que @Transactional funcionara en los objetos persistentes. Probablemente debido a que la hibernación es la operación en un proxy con el que estar.
- Pérdida de control cuando las transacciones realmente se están llevando a cabo.
- Proporcionar API perezosa / no perezosa, por ejemplo,
loadData()
yloadDataWithDeps()
- Obliga a la aplicación a saber cuándo emplear qué rutina, de nuevo acoplamiento estrecho
- Desbordamiento del método,,
loadDataWithA()
....,loadDataWithX()
- Forzar la búsqueda de dependencias, por ejemplo, proporcionando solo
byId()
operaciones- Requiere muchas rutinas no orientadas a objetos, por ejemplo
findZzzById(zid)
, y luego engetYyyIds(zid)
lugar dez.getY()
- Puede ser útil buscar cada objeto en una colección uno por uno si hay una gran sobrecarga de procesamiento entre las transacciones.
- Requiere muchas rutinas no orientadas a objetos, por ejemplo
- Haga parte de la aplicación @Transactional en lugar de solo el DAO
- Posibles consideraciones de transacciones anidadas
- Requiere rutinas adaptadas para la gestión de transacciones (p. Ej., Suficientemente pequeñas)
- Pequeño impacto programático, aunque puede resultar en grandes transacciones
- Proporcionar al DAO perfiles de búsqueda dinámicos , por ejemplo,
loadData(id, fetchProfile);
- Las aplicaciones deben saber qué perfil usar cuando
- Tipo de transacciones AoP, por ejemplo, interceptar operaciones y realizar transacciones cuando sea necesario
- Requiere manipulación de código de bytes o uso de proxy
- Pérdida de control cuando se realizan transacciones
- Magia negra, como siempre :)
¿Perdí alguna opción?
¿Cuál es su enfoque preferido cuando intenta minimizar el impacto de las lazy-loaded
relaciones en el diseño de su aplicación?
(Oh, y lo siento por WoT )
java
hibernate
spring
lazy-loading
application-design
Johan Sjöberg
fuente
fuente
Respuestas:
Yo diría que la suposición inicial es incorrecta. La persistencia transaparente es un mito, ya que la aplicación siempre debe ocuparse del ciclo de vida de la entidad y del tamaño del gráfico de objetos que se carga.
Tenga en cuenta que Hibernate no puede leer pensamientos, por lo tanto, si sabe que necesita un conjunto particular de dependencias para una operación en particular, debe expresar sus intenciones de Hibernate de alguna manera.
Desde este punto de vista, las soluciones que expresan estas intenciones explícitamente (es decir, 2, 4 y 7) parecen razonables y no adolecen de la falta de transparencia.
fuente
No estoy seguro de qué problema (causado por la pereza) está insinuando, pero para mí el mayor dolor es evitar perder el contexto de la sesión en mis propios cachés de aplicaciones. Caso típico:
foo
se carga y se coloca en un mapa;foo.getBar()
(algo que nunca se llamó antes y es evaluado de forma diferida);Entonces, para abordar esto, tenemos una serie de reglas:
OpenSessionInViewFilter
para aplicaciones web);try/finally
) para que las subclases no tengan que pensar en ello;Esto, como puede ver, de hecho no se acerca a lo no invasivo y transparente . Pero el costo sigue siendo soportable, en comparación con el precio que tendría que pagar por una carga ansiosa. El problema con este último es que a veces conduce al efecto mariposa cuando se carga un solo objeto referenciado, y mucho menos una colección de entidades. El consumo de memoria, el uso de la CPU y la latencia, por mencionar lo menos, también son mucho peores, así que supongo que puedo vivir con eso.
fuente
transparency
es por obligar a la aplicación a preocuparse por cargar objetos perezosos. Si todo fue recuperado con entusiasmo, la aplicación podría ignorar por completo si los objetos se mantienen en una base de datos o no, yaFoo.getBar()
que siempre tendrá éxito. >when passing objects between threads, pass IDs
, sí, esto correspondería al # 5.Un patrón muy común es usar OpenEntityManagerInViewFilter si está creando una aplicación web.
Si está creando un servicio, abriría el TX en el método público del servicio, en lugar de en los DAO, ya que muy a menudo un método requiere obtener o actualizar varias entidades.
Esto resolverá cualquier "excepción de carga diferida". Si necesita algo más avanzado para ajustar el rendimiento, creo que buscar perfiles es el camino a seguir.
fuente
OSIV
sigue siendo un antipatrón y conduce a problemas muy serios como la incapacidad para lidiar con elegancia con excepciones o degradación del rendimiento. En resumen: en mi humilde opinión, OSIV es una solución tranquila, pero buena solo para proyectos de juguetes.