2009-05-18 9 views
12

¿Cuál es la mejor práctica?Hibernate + Swing

  • Una sola sesión para toda la aplicación
  • Una sesión por la ventana
  • Una sesión por hilo, y separar todo al azar
  • Algo más?

He buscado en Google, y no hay consenso. Todos dicen algo y luego lo retiran. Encuentro arrastrando una sesión en torno a la estupidez ...

Entonces, ¿cuál es el problema?

+0

¿Has abandonado la pregunta? – Surya

+0

Mayormente, sí. <> – Tordek

+1

Han pasado 3 años y no obtuvo la respuesta adecuada. Yo tampoco. A veces tengo la sensación de que incluso los chicos de JBoss no saben cómo usar su propio producto. Solo uso una sesión por evento. –

Respuesta

6

Tal vez una sesión por manejo de eventos sea el enfoque correcto. En las aplicaciones web, generalmente creamos una sesión por solicitud (usando todas esas cosas de OpenSessionInView, etc.). Si pensamos detenidamente, cada solicitud en la aplicación web es una interacción diferente en la aplicación, y en una aplicación Swing, cada evento disparado es una interacción diferente en la aplicación. Si aplicamos los mismos principios que aplicamos en aplicaciones web en aplicaciones Swing, entonces deberíamos crear una sesión por manejo de eventos.

Pero debemos determinada si se utiliza Hibernate es la mejor opción ... La base de datos que está accediendo a una base de datos es un local o remoto? ¿Comparten otras aplicaciones la misma base de datos? Debería considerar crear un backend EJB/HTTP en lugar de tener sus aplicaciones Swing accediendo directamente a la base de datos. El uso de Hibernate sugiere que tiene una base de datos no tan simple, por lo que creo que debería considerar crear un backend EJB \ HTTP.

1

Al cerrar la sesión, tenga en cuenta que esto provoca que sus objetos se despeguen, y supondrá una sobrecarga adicional para volver a conectar los objetos a la sesión para guardarlos o actualizarlos con la base de datos.

Dicho esto, estoy de acuerdo con usted en que cargar con una sesión en torno no tiene mucho sentido, y debe ser la liberación de la sesión al final de cada transacción. Dirigiría hacia la creación de un conjunto de sesiones desde el cual cada hilo pueda acceder a una sesión según lo necesite, y liberarla nuevamente cuando se termine. Puede usar Spring para ayudarlo a administrar las sesiones para esto.

1

Realmente depende de la aplicación, pero en realidad sólo quiere una sesión a durar tanto como la operación lógica en la base de datos. Estoy bastante seguro de que las sesiones no son seguras para subprocesos, por lo que es posible que desee buscar la localización del comportamiento en una sola clase.

1

Realmente no se puede ver ninguna razón de peso por la que se quiere cosas de manera diferente en una aplicación con un front-end de oscilación, a continuación, un front-end web. En este último caso, generalmente se crea una sesión para cada solicitud, aunque en muchos casos la creación real de la sesión se maneja de forma transparente (por ejemplo, por Spring).

Asumiendo que su aplicación oscilación implementa el patrón MVC, entonces ¿por qué no crear la sesión al inicio de cada método controlador, y cerrarlo al final. Si su aplicación es de subprocesos múltiples, puede usar una variable ThreadLocal para ubicar fácilmente cada sesión en un hilo en particular. Esto es casi exactamente análogo a lo que sucede en un contenedor Servlet (es decir, una aplicación web).

0

Prefiero la sesión por actualización del modelo. Ayuda a evitar sesiones de larga duración y también ayuda a minimizar el número total de creaciones de sesión.

0

Si crea una subprocesos entonces usted no paga la mayor parte de un precio para hacer conexiones por transacción o por solicitud, dependiendo de lo que está haciendo.

Tiendo a dividir mi código en el nivel inferior en una clase que trata con una tabla o algún otro objeto, y luego encima de eso habrá un controlador, que sabe cómo hablar con varios daos. Por ejemplo, para colocar una orden de libro, verifique si está en stock, luego procese el pago, luego disminuya el artículo y realice el pedido. Estas serían una transacción que abarcaría varios daos, por lo que deberían usar la misma sesión.

Pero, usar una conexión sería problemático, ya que está bloqueando una conexión por un tiempo, y puede perder la conexión, aunque suponga que todavía existe. El mismo problema se agravaría si se solicita por pantalla o página.

Puede ver algunas de las mejores prácticas de los usuarios de Spring, ya que Hibernate parece ser bastante popular con ese marco.

1

Mejores prácticas (su kilometraje puede variar): El patrón más común en una aplicación cliente/servidor multiusuario es la sesión por solicitud. En este modelo, una solicitud del cliente se envía al servidor (donde se ejecuta la capa de persistencia de Hibernate), se abre una nueva sesión de Hibernate y todas las operaciones de la base de datos se ejecutan en esta unidad de trabajo. Una vez que se ha completado el trabajo (y se ha preparado la respuesta para el cliente), la sesión se vacía y se cierra. También usaría una única transacción de base de datos para atender la solicitud de los clientes, comenzando y comprometiéndolo al abrir y cerrar la sesión. La relación entre los dos es uno a uno y este modelo es perfecto para muchas aplicaciones.

ver: http://docs.jboss.org/hibernate/stable/core/reference/en/html_single/#transactions-basics-uow

2

lo que se quiere resolver es la sincronización de dos modelos: Usted tiene el modelo en memoria y el modelo de base de datos. La simple propagación de cada cambio en la base de datos, como sucede, es demasiado costoso. Además, desea poder controlar los errores (es decir, retrotraer su modelo a un estado coherente). Para poder hacer esto, debes tener una forma de decir "ahora, es consistente".

La solución actual es iniciar una transacción en la base de datos cuando se sabe que el modelo es consistente (generalmente antes de comenzar a hacer cambios) y luego hacer todos los cambios en su modo en memoria, asignarlos de alguna manera a la base de datos , actualice la base de datos y luego confirme la transacción.

Si bien esto suena simple, la programación OO se interpone de manera activa. Ocultamos las operaciones de los modelos en lo más profundo de la estructura de las llamadas, intentamos realmente que los usuarios de un código no sepan lo que el código realmente hace. En un mundo perfecto, sus herramientas de desarrollo deberían desenrollar todo el código que necesita una operación en un solo método/función, envolverlo en una transacción y terminarlo.

Esto no funciona. En cambio, decidimos introducir una variable global: la sesión. Lo cual es malo, y estamos avergonzados de eso, así que tratamos de ocultar este hecho, pero la sesión es global, por operación. Ahora necesita una forma de adjuntar la sesión a la operación. Puede decir "todo el código que se ejecuta en el hilo actual es una operación". Si lo hace, la solución natural es hacer que la sesión sea global por hilo.

O tiene algún token. Luego, adjuntará la sesión al token y lo pasará por alto.

Pero el problema fundamental es y siempre fue: Cómo adjuntar una sesión a una sola operación en el modelo. Las partes difíciles deben saber cuándo comienza una operación, cuándo finaliza y cómo manejar los errores.

Para las aplicaciones web, usar una solicitud es la forma natural de definir una operación: todo lo que sucede durante una solicitud se considera un solo paso.La aplicación realmente no mantiene el modelo en la memoria; todo se olvida al final de la solicitud y se carga de nuevo desde la base de datos cuando entra la siguiente solicitud. Lento pero manejable.

Las aplicaciones de escritorio son un tipo de bestia completamente diferente. Por lo general, mantienen todo el modelo en la memoria todo el tiempo. No persistimos los cambios a menos que el usuario lo solicite (cuando "guarda" su trabajo) porque eso sería demasiado lento y, como no hay nada como una solicitud, no hay una forma simple y automática de definir una "operación" .

La idea de adjuntar una operación a un evento es buena. Solo que, en las aplicaciones de escritorio, puede tener múltiples hilos que se comunican con eventos y ahora necesita una forma de marcar todos los eventos como "pertenecen a la operación iniciada con el evento X recibido hace mucho tiempo". Los eventos suelen ser datos pequeños e inmutables, por lo que no puede adjuntarles su sesión. Pero necesitas algún tipo de token para marcar todos los eventos que pertenecen juntos. Por eso, la mayoría de las aplicaciones de escritorio funcionan con un servidor (que también funciona como una aplicación web) y sin un gran modelo en memoria o no usan una base de datos, pero guardan su modelo en un formato personalizado (piense en Office).

+0

Debo decir que es una excelente definición del problema. Pero no es una solución (aunque una definición del problema ayuda a obtener una solución). Gracias. – Tordek

+0

Dado que las computadoras son tan lentas hoy que debemos preocuparnos por el rendimiento, todavía no existe una solución general. –

6

He usado Hibernate en una aplicación de Internet Rich de tamaño empresarial, que por técnica se asemeja mucho a una combinación hibernate + swing. Pasé bastante tiempo investigando los diferentes patrones de diseño para usar hibernate en una aplicación de 3 niveles.

Hibernate no es exactamente para este tipo de aplicación, por lo que no había mucha información disponible sobre este tema. Probé con diferentes patrones de diseño, pero la mayoría condujo a fugas de memoria, problemas para adjuntar y desacoplar objetos en sesión (s), problemas con transacciones, etc. La solución final fue utilizar un patrón de sesión por solicitud. Básicamente, en cada solicitud que realiza desde la lógica de la interfaz de usuario a la lógica de negocios, crea una nueva sesión de hibernación. Luego, la lógica de su negocio realiza lo que usted quiera que haga y, justo antes de finalizar la ejecución de la lógica de negocios, vacía y cierra la sesión abierta. De esta forma no habrá fugas de memoria, los objetos se conectarán correctamente y se desconectarán de las sesiones.

Esta no es una solución perfecta, ya que encontrará algunos problemas, como la carga diferida y las transacciones. Explicaré brevemente esos problemas y cómo resolverlos.

Transacciones
Debido a que finaliza la sesión de hibernación después de cada solicitud, no puede haber transacciones que viven más allá de una solicitud. Esto puede ser algo problemático, por ejemplo, supongamos que quiere almacenar 10 objetos dentro de la misma transacción. No puede realizar una solicitud de guardado por separado para cada objeto, ya que la transacción finaliza. Por lo tanto, debe crear un método que tome como entrada una lista de objetos y guarde todos esos objetos dentro de la misma transacción. Si la transacción falla, entonces restituye todos los objetos.

Lazy loading
La carga diferida de sus objetos no funcionará porque es muy probable que no estén conectados a una sesión (es decir, si carga algo de forma lenta una vez que la sesión ha finalizado). Para que la carga diferida funcione, debe volver a conectar los objetos a la sesión. También se me ocurrió una solución para esto, que es un poco complicado cuando se crean nuevos objetos de entidad, pero funciona bien en el desarrollo cotidiano. La idea es tener getters y setters duplicados para un campo que está cargado de forma diferida. Un par es privado y el otro es público. La idea es que el par getter/setter privado es lo que hibernate usa internamente y getters y setter públicos son transitorios y utilizados por el desarrollador. Entonces, lo que sucede es que cuando el desarrollador llama al getter público, el getter comprueba si el campo ya se ha cargado, de lo contrario, adjunta el objeto a una sesión, carga el campo y cierra la sesión. Voilá, todo funciona y el desarrollador nunca notó nada.Aquí hay un pequeño código para darle la idea:


@Entity 
public class Foo { 
    List<Bar> bars = new ArrayList<Bar>(); 

    @OneToMany 
    private List<Bar> getBarsInternal() { 
    return bars; 
    } 

    private void setBarsInternal(List<Bar> bars) { 
    this.bars = bars; 
    } 

    @Transient 
    public List<Bar> getBars() { 
    // pseudo code 
    if(check if bar is still lazy loaded) { 
     // the field is still lazy loaded 
     // attach the field to a session an initialize it 
     // once the field is initialized, it can be returned 
    } 
    return getBarsInternal();  
    } 

    public void setBars(List<Bar> bars) { 
    setBarsInternal(bars); 
    } 
} 
0

Desde la creación de la sesión es caro en Oracle, y sobre todo lo uso como Oracle RDBMS, me gustaría ir a "una sola sesión para toda la aplicación". "Una sesión por manejo de evento" parece ridículo en el contexto de una aplicación GUI. Hay razones para hacerlo en la web, pero esas razones generalmente no se aplican a un programa GUI.

+2

Las sesiones de Hibernate son bastante baratas, usar una sesión por aplicación provocará una fuga de memoria :( –

+0

¿Te importaría explicar por qué la creación de sesiones es tan cara en Oracle? Utilizo Hibernate con Oracle y no he notado ningún problema importante de rendimiento, pero Realmente no hago nada demasiado pesado y nuestra base de datos es muy rápida, así que podría estar ocultando cualquier problema. – Pavel

0

Lo que realmente desea evitar es el escenario de sesión por operación, donde para cada operación de base de datos se crea y cierra una nueva sesión de Hibernate.

EDITAR: Ignore mis tonterías anteriores sobre una sesión durante toda la vida de la aplicación, es completamente posible (y probablemente la forma más eficiente) de hacerlo. Solo asegúrese de demarcar sus transacciones de manera apropiada, ya sea de forma declarativa utilizando algo como Spring @Transaction o programáticamente.

+0

No veo cómo tener una sesión durante la vigencia del programa podría evitar que realice una confirmación cada vez que se solicite ha sido procesado, entonces, ¿por qué debería haber una gran pérdida de datos? –

+0

Tienes razón en realidad, eso no debería impedir que uses una sesión. – Pavel

5

Empezamos con una sola sesión-por-aplicación, he enumerado las motivaciones y los problemas de abajo,

Como su nombre indica toda la interacción del usuario con la aplicación consistiría en una sesión única de estar siempre y toda (principal) la comunicación con la base de datos sería a través de esta sesión. Su "unidad de trabajo" abarcaría ahora toda la interacción del usuario con la aplicación ... lo que significa que su "transacción de la aplicación" es la vida entera de la aplicación.

¿Por qué? 1. El controlador más grande para hacer esto parece ser la capacidad de utilizar las funciones de latencia de hibernación, por ejemplo, un diálogo que muestra la información del Libro puede completar dinámicamente la información del autor cuando se selecciona el cuadro combinado desplegable.

  1. Como todas las modificaciones a los datos subyacentes se realizan a través de esta única sesión larga, puede demarcar explícitamente los límites de las transacciones en ubicaciones convenientes.

  2. Aproveche la caché de sesión sin tener que cargar/descargar objetos cada vez desde la base de datos.

Cómo:

Si lleva a cabo todo el trabajo importante en hilo de interfaz gráfica de usuario (casi todos los sistemas GUI de Java son eventos de un solo subproceso puede utilizar pellizco hilo patrón local para obtener la sesión principal de una interfaz gráfica de usuario . hilo

Problemas:

ventajas anteriores podrían pintar un panorama color de rosa, pero utilizando sesiones de carrera larga y sin mucha premeditación migh Puede causar problemas serios que podrían no ser aparentes hasta que la aplicación sea lo suficientemente grande o compleja.

  1. Su aplicación puede sufrir de”caché de sesión hinchada” phenomenon..with cada uno su aplicación cada vez más lento y más lento con la caché de sesión como crece más y más grande como Hibernate tiene que pasar por todos los objetos de la caché de sesión para cada consulta (con una gran sesión esto puede ralentizarlo, si la sesión se agranda, el enjuague tardará más y más tiempo, incluso si no hay cambios). Esencialmente, la ventaja n. ° 3 anterior puede convertirse en una desventaja (debido a que hiberna el retraso de escritura transaccional) si la sesión en sí permite crecer a lo grande. Una solución alternativa que podría ser útil en algunos casos podría ser utilizar query.setReadOnly (true), session.setReadOnly() para consultas/entidades que cargan objetos de solo lectura en la sesión.
  2. Manual “desalojo” para mantener la sesión consistente, Es posible que tenga consultas SQL utilización por diversas razones en diversos lugares de la aplicación para realizar actualizaciones/eliminaciones en tal caso tiene hacer desaloja manuales con el fin de mantener la sesión se corrompan.
  3. transaccional de escritura en segundo podría venir en su camino (dependiendo de la forma en que administra sesión de larga duración) si va a acceder a los datos de “otras sesiones”.
  4. El usuario puede experimentar congelamiento de GUI cuando hibernate está haciendo su magia de lazyloading.
  5. Carga de carga lenta n + 1 problemas de consulta (ver enlaces a continuación)
  6. Problemas de datos obsoletos. Si los datos se actualizan por algún trabajo en segundo plano o subproceso que no utiliza la sesión principal ... su sesión principal no estará actualizada con los cambios

Sigo pensando que si tiene una estrategia de desalojo consistente, por ejemplo, desalojar el gráfico de órdenes cuando se cierra la ventana de órdenes, esta podría ser una buena estrategia.

Nos cambiaron a sesión-por-ventana, que tenía sus propios problemas como la gestión de la comunicación entre la ventana (por ejemplo, cambios en la ventana de precios podría hacer que los datos de la ventana fin rancio). Podría querer ver el ejemplo de swing de Christian Bauer que utiliza este enfoque.

Supongo que, como con todo lo demás, no hay una única forma correcta de hacerlo, cómo administrar la sesión depende de cómo se presenta la aplicación, por ejemplo, si cada ventana puede ser una unidad de trabajo independiente, que no interfiere con otras unidades de trabajo, entonces la sesión por ventana podría ser un mejor enfoque.

0

Utilizo una sesión por serie de acciones de base de datos que pueden ser envueltas en una transacción, esto incluye pasar colecciones de objetos siempre que sea posible en un intento, pero el problema de sesión cerrada para campos/propiedades cargadas perezosas siguió apareciendo.

Mis aplicaciones son híbridos de Swing y JavaFX y esta es la forma en que los fijó:

Mi primer enfoque no iba a tener ninguna carga diferida en uso, pero esto resultó ser pesadilla para algunas de las entidades con las Gráficos de objetos más complicados.

La Tabla de control de JavaFX juega un papel importante en mis aplicaciones, creo que la oscilación JTable chupa en comparación con el TableView en JavaFX.

Ahora, cuando cargo un JFrame o un JInternalFrame o una JDialog, que precargar el más reciente 'trabajó en' entidades, a todos sus hijos están cargados de Lazy Loading, sin perfomance de golpe, Ato los datos a la TableView control con los principales campos cargados ansiosos del núcleo siendo visibles. Se requiere que un usuario seleccione un registro de la grilla TableView para trabajar, cuando lo hacen Recupero la instancia de la entidad nuevamente ya que tengo un identificador para su Id, luego de que el objeto es recuperado, luego el .size() El método se invoca para todas las colecciones secundarias que se cargan de forma perezosa. Esto obliga a Hibernate a cargar los campos/propiedades perezosos.

ahora puedo hacer lo que quiera con un caso particular de un objeto, y todos los niños estén ocupados.

Creo que este enfoque mejora el rendimiento dos veces. Cuando recuperas datos de la Lista, es rápido. Cuando busca un Objeto con Id, su punto de pin, y como los niños están todos cargados de perezoso, significa que los seleccionados también son livianos y más compactos.