2010-06-29 18 views
5

En griales, que puede poner en práctica una N: 1 relación de la siguiente manera:¿Cuándo se debe usar Many para las relaciones N: 1 en las clases de dominio Grails?

class Parent { hasMany = [children:Child] } 
class Child { belongsTo = [parent:Parent] } 

ahora (si AddTo y removeFrom es siempre utiliza correctamente) que puede hacer que los niños de los padres a través de parent.children.

Pero también puede hacerlo sin hasMany:

class Parent { } 
class Child { belongsTo = [parent:Parent] } 

entonces tengo que usar Child.findAllByParent (padre) para llegar a todos los niños.

Mi pregunta: ¿Hay alguna razón importante por la que debería usar hasMany si también puede consultar los hijos de un padre de la segunda manera?

Supongo que a veces es más fácil (y quizás más rápido si se busca junto con el padre?) Simplemente referirse a parent.children, pero por otro lado esta lista puede ser bastante larga cuando hay varios hijos. Y lo que no me gusta de Many tampoco es que siempre tenga que preocuparse por addTo o removeFrom o para borrar la sesión después de agregar un nuevo Child con un Parent para que grails lo haga automáticamente ...

Is la respuesta es que simplemente debes usar hasMany si hay pocos niños y no usarlo si hay muchos (por razones de rendimiento), o hay más detrás de esto?

Respuesta

8

El uso de hasMany versus belongsTo está más relacionado con el comportamiento en cascada que desea especificar cuando se produce una actualización/eliminación. En su segundo ejemplo, la cascada se establece en TODO en el lado de los niños y NINGUNO en el lado de los padres. Si elimina un elemento secundario, nada sucederá en el elemento principal. Si elimina el elemento primario, todos los elementos secundarios se eliminarán automáticamente.

En el primer ejemplo, la cascada se establece en TODO en el lado principal y GUARDAR-ACTUALIZAR en el lado secundario. Así que ahora usted puede hacer algo como:

parent.addToChildren(child1) 
parent.addToChildren(child2) 
parent.addToChildren(child3) 
parent.save(flush:true) 

Y cuando se guarda el padre, todos los niños se actualizará.

Al referirse a algo que no pidió, usted podría también suponer tener algo como:

class Parent { hasMany = [children:Child] } 
class Child { Parent parent } 

Si se define la relación de hijo a padre de esta manera, tendrá que gestionar manualmente los objetos secundarios que hace referencia al padre cuando elimina el padre *. (* Esto corrige una declaración anterior que resultó ser inexacta)

Así, el hasMany/belongsTo tiene dos consideraciones principales:

  1. ¿Qué tipo de estrategia en cascada hace que desea ejecutar sobre actualizaciones/eliminaciones
  2. ¿Cómo es probable que accedas a los datos, si esperas necesitar recuperar un conjunto de hijos para un padre, tener un método parent.getChildren() es muy conveniente.

ACTUALIZACIÓN:

También quiero aclarar, no se GORM-fetch ansiosos cuando se utiliza hasMany; de forma predeterminada, GORM usa una estrategia de recuperación diferida para que no llegue a los hijos hasta que intenten acceder al elemento primario.niños

Si desea una asociación para ser recuperada tempranamente por defecto, se puede especificar la asignación apropiada:

class Parent { 
    hasMany = [children:Child] 
    static mapping = { 
    children lazy:false 
    } 
} 

Por último, se ha mencionado que no le gusta que usted tiene que preocuparse por el AddTo/removeFrom en el lado hasMany. No deberías tener que hacer esto si guardas con flush: verdadero.

def parent = Parent.get(id) 
def child = new Child(name:'child1', parent:parent) 
if(child.save(flush:true)) { 
    // both sides of the relationship should be good now 
} 

EDIT: fijo orden inverso de los valores por defecto en cascada hijo/padre y el error corregido con respecto a cómo maneja las relaciones gorm sin belongsTo

+0

Gracias, así que todo se trata del comportamiento en cascada. ¿Diría que el rendimiento no es un problema cuando la lista para administrar para los niños se vuelve muy grande? En cuanto a su actualización, desafortunadamente se guarda con flush: true no es suficiente para addTo/removeFrom automático. Como aprendí en mi ejemplar sometido a Grails https://cvs.codehaus.org/browse/GRAILS-6356, debes borrar la sesión con sessionFactory.currentSession.clear() si quieres lograr esto DENTRO de una prueba o un controlador . En una producción con scaffolded, funciona porque la sesión se borra después de la acción de guardar/actualizar del elemento secundario. –

+0

Acabo de probarlo y parece que la eliminación en cascada es lo opuesto a lo que dice al principio: en mi PRIMER ejemplo (con hasMany), al eliminar un elemento primario, también se eliminan todos los elementos secundarios. En el SEGUNDO ejemplo (sin hasMany), los niños deben borrarse manualmente antes de eliminar un elemento principal. –

+0

En realidad, mis pruebas también revelaron que en su ejemplo con hasMany y no belongsTo, la eliminación de parent no provoca que los hijos tengan un padre nulo pero arroja una DataIntegrityViolationException ... Tengo que establecer los padres de los hijos en null antes de la eliminación . –

0

Muy buena pregunta, y la respuesta aceptada actualmente es buena. Hay otra consideración de rendimiento importante, que es lo que sucede cuando agrega y guarda un nuevo hijo. En su primer ejemplo, Grails tiene que cargar por defecto la lista completa de elementos secundarios de la base de datos antes de insertar la nueva en el conjunto, para garantizar la exclusividad. En el segundo caso, no lo hace, lo que conduce a un rendimiento mucho mejor. Puede evitar este comportamiento en su primer ejemplo definiendo los elementos secundarios como 'Colección' según http://grails.org/doc/latest/guide/single.html#sets,ListsAndMaps

Cuestiones relacionadas