0

I have a problem with bidirectional many-to-many mapping in hibernate by annotation.

The setup is Student : StudentCertification with N:N relationship. The @ManyToMany(mappedby = …) annotation is used on the inverse non-owning side (StudentCertification) with @ManyToMany and @JoinTable annotation on the owner side (Student).

The problem is, when the records are stored by saving non-owning side only, no mapping table (Student_StudentCertification) is filled up whereas table of owning and non-owning side is filled up (see Hibernate log 2). Therefore, there is no N:N relationship in database, only not related two tables Student and StudentCertification exists. Note, all three mentioned tables are created (see the hibernate log 1). When the owning side object (Student) is saved, there is no problem. All tables are filled up. It indicates that the parameter mappedby does not work well and do not point at the owner side property where the description of N:N relationship is defined by annotation. The code used jUnit test to save 10 non-owning objects STUDENTCERTIFICATION which each of them has add 10 different students (see the jUnit code):

Could you help me, why the mappedby parameter is not enough??? Or where else could the problem be? Thanks.

Owning side STUDENT:

@Entity
@Table(name = "STUDENT")
public class Student {
//== VARIABLE INSTANCE FIELDS ==============================================

@Id
@GeneratedValue
@Column(name = "STUDENT_ID_COLUMN")
private int student_id;

@Column(name = "NAME_COLUMN", nullable = false)
private String student_name;


@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "STUDENT_STUDENTCERTIFICATION",
joinColumns        = {@JoinColumn

(name = "STU_ID", referencedColumnName = "STUDENT_ID_COLUMN")},
    inverseJoinColumns = {@JoinColumn
(name = "CER_ID", referencedColumnName = "CERTIFICATION_ID_COLUMN")} )
    private List<StudentCertification> _listOfCertifications 
= new ArrayList<StudentCertification>();


//== INSTANCE GETTERS AND SETTERS ==========================================
// . . . the rest of all getter and setters

}

NON-Ownining side STUDENTCERTIFICATION:

@Entity

@Table(name = "STUDENTCERTIFICATION")
public class StudentCertification {

//== VARIABLE INSTANCE FIELDS ==============================================

@Id
@GeneratedValue
@Column(name = "CERTIFICATION_ID_COLUMN")
private int _certification_id;

@Column(name = "CERTIFICATION_NAME_COLUMN", nullable = false)
private String _certification_name;

@ManyToMany(cascade = CascadeType.All, mappedBy = "_listOfCertifications")
private List<Student> _listOfStudents;


//== INSTANCE GETTERS AND SETTERS ==========================================

/**
 * @return the _certification_id
 */
public int get_certification_id() {
    return _certification_id;
}

// . . . the rest of all getter and setters 




}

jUnit test:

public class TestHibernateManyToMany {



//== VARIABLE INSTANCE FIELDS ==============================================

/** The list of student and certification */
private List<Student>           listSuccStud    =   new ArrayList<Student>();
private List<StudentCertification>  listSuccCert    =   new ArrayList<StudentCertification>();




/**
 * Sets the testing fixture up.
 *
 * @throws Exception the exception
 */
@Before
public void setUp() throws Exception {
    //code for creation of list of 10 students and list of 10 certifications

}


//== TESTING CLASSES AND METHODS ===========================================




/**
 * Test many to many, the given certification is related to the 10 students and then is saved
 */
@Test
public void testManyToManyBidir() {

    // assign all students for each certification
    for (StudentCertification cer : listSuccCert) {
        cer.set_listOfStudents(listSuccStud);
        Main.safeTheObjectIntoDatabase(cer); //saving certification
    }
}

Main code saving the non-owning side model:

public class Main {

//== TESTING CLASSES AND METHODS ===========================================

/**
 * Test method to save the object into database.
 *
 * @param object the object
 */
public static void safeTheObjectIntoDatabase(Object object) {

    {SessionFactory sessionFactory = HibernateUtil.getSessionFactory();//create factory

        Session session = sessionFactory.openSession();
        Transaction transaction = null;
        try{    
            transaction = session.beginTransaction();
            session.save(object);
            transaction.commit();
        }
        catch (Exception e)
        {
            if(transaction != null)
            {
                transaction.rollback();
            }
            throw new RuntimeException("Pri volani SQL nastala chyba", e);
        }
        finally 
        {
            session.close();
        }
    }//block session factory
}

Hibernate log 1: Table creation

Hibernate: alter table STUDENT_STUDENTCERTIFICATION drop foreign key FK_j6up3gxynxgvtfax9105f0vbs
Hibernate: alter table STUDENT_STUDENTCERTIFICATION drop foreign key FK_tfdgl5fmkt2t1dae8p3fwpal5
Hibernate: drop table if exists STUDENT
Hibernate: drop table if exists STUDENTCERTIFICATION
Hibernate: drop table if exists STUDENT_STUDENTCERTIFICATION
Hibernate: create table STUDENT (STUDENT_ID_COLUMN integer not null auto_increment, NAME_COLUMN varchar(255) not null, primary key (STUDENT_ID_COLUMN))
Hibernate: create table STUDENTCERTIFICATION (CERTIFICATION_ID_COLUMN integer not null auto_increment, CERTIFICATION_NAME_COLUMN varchar(255) not null, primary key (CERTIFICATION_ID_COLUMN))
Hibernate: create table STUDENT_STUDENTCERTIFICATION (STU_ID integer not null, CER_ID integer not null)
Hibernate: alter table STUDENT_STUDENTCERTIFICATION add constraint FK_j6up3gxynxgvtfax9105f0vbs foreign key (CER_ID) references STUDENTCERTIFICATION (CERTIFICATION_ID_COLUMN)
Hibernate: alter table STUDENT_STUDENTCERTIFICATION add constraint FK_tfdgl5fmkt2t1dae8p3fwpal5 foreign key (STU_ID) references STUDENT (STUDENT_ID_COLUMN)

Hibernate log 2: Table insertion and update, !!! no mapping table insert !!!

Hibernate: insert into STUDENTCERTIFICATION (CERTIFICATION_NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENT (NAME_COLUMN) values (?)
Hibernate: insert into STUDENTCERTIFICATION (CERTIFICATION_NAME_COLUMN) values (?)
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
Hibernate: insert into STUDENTCERTIFICATION (CERTIFICATION_NAME_COLUMN) values (?)
Hibernate: update STUDENT set NAME_COLUMN=? where STUDENT_ID_COLUMN=?
... the similar pattern of code, !!! no mapping table STUDENT_STUDENTCERTIFICATION insert !!!
Petr Dostál
  • 321
  • 3
  • 4
  • That's expected. Hibernate/JPA only considers the owning side. It's your responsibility to ensure the object graph is coherent, and in particular, that the owning side of an association is correctly initialized. – JB Nizet Sep 15 '14 at 10:20
  • How to ensure this in the code? What part of code should I modify or add? Thks. – Petr Dostál Sep 15 '14 at 10:37
  • Instead of, or in addition to, calling certification.setStudents(), call student.setCertifications(). – JB Nizet Sep 15 '14 at 10:39
  • Unfortunately, the point of this code is to avoid add certifications to the student (student.setCertifications()), which works well as I wrote. I want to add the Students to the Certification only (certification.setStudents()). So this setter should be modified to ensure proper owning side initiaIization? But how? BTW: I solved the similar problem in 1:N relation ship, see http://stackoverflow.com/questions/16865658/hibernate-one-to-many-relation-foreign-key-always-null/25804077#25804077. – Petr Dostál Sep 15 '14 at 11:16
  • How? By also initializing the owner side. Every time a student is added to a certification, the certification should be added to the student. Why do you make Student the owner side if you only add students to certifications? Why don't you make certification the owning side? – JB Nizet Sep 15 '14 at 11:20
  • Thanks. Problem solved by initializing owning side in the setter of non-ownining side. public void set_listOfStudents(List _listOfStudents) { this._listOfStudents = _listOfStudents; // initalizing the owning side for (Student st : _listOfStudents) { st.addStudentCertification(this); } } – Petr Dostál Sep 15 '14 at 11:46
  • In the owninig side Student.java setter, the certification is add to the list of certifications: public void addStudentCertification(StudentCertification sc) { this._listOfCertifications.add(sc); } – Petr Dostál Sep 15 '14 at 11:57
  • Could you post the link, where the fact that Hibernate/JPA only considers the owning side is mentioned? Hope that this could be helpful for many people. Thanks – Petr Dostál Sep 15 '14 at 12:00
  • I've answered this question countless times, and posted the link countless times. People just don't read documentation, unfortunately. http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#d5e5580 – JB Nizet Sep 15 '14 at 12:17

0 Answers0