-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Deleting not possible in ManyToMany association
PostPosted: Sat Jan 20, 2007 5:21 pm 
Beginner
Beginner

Joined: Mon Mar 20, 2006 7:59 am
Posts: 30
Hello,

I am using the latest version of Hibernate (3.2.1.GA) with JPA annotations.

I've got two classes: Student and Exam. A student only consists of a name and an exam consists of a name and a list of students.

I load an exam from the database and remove a student from the list. When I merge the resulting exam object into the database the student's removal does not get persisted in the database.

In Java-(pseudo)-code it would look like this:

Code:
Exam exam = (Exam) entityManager.createQuery("FROM Exam ex").getSingleResult();
exam.removeStudent(exam.getStudents().get(0)); // Removes the first student
entityManager.merge(exam); <-- This does not persist the changed list.


This is the complete testcase: (The test fails with Firebird and H2 - that's all I've tested as I think it is a general issue)

Exam.java:
Code:
package hibernate;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;

@Entity
public class Exam {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   private String name;

   @ManyToMany()
   @JoinTable(name = "Exams_Students")
   private List<Student> students = new ArrayList<Student>();
   
   public Exam() {
      
   }

   public Exam(String name) {
      super();
      this.name = name;
   }

   public String getName() {
      return this.name;
   }

   public void setName(String name) {
      this.name = name;
   }
   
   public List<Student> getStudents() {
      return this.students;
   }
   
   public void addStudent(Student student) {
      this.students.add(student);
   }
   
   public void removeStudent(Student student) {
      this.students.remove(student);
   }
   
}

Student.java:
Code:
package hibernate;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Student {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;
   
   private String name;

   public Student() {
      
   }

   public Student(String name) {
      super();
      this.name = name;
   }
   

   public String getName() {
      return this.name;
   }

   public void setName(String name) {
      this.name = name;
   }
   
}


BaseTest.java:
Code:
package hibernate;

import javax.persistence.EntityManagerFactory;

import org.hibernate.ejb.Ejb3Configuration;
import org.junit.Before;

import de.pwell.wvmonitor.domain.Location;
import de.pwell.wvmonitor.domain.Machine;

public class BaseTest {

   protected EntityManagerFactory factory;

   @Before
   public void setUp() {
      Ejb3Configuration cfg = new Ejb3Configuration();
      cfg.setProperty("hibernate.hbm2ddl.auto", "create");
      cfg.setProperty("hibernate.connection.pool_size", "1");
      cfg.setProperty("hibernate.current_session_context_class", "thread");
      cfg.setProperty("hibernate.cache.provider_class", "org.hibernate.cache.NoCacheProvider");
      cfg.setProperty("hibernate.show_sql", "true");
      
//      cfg.setProperty("hibernate.connection.driver_class", "org.firebirdsql.jdbc.FBDriver");
//      cfg.setProperty("hibernate.connection.url", "jdbc:firebirdsql:server:/var/lib/firebird2/wvmonitor/wvmonitor.fdb");
//      cfg.setProperty("hibernate.connection.username", "sysdba");
//      cfg.setProperty("hibernate.connection.password", "master");
//      cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.FirebirdDialect");
      
      cfg.setProperty("hibernate.connection.driver_class", "org.h2.Driver");
      cfg.setProperty("hibernate.connection.url", "jdbc:h2:mem:");
      cfg.setProperty("hibernate.connection.username", "sa");
      cfg.setProperty("hibernate.connection.password", "");
      cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");

      cfg.addAnnotatedClass(Exam.class);
      cfg.addAnnotatedClass(Student.class);
      this.factory = cfg.createEntityManagerFactory();
   }

}


Test.java:
Code:
package hibernate;

import static org.junit.Assert.fail;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

public class Test extends BaseTest {

   /**
    * {@inheritDoc}
    */
   @Override
   public void setUp() {
      super.setUp();
      EntityManager em = factory.createEntityManager();
      EntityTransaction trans = em.getTransaction();
      trans.begin();
      try {
         em.persist(new Student("Peter"));
         em.persist(new Student("Mike"));
         em.persist(new Student("Steve"));

         List<Student> students = (List<Student>) em.createQuery("FROM Student t").getResultList();
         
         Exam ex = new Exam("Exam 1");
         int counter = 0;
         for (Student student : students) {
            ex.addStudent(student);
         }
         em.persist(ex);
         
         trans.commit();
      } finally {
         if (trans.isActive())
            trans.rollback();
      }      
   }

   @org.junit.Test
   public void testExam() {
      EntityManager em = factory.createEntityManager();
      EntityTransaction trans = em.getTransaction();
      trans.begin();
      try {
         Query query = em.createQuery("FROM Exam t");
         query.setMaxResults(1);
         Exam ex = (Exam)query.getSingleResult();
         if (ex.getStudents().size() != 3)
            fail("Here should be three students.");
         
         ex.removeStudent(ex.getStudents().get(0));

         em.merge(ex);
         em.flush();
         trans.commit();
      } finally {
         if (trans.isActive())
            trans.rollback();
      }
      
      // Create new EntityManager.
      em = factory.createEntityManager();

      Query query = em.createQuery("FROM Exam t");
      query.setMaxResults(1);
      
      Exam updatedEx = (Exam)query.getSingleResult();
      if (updatedEx.getStudents().size() != 2)
         fail("Here should only be two students.");  // <--- Here does the test fail.
   }
}


I am doing something wrong or is it a bug in hibernate?

Regards and thanks a lot for helping,
Dominik


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jan 21, 2007 9:05 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
there is a small bug in hibernate
But you do not have to use merge, since the object is already managed by the persistence context (every loaded object is managed until the pc completion).
commenting th emerge should work

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 22, 2007 1:53 pm 
Beginner
Beginner

Joined: Mon Mar 20, 2006 7:59 am
Posts: 30
Ah okay I see. That means my "saveOrUpdate" method in my DAO is completely useless? I implemented it like that:

Code:
   public T saveOrUpdate(T entity) {
      EntityTransaction trans = getEntityManager().getTransaction();
      boolean alreadyActive = trans.isActive();
      T result = entity;
      try {
         if (!alreadyActive)
            trans.begin();

         if (entity.getId() == null)
            getEntityManager().persist(entity);
         else
            result = getEntityManager().merge(entity);

         if (!alreadyActive)
            trans.commit();
         else
            flush();
      } finally {
         if (!alreadyActive && trans.isActive())
            trans.rollback();
      }
      return result;
   }


I thought I had to "merge" (or whatever) the entity I tried to save. But that means also that I cannot change whatever property of the entity without clearing the entitymanager when i don't want the changes to be persisted. Is that right?

What do you usually do therefore? How should I write my DAO-layer if changes are persisted automatically?

Thanks,
Dominik


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 24, 2007 4:48 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
sir-archimedes wrote:
What do you usually do therefore? How should I write my DAO-layer if changes are persisted automatically?


That's the beauty of it you don't have to.
if you want a example app check out http://caveatemptor.hibernate.org and the book Java Persistence with Hibernate

About your comment on clearing the PC to avoid change propagation, you are right, but you can do em.getDelegate().evict(object); //add the Session casting

_________________
Emmanuel


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.