2011-06-09 6 views
34

que tienen una situación extraña que parece indicar un problema GORM Cacheing¿Alguna vez necesito enjuagar explícitamente GORM guardar llamadas en griales?

//begin with all book.status's as UNREAD 
Book.list().each { book.status = Status.READ ; book.save() } 

println (Book.findAllByStatus (Status.READ)) //will print an empty list 
println (Book.list().findAll (it.status == Status.READ)) // will print all books 

No puedo entender por qué las dos últimas consultas pueden devolver resultados diferentes. Sin embargo, si realizo la siguiente modificación de book.save (flush: true). Ambas instrucciones impresas devolverán todos los libros.

Tenía la impresión de que esto no era necesario en una sola aplicación.

Como referencia que estoy usando

  • DB: mysql
  • maravilloso: 1.7.10
  • Grails: 1.3.7

@ Hoang Long

Mi problema se demuestra a continuación, supongo que action1/action2 se llaman muchas veces, en ningún patrón particular

def action1 = { 
    Foo foo = Foo.get(params.id) 
    //... modify foo 
    foo.save() //if I flush here, it will be inefficient if action1 is called in sequence 
} 

def action2 = { 
    //if I flush here, it will be inefficient if action2 is called in sequence 
    List<Foo> foos = Foo.findAllByBar (params.bar) 
    //... do something with foos 
} 

Una solución sería tener una bandera que está establecido por action1 y usada por acción2 para eliminar si es necesario. Mi problema es que esta es una solución demasiado compleja, que no es escalable a medida que aumenta la complejidad de las llamadas a bases de datos.

boolean isFlushed = true 

def action1 = { 
    Foo foo = Foo.get(params.id) 
    //... modify foo 
    foo.save() 
    isFlushed = false 
} 

def action2 = { 
    if (!isFlushed) { 
     //flush hibernate session here 
    } 
    List<Foo> foos = Foo.findAllByBar (params.bar) 
    //... do something with foos 
} 

Respuesta

30

En su caso, la primera declaración devuelve la lista vacía porque lee datos de la base de datos, pero los datos aún no están allí.

Es cómo funciona Hibernate: Cuando se llama a guardar con (flush: true), se eliminará la sesión de Hibernate, todos los datos persistentes en la sesión a la base de datos inmediatamente. Si no usa (flush:true), los datos solo se registran en la sesión de Hibernate y solo se conservan en la base de datos cuando la sesión de Hibernate se vacía. El tiempo para purgar la sesión es determinado automáticamente por Hibernate para optimizar el rendimiento.

En general, debe dejar que Hibernate haga el trabajo por usted (por el bien de la optimización), a menos que desee que los datos se conserven de inmediato.

Según Peter Ledbrook:

Let Hibernate do it's job and only manually flush the session when you have to, or at least only at the end of a batch of updates. You should only really use if you're not seeing the data in the database when it should be there. I know that's a bit wishy-washy, but the circumstances when such action is necessary depend on the database implementation and other factors.

De GORM Gotchas - part 1

ACTUALIZACIÓN: ser claro acerca de cómo vaciar la sesión una vez, después de todo el objeto ser salvo:

import org.hibernate.* 

class SomeController { 
    SessionFactory sessionFactory 

    def save = { 
    assert sessionFactory != null 

    // loop and save your books here 

    def hibSession = sessionFactory.getCurrentSession() 
    assert hibSession != null 
    hibSession.flush() 
    } 
} 
+0

Bastante. El ejemplo que di en mi pregunta fue un caso idealizado del problema.En realidad, cada llamada a save() ignora qué consultas se usarán más adelante ... No quiero usar siempre (flush: true) por miedo, pero ¿qué más puedo hacer? – Akusete

+0

@Akusete: en su caso, ¿solo podría llamar al hibernate para finalizar la sesión al final? –

+0

Este enlace puede ser útil: http://grails.org/FAQ#Q:%20How%20can%20I%20flush%20a%20Hibernate%20session%20multiple%20times%20within%20my%20controller%3F –

7

I se pregunta cuál fue su configuración FlushMode.

De forma predeterminada, se establece en "auto" y significa que la sesión se vacía antes de cada consulta que golpea DB directamente (y probablemente en otros casos también).En ese caso, su Foo.findAllByBar debe enjuagar primero la sesión (¡posible problema de rendimiento!) Y leer el valor correcto de la base de datos.

Existen otros dos valores para FlushMode y si configura uno de ellos, esto explicaría sus problemas. Primero es "manual", lo que significa que usted decide realizar una sesión de vaciado manual (por ejemplo, con guardar (vaciar: verdadero)). Si no hace eso, entonces Foo.findAllByBar lee el estado obsoleto de la base de datos. El segundo es "commit" lo que significa que la sesión se vacía con cada confirmación de transacción. Eso es muy útil si utiliza la instrucción "withTransaction" en grillas.

Recursos: http://schneide.wordpress.com/2011/03/08/the-grails-performance-switch-flush-modecommit/ http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/objectstate.html#d0e1215

40

Do I ever need to explicitly flush GORM save calls in grails?

En resumen , si desea utilizar el objeto inmediato como lo están haciendo en su código!.

Me enfrenté al mismo problema, esta es la imagen que obtuve después de leer algunas referencias.

Esto es hibernate la sesión número.
La sesión de Hibernación se crea cuando se llama a la acción del controlador y finaliza cuando la acción regresa (o muere con error temprano). Si un código no está llamando a ningún código transaccional, la interacción db de Hibernate se puede representar así:
Suponga que el nombre de la acción de entrada es nombre de acción y la llamada a la acción se completa sin ningún error.

NB: La barra del medio (la memoria caché de segundo nivel está desactivada) porque no hay ningún código transaccional. without transaction without error

si el mismo código tiene un error:

without transaction with error

Pero si su acción está llamando método transaccional o está creando transacción en línea con withTransaction (y asuma la llamada a la acción completada sin ningún error). with transaction without error

Si el código tiene un error: with transaction with error

espero que ayude, pero si he hecho algún error o perdida para incluir gran punto, me voy a comentar actualizar mis fotos.

+1

¡Buena explicación! –

+0

¡Esto es genial, bonitos gráficos! –

1

Para información adicional, no puede usar flush o save (flush: true) en los eventos de clase de su dominio (afterUpdate, beforeUpdate, ect) Causa un error de desbordamiento de la pila. Puede usar save() sin vaciado.

gorm docs

Cuestiones relacionadas