2010-04-15 17 views
8

Tengo una relación de muchos a mayo CohortGroup y Employee. Cada vez que inserto un empleado en CohortGroup, hibernate elimina el grupo de la tabla de resolución y vuelve a insertar todos los miembros, más el nuevo. ¿Por qué no solo agregas el nuevo?hibernar insertar en una colección provoca una eliminación luego todos los elementos en la colección que se insertará de nuevo

La anotación en el Grupo:

@ManyToMany(cascade = { PERSIST, MERGE, REFRESH }) 
@JoinTable(name="MYSITE_RES_COHORT_GROUP_STAFF", 
joinColumns={@JoinColumn(name="COHORT_GROUPID")}, 
inverseJoinColumns={@JoinColumn(name="USERID")}) 
public List<Employee> getMembers(){ 
    return members; 
} 

El otro lado en el Empleado

@ManyToMany(mappedBy="members",cascade = { PERSIST, MERGE, REFRESH }) 
public List<CohortGroup> getMemberGroups(){ 
    return memberGroups; 
} 

Código snipit

Employee emp = edao.findByID(cohortId); 
CohortGroup group = cgdao.findByID(Long.decode(groupId)); 
group.getMembers().add(emp); 
cgdao.persist(group); 

a continuación es el sql reportado en el registro de

delete from swas.MYSITE_RES_COHORT_GROUP_STAFF where COHORT_GROUPID=? 
insert into swas.MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 
insert into swas.MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 
insert into swas.MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 
insert into swas.MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 
insert into swas.MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 
insert into swas.MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 

Esto se ve muy ineficiente y está causando algunos problemas. Si se realizan varias solicitudes para agregar un empleado al grupo, algunas terminan por escrito.

Las costuras como iguales y hashCode pueden ser una razón para esto. A continuación están la implementación de estos métodos. ¿Alguna bandera roja?

CohortGroup

@Override 
public int hashCode() { 
    final int prime = 31; 
    int result = getName().hashCode(); 
    result = prime * result + ((emp == null) ? 0 : emp.hashCode()); 
    return result; 
} 
@Override 
public boolean equals(Object obj) { 
    if (this == obj) {return true;} 
    if (!(obj instanceof CohortGroup)) {return false;} 
    CohortGroup other = (CohortGroup) obj; 
    if(!getName().equals(other.getName())){return false;} 
    if (emp == null && other.getOwner() != null) { 
     return false; 
    } else if (!emp.equals(other.getOwner())) { 
     return false; 
    } 
    return true; 
} 

Empleado

 @Override 
public boolean equals(Object obj) { 
    if (this == obj) {return true;} 
    if (obj == null) {return false;} 
    if (!(obj instanceof Employee)) {return false;} 
    Employee other = (Employee) obj; 
    if (EMPLID == null && other.getEMPLID() != null) { 
     return false; 
    } else if (!EMPLID.equals(other.getEMPLID())) { 
     return false; 
    } 
    return true; 
} 

    @Override 
public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + ((EMPLID == null) ? 0 : EMPLID.hashCode()); 
    return result; 
} 

he añadido un método addMember a la CohortGroup que se suma a ambos lados de la relación:

public void addMember(Employee emp){ 
    this.getMembers().add(emp); 
    emp.getMemberGroups().add(this); 

} 

Continuación gracias a todos los que Están ayudando.

+0

Sí, tengo el mismo comportamiento. Acabo de aprender a vivir con eso :-) Ocurre cuando configuras una Lista/Set en una clave externa –

+0

Entonces, ¿qué haces para vivir con ella? Reduzca la velocidad de la interfaz de usuario para evitar que suceda. Tengo una aplicación ajax que puede tomar estas solicitudes tan rápido como el usuario pueda hacer clic. Esto causa algunas fallas intermitentes en las inserciones. – Mark

+0

Tuve más o menos el mismo problema ... ¡Ten fe! http://stackoverflow.com/questions/179259 – Yuval

Respuesta

0

que tienen insertos que actúan de la manera que les esperaba ahora. Gracias a Pascal y z5h, he aprendido mucho. Creo que tengo hashCode e iguales implementados correctamente. Sin embargo, esto nunca resolvió el problema para mí. En cambio, he implementado una Entidad Intermedia.

Por lo que vale la pena a continuación, están las asignaciones en mis clases Employee, CohortGroup y ahora CohortGroupMemeber.

Empleado:

@OneToMany(mappedBy="member") 
public List<CohortGroupMember> getMemberGroups(){ 
    return memberGroups; 
} 
public void setMemberGroups(List<CohortGroupMember> grps){ 
    memberGroups = grps; 
} 

CohortGroupMember

@ManyToOne 
@JoinColumn(name="USERID") 
public Employee getMember(){ 
    return emp; 
} 
public void setMember(Employee e){ 
    emp = e; 
} 
@ManyToOne 
@JoinColumn(name="COHORT_GROUPID") 
public CohortGroup getGroup(){ 
    return group; 
} 
public void setGroup(CohortGroup cg){ 
    group = cg; 
} 

CohortGroup

@OneToMany(mappedBy="group") 
public List<CohortGroupMember> getMembers(){ 
    return members; 
} 
public void setMembers(List<CohortGroupMember> emps){ 
    members = emps; 
} 

El libro he seguido de esto es Java Persistence con Hibernate capítulo 7.2.3

+1

Aunque esta es una solución. La mejor solución es usar un conjunto en lugar de una lista. Vea el artículo que relacioné con la respuesta de Marc por el motivo. – JustinKSU

+0

@JustinKSU Gracias, ¡realmente aprecio ese gran artículo! Siempre puedes aprender algo nuevo sobre la hibernación :) –

0

Tienes que definir hashCode() y equals() en tus CohortGroup y Employee entidades. Esto puede hacerlo automáticamente su IDE y puede estar en la clave principal (a veces no es una buena idea) o en una clave comercial (preferible).

Lee this article.

+0

Gracias por el enlace al artículo, ayuda a – Mark

7

Sospecho que no está anulando equals y hashCode adecuadamente. Anularlos erróneamente puede llevar al tipo de comportamiento que está experimentando (ya que la tecla de almohadilla se usa como claves en los mapas). Verifique lo que hizo con equals y hashCode.

Usando sus entidades anotadas con buena equals y hashCode, este código (lógicamente equivalente):

Session session = HibernateUtil.beginTransaction(); 
Employee emp = (Employee) session.load(Employee.class, 1L); 
CohortGroup group = (CohortGroup) session.load(CohortGroup.class, 1L); 
group.getMembers().add(emp); 
emp.getMemberGroup().add(group); // set the other side too!! 
session.saveOrUpdate(group); 
HibernateUtil.commitTransaction(); 

produce el siguiente resultado en mi máquina:

 
08:10:32.426 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - processing flush-time cascades 
08:10:32.431 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - dirty checking collections 
08:10:32.432 [main] DEBUG org.hibernate.engine.CollectionEntry - Collection dirty: [com.stackoverflow.q2649145.CohortGroup.members#1] 
08:10:32.432 [main] DEBUG org.hibernate.engine.CollectionEntry - Collection dirty: [com.stackoverflow.q2649145.Employee.memberGroup#1] 
08:10:32.443 [main] DEBUG org.hibernate.engine.Collections - Collection found: [com.stackoverflow.q2649145.CohortGroup.members#1], was: [com.stackoverflow.q2649145.CohortGroup.members#1] (initialized) 
08:10:32.448 [main] DEBUG org.hibernate.engine.Collections - Collection found: [com.stackoverflow.q2649145.Employee.memberGroup#1], was: [com.stackoverflow.q2649145.Employee.memberGroup#1] (uninitialized) 
08:10:32.460 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 2 objects 
08:10:32.461 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 (re)creations, 2 updates, 0 removals to 2 collections 
08:10:32.463 [main] DEBUG org.hibernate.pretty.Printer - listing entities: 
08:10:32.473 [main] DEBUG org.hibernate.pretty.Printer - com.stackoverflow.q2649145.CohortGroup{id=1, members=[com.stackoverflow.q2649145.Employee#1]} 
08:10:32.474 [main] DEBUG org.hibernate.pretty.Printer - com.stackoverflow.q2649145.Employee{id=1, memberGroup=} 
08:10:32.474 [main] DEBUG o.h.p.c.AbstractCollectionPersister - Inserting collection: [com.stackoverflow.q2649145.CohortGroup.members#1] 
08:10:32.480 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 
08:10:32.491 [main] DEBUG org.hibernate.SQL - insert into MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 
Hibernate: insert into MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 
08:10:32.496 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 1 
08:10:32.497 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 2 
08:10:32.499 [main] DEBUG o.h.p.c.AbstractCollectionPersister - done inserting collection: 1 rows inserted 

No se elimine antes de que el inserto !

Por cierto, tenga en cuenta que debe establecer el enlace en ambos lados cuando se trabaja con asociaciones bi-direccional, como lo hice en el grupo y en el empleado.

O añadir métodos de gestión de enlaces defensivas en sus clases, por ejemplo en CohortGroup:

public void addToMembers(Employee emp) { 
    this.getMembers().add(emp); 
    emp.getMemberGroup().add(this); 
} 

public void removeFromMembers(Employee emp) { 
    this.getMembers().remove(emp); 
    emp.getMemberGroup().remove(this); 
} 
+0

Voy a verificar dos veces el código hash y es igual. Están implementados, solo hice que Eclipse los creara para mí. Si eso no funciona, comenzaré con tus otras recomendaciones. Publicaré mis resultados para la prosperidad. – Mark

+0

Estoy bastante seguro de que tengo el código hash y equals implementado correctamente, pero el comportamiento permanece. La primera vez que un grupo tiene un empleado agregado, no hay eliminación. Simplemente sucede si ya hay más de un empleado en el grupo. No estoy seguro si su prueba agregó un empleado a un grupo que ya tenía empleados o no. – Mark

+0

@Mark Sí, el grupo tenía 2 empleados, como se muestra en el seguimiento –

1

Como otros han sugerido, es probablemente un problema con hashcode o equals.

Específicamente: los proxies Hibernate causan problemas con instaceof que está utilizando en su método equals. Eso significa malas noticias.

mira esto: http://community.jboss.org/wiki/ProxyVisitorPattern

+0

alterar el método es igual que hacer una comparación de la clase getNombre (this.getClass(). GetName(). Equals (other.getClass(). GetName()) evitar el problema, o están haciendo alusión a cambio de utilizar el ?.. patrón de visitante a recuperar estas listas – Mark

+0

sin alterar el método no funcionará porque las clases proxy tienen nombres divertidos una cosa que puede hacer es añadir un método como Clase getThisClass modo 'pública() {return this.getClass(); } 'Cuando llamas a' getClass' en el proxy, obtienes la clase del proxy. Cuando llamas a 'getThisClass', delega a tu clase" real ". Tipo de hack en comparación con el visitante. – z5h

+0

¿No debería agregar algo? Verificación para asegurarse de que el método existía antes de llamarlo La razón por la cual hace una instancia es asegurarse de que es seguro lanzar el objeto pasado. Así que no entiendo cómo funcionaría getThisClass cuando se llama desde el método igual o el visitante la opción sigue siendo la mejor c andidate para explorar. – Mark

5

que tenía este mismo problema y con un poco de prueba y error encontraron que las eliminaciones no ocurrieron si utilicé un conjunto en lugar de una lista como mi colección. Irritante, dado que estoy usando JSF y los componentes de la interfaz de usuario solo se repetirán las listas. Pero ahí está.

+3

Esta es la raíz del problema. Lee http://assarconsulting.blogspot.com/2009/08/why-hibernate-does-delete-all-then-re.html para obtener más detalles. – JustinKSU

+0

-1 porque tuve el mismo problema con Sets. Su respuesta ayudará a cualquiera que esté manteniendo el conjunto de entidades dentro de una sesión de hibernación y así puedan confiar en la identidad java estándar (==). Sin embargo, entre dos sesiones de hibernación, los Conjuntos mostrarán el mismo problema que las Listas. El problema se reduce a implementar equals y hashCode correctamente. –

Cuestiones relacionadas