гибернация при вставке в коллекцию приводит к удалению, после чего все элементы в коллекции снова вставляются - PullRequest
8 голосов
/ 16 апреля 2010

У меня есть отношения ко многим майам CohortGroup и Employee. Каждый раз, когда я вставляю сотрудника в спящий режим CohortGroup, он удаляет группу из таблицы разрешений и снова вставляет всех участников, а также нового. Почему бы просто не добавить новый?

Аннотация в группе:

@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;
}

Другая сторона в Сотруднике

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

Код снипита

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

ниже sql сообщается в журнале

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 (?, ?)

Этот шов действительно неэффективен и вызывает некоторые проблемы. Если делается несколько запросов на добавление сотрудника в группу, то некоторые из них переписываются.

Причиной этому могут быть такие швы, как equals и hashCode. Ниже приведена реализация этих методов. Есть красные флаги?

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;
}

сотрудник

       @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;
}

В CohortGroup добавлен метод addMember, который добавляет обе стороны отношения:

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

}

Продолжение спасибо всем, кто помогает.

Ответы [ 7 ]

7 голосов
/ 16 апреля 2010

Я очень подозреваю, что вы не переопределяете equals и hashCode должным образом. Неправильное их переопределение может привести к типу поведения, которое вы испытываете (поскольку ключ хеша используется в качестве ключей на картах). Дважды проверьте, что вы сделали с equals и hashCode.

Используя ваши аннотированные сущности с хорошими equals и hashCode, этот код (логически эквивалентный):

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();

производит на моей машине следующий вывод:

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

Нет удаления перед вставкой!

Кстати, обратите внимание, что при работе с двунаправленными ассоциациями вы должны установить связь на обеих сторонах , как я это делал в группе и на сотруднике.

Или добавьте в свои классы методы управления защитными ссылками, например, 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);
}
5 голосов
/ 24 августа 2011

У меня была такая же проблема, и с некоторыми пробами и ошибками было обнаружено, что удаления не произошло, если я использовал Set вместо List в качестве своей коллекции. Раздражает, учитывая, что я использую JSF и компоненты пользовательского интерфейса будут перебирать только списки. Но это так.

1 голос
/ 08 апреля 2019

У меня такая же проблема. Я изменил из списка в набор. Это работает для меня.

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

@ManyToMany(mappedBy="members",cascade = { PERSIST, MERGE, REFRESH } )
public Set<CohortGroup> getMemberGroups(){
  return memberGroups;
}
1 голос
/ 19 апреля 2010

Как и предполагали другие, это, вероятно, проблема с hashcode или equals.

В частности: прокси Hibernate вызывают проблемы с instaceof, который вы используете в методе equals. Это говорит о плохих новостях.

Проверьте это: http://community.jboss.org/wiki/ProxyVisitorPattern

0 голосов
/ 17 июля 2018

Загрузить объект в контексте постоянства и вызвать слияние, если существует, и сохранить, если не существует.

Employee emp = new Employee();
//set emp attribute and members.
Session session = ...
Transaction transaction = ...
Employee db = (Employee) session.get(Employee .class,emp.getId());
if(db != null)  session.merge(emp);
else  session.save(emp);
/*session.saveOrUpdate(emp); This will delete the join table and reinsert entry  
again*/
transaction.commit();
session.close();
0 голосов
/ 20 апреля 2010

У меня есть вставки, действующие так, как я ожидаю их сейчас. Благодаря Паскалю и z5h я многому научился. Я считаю, что у меня есть hashCode и равно правильно реализованы. Это никогда не решало проблему для меня все же. Вместо этого я реализовал Промежуточную сущность.

Ниже приведены сопоставления в моих классах Employee, CohortGroup, а теперь и CohortGroupMemeber.

Сотрудник:

@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;
}

Книга, за которой я следовал, - «Сохранение Java» в Hibernate, глава 7.2.3

.
0 голосов
/ 16 апреля 2010

Вы должны определить hashCode() и equals() для ваших CohortGroup и Employee сущностей. Это может быть сделано автоматически вашей IDE и может быть либо по первичному ключу (иногда не очень хорошая идея), либо по бизнес-ключу (предпочтительно).

Читать эту статью .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...