2011-11-14 13 views
26

Esta es una pregunta de diseño, código concreto no enviado para proteger mi parte inferior.org.hibernate.Session.clear() considerado Dañino?

Cuando se trabaja con Hibernate el flujo de trabajo estándar es la siguiente:

  1. Sesión Abierta
  2. iniciar la transacción
  3. hacer el negocio (leer y modificar datos)
  4. confirmar la transacción
  5. Cerrar Sesión

con posiblemente iteraciones a través de 2-4.

¿Cuáles son los casos de uso razonables para Session.clear()?

A: El problema concreto que tengo es una (gran) pieza de código que carga y modifica entidades, luego borra (n) la sesión, esencialmente desechando los cambios que se hicieron. (La tarea comercial que debe realizarse no incluye la modificación de las entidades, por lo que el código "funciona").

Me parece que el diseño correcto sería asegurarse de que el código (grande) no haga cambios que no quiera guardar.

B: Supongo que Session.clear() existe por conveniencia/flexibilidad, no porque sea una buena idea usarlo.

¿He entendido mal la filosofía de Hibernate?

C: Subestación: ¿Es una mala idea que el código del marco elimine incondicionalmente() la sesión cuando se completa una tarea? En mi humilde opinión, el marco debería quejarse si la sesión está sucia cuando se completa una tarea. La sesión debe cerrarse, ya que la tarea está terminada ... (Sin tener en cuenta el rendimiento por minuto)

(Etiquetas A, B y C para que pueda indicar qué parte está respondiendo).

Respuesta

29

Ad. A: Parece que sabe lo que hace clear(). La razón para llamarlo explícitamente es eliminar todas las entidades administradas de la memoria caché L1, de modo que no crezca infinitamente al procesar grandes conjuntos de datos en una transacción.

Descarta todos los cambios que se realizaron a las entidades administradas no persistió explícitamente. Esto significa que puede modificar con seguridad una entidad, actualizarla de forma explícita y borrar la sesión. Este es el diseño derecho. Obviamente, si no se realizan cambios (larga, pero solo sesión de lectura), clear() siempre es seguro.

También puede usar stateless sessions.

Ad. B: No, existe por las razones anteriores: para asegurarse de que L1 (caché de sesión) no crezca demasiado. Por supuesto, mantenerlo manualmente es una mala idea y una indicación de que se debe usar otra herramienta para grandes conjuntos de datos, pero a veces es una necesidad.

Tenga en cuenta que en la especificación JPA también hay clear() y flush() método.En este caso, siempre debe llamar al flush() primero para insertar cambios en la base de datos (actualización explícita) antes de llamar al clear().

Ad. C: En realidad es una buena idea advertir al usuario (tal vez emitiendo un mensaje de advertencia en lugar de lanzar una excepción) cuando él/ella borra la sesión con cambios sucios. Además, no creo que un framework código debe llamar al clear() incondicionalmente, a menos que esté seguro de que el código de usuario que ejecuta se vacía o no realiza ningún cambio.

+0

ad A): El procesamiento de grandes conjuntos de datos en una transacción me parece un mal diseño (sin embargo, es posible que no haya pensado en todos los posibles problemas ...). ¿Qué quiere decir con persistir "explícitamente" en una entidad? session.save (entidad)? Ad C): Parece que estamos de acuerdo en que los marcos no deberían hacer esto: el marco no puede estar seguro de qué hacen los clientes. :-) –

+0

@MortenLauritsenKhodabocus: obviamente, cargar y modificar miles de registros en una transacción de Hibernate es una señal de que se debe usar una herramienta diferente. Sí, por * explícitamente * Me refiero a llamar a 'save()' en lugar de dejar que Hibernate descubra entidades sucias. –

+0

@MortenLauritsenKhodabocus Por favor, ilumíname. ¿Por qué sería una mala idea procesar un gran conjunto de datos de solo lectura en una transacción? Me parece que si se trata de ordenar por el lado de la base de datos, sería inteligente, ya que evitaría tener que reordenar varias veces. ¿El hecho de no usar una transacción significa que el DB recordará su estado ordenado a través de diferentes consultas que son parte de la misma transacción? Además, ¿no es 'usar una transacción' exactamente lo que hace la API ScrollableResults de Hibernate? – KyleM

3

Aquí hay otra razón por la que acabo de encontrar: el almacenamiento en memoria caché de los resultados anteriores al llamar a un procedimiento almacenado varias veces en la misma transacción. Código simplificado de la siguiente manera.

//Begin transaction 
SessionFactory sf = HibernateSessionFactory.getFactory(); 
Session dbSession = sf.getCurrentSession(); 
dbSession.beginTransaction(); 

//First call to stored procedure 
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA"); 
query.setString("custName", "A"); 
List<ShipSummaryRow> shipSummaryRows = query.list(); 

//Second call to stored procedure 
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA"); 
query.setString("custName", "B"); 
List<ShipSummaryRow> shipSummaryRows = query.list(); 

//Commit both  
dbSession.getTransaction().commit(); 

Sin una clara() después de la primera llamada, las filas ResultSet de la primera llamada se replican en el conjunto de resultados de la segunda llamada. Estoy usando Oracle 11gR2.

Clave para replicar este error es hacer ambas llamadas dentro de la misma transacción. Dado que estoy usando la sesión abierta en el patrón de vista, ambas llamadas ocurren automáticamente dentro de la misma transacción (ya que el código original llama al proceso dentro de un bucle que almacena los resultados de cada uno). Por lo tanto, lo llamo un error; else podría considerarse una función, pero incluso entonces clear() no se menciona en las muestras de código indicando que debería llamarse. session.flush() no hizo nada. Asignando el archivo como a continuación. Como resultado, agregué clear() al final de todas mis llamadas a procedimientos. Todavía no he probado con mis llamadas SQL personalizadas. Esto es algo trivial; sorprendido de que el error exista

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping> 
    <class name="com.jfx.rr.model.ShipSummaryRow"> 
     <id name="id" type="integer"/> 
     <property name="shipQtrString" not-null="true" type="string"/> 
     <property name="shipAmount" not-null="true" type="double"/> 
    </class> 
    <sql-query callable="true" name="RR_CUST_OPP_DATA"> 
     <return class="com.jfx.rr.model.ShipSummaryRow"> 
      <return-property column="SHIPPED_ID" name="id"/> 
      <return-property column="SHIP_QTR" name="shipQtrString"/> 
      <return-property column="SHIPPED_AMOUNT" name="shipAmount"/> 
     </return> 
     { call RR_DASHBOARD_REPORTS_PKG.RR_CUST_OPP_DATA(?, :custName) } 
    </sql-query> 
</hibernate-mapping> 
Cuestiones relacionadas