-->
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.  [ 5 posts ] 
Author Message
 Post subject: Concept help | Association mapping
PostPosted: Tue Jun 24, 2008 11:16 am 
Newbie

Joined: Tue May 24, 2005 1:14 am
Posts: 6
Hi
I have an issue and I was hoping someone could help.
Suppose I have three tables, say

Code:
STUDENT -> student_id, student_name
SUBJECT -> subject_id, subject_name
STUD_SUB ->student_id, subject_id, score (association table)


My problem is that I need to have a list of Subjects for each Student and their score in that subject,
something like
Code:
Map<Subject, Integer>
in the Student object.

The mapping needs to be uni-directional, i.e, from Student to Subjects.
Can somebody help pls?

Thanks in advance,
Rajiv


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 24, 2008 12:23 pm 
Expert
Expert

Joined: Tue May 13, 2008 3:42 pm
Posts: 919
Location: Toronto & Ajax Ontario www.hibernatemadeeasy.com
I've seen this challenge come up a few times before.

Check out this thread on JavaRanch. I believe it is exactly the same challenge that you are facing:

http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=78&t=003773

I believe a bit of a solution can be found here:

http://java-aap.blogspot.com/2006/04/hibernate-annotations-composite.html

Let us know how you fare!

_________________
Cameron McKenzie - Author of "Hibernate Made Easy" and "What is WebSphere?"
http://www.TheBookOnHibernate.com Check out my 'easy to follow' Hibernate & JPA Tutorials


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 24, 2008 5:06 pm 
Beginner
Beginner

Joined: Wed Jun 11, 2008 4:43 am
Posts: 20
I don't know if it is the best way to do this, but I have a quite elegant solution to this problem... And, it seems to work properly! Of course, it is not as elegant as if Hibernate could manage it natively...
Sorry not to have added comments in my code but there is not so much to understand...

First, the Subject class and mapping file which are very simple, nothing special in there:
Code:
package mypackage;

public class Subject {

   private Long subject_id;
   
   private String subject_name;
   
   public Long getSubject_id() {
      return subject_id;
   }

   public void setSubject_id(Long subject_id) {
      this.subject_id = subject_id;
   }

   public String getSubject_name() {
      return subject_name;
   }

   public void setSubject_name(String subject_name) {
      this.subject_name = subject_name;
   }

}


Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="mypackage">
  <class name="Subject">
     <id name="subject_id">
        <generator class="increment"></generator>
     </id>
     <property name="subject_name"></property>
  </class>
</hibernate-mapping>


For the student, we need a bag to hold the student's scores. I've mapped the bag on a list. The getScore method go through the list and gets the StudentScore (see below) that matches the Subject and returns the score, like in a Map<Subject,Integer>. You can try to implement such a map if you like...
Code:
package mypackage;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Student {

   private Long student_id;
   
   private String student_name;

   private List<StudentScore> scores = new LinkedList<StudentScore>();

   public Long getStudent_id() {
      return student_id;
   }

   public void setStudent_id(Long student_id) {
      this.student_id = student_id;
   }

   public String getStudent_name() {
      return student_name;
   }

   public void setStudent_name(String student_name) {
      this.student_name = student_name;
   }

   public List<StudentScore> getScores() {
      return scores;
   }

   public void setScores(List<StudentScore> scores) {
      this.scores = scores;
   }
   
   public int getScore(Subject s) {
      Iterator<StudentScore> iter = scores.iterator();
      StudentScore result = null;
      while((iter.hasNext()) && ((result==null) || (result.getSubject()!=s))) {
         result = iter.next();
      }
      if ((result!=null) && (result.getSubject()==s)) {
         return result.getScore();
      }
      return -1;
   }
}


Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="mypackage">
  <class name="Student">
     <id name="student_id">
        <generator class="increment"></generator></id>
     <property name="student_name"></property>
     <bag name="scores">
        <key column="student_id"></key>
        <one-to-many class="StudentScore" ></one-to-many>
        
     </bag>
  </class>
</hibernate-mapping>


Now the association table. I had to declare a composite primary key. Thus, the StudentScore class has to implement Serializable.
I also added a reference to the subject by reusing the subject_id column. Thus I had to add insert="false" and update="false" in the mapping.
Code:
package mypackage;

import java.io.Serializable;

public class StudentScore implements Serializable {

   static final long serialVersionUID = 1L;

   private Long student_id;
   
   private Long subject_id;
   
   private int score;
   
   private Subject subject;

   public int getScore() {
      return score;
   }

   public void setScore(int score) {
      this.score = score;
   }

   public Long getStudent_id() {
      return student_id;
   }

   public void setStudent_id(Long student_id) {
      this.student_id = student_id;
   }

   public Long getSubject_id() {
      return subject_id;
   }

   public void setSubject_id(Long subject_id) {
      this.subject_id = subject_id;
   }

   public Subject getSubject() {
      return subject;
   }

   public void setSubject(Subject subject) {
      this.subject = subject;
   }

}


Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="mypackage">
  <class name="StudentScore">
     <composite-id>
        <key-property name="student_id"></key-property>
        <key-property name="subject_id"></key-property>
     </composite-id>
     <property name="score"></property>
     <many-to-one name="subject" column="subject_id" class="Subject"
        insert="false" update="false">
     </many-to-one>
  </class>
</hibernate-mapping>


And here is a short example code to test all this:
Code:
   public void testStudents() {
      // Create the DB and populate it!
      HibernateUtil hibernate = new HibernateUtil(true);
      Session session = hibernate.getSessionFactory().getCurrentSession();
      try {
         session.beginTransaction();
         // Create a student
         Student student = new Student();
         student.setStudent_name("STUDENT1");
         session.save(student);
         // Create some subjects
         Subject[] subjects = new Subject[10];
         for (int i=0;i<subjects.length;i++) {
            subjects[i] = new Subject();
            subjects[i].setSubject_name("SUBJECT"+i);
            session.save(subjects[i]);
            // Give a score for the student in the newly created Subject
            StudentScore score = new StudentScore();
            score.setStudent_id(student.getStudent_id());
            score.setSubject_id(subjects[i].getSubject_id());
            score.setScore(i);
            score.setSubject(subjects[i]);
            session.save(score);
         }
         session.getTransaction().commit();
      } catch (HibernateException e) {
         e.printStackTrace();
         session.getTransaction().rollback();
      } finally {
         hibernate.getSessionFactory().close();
      }
      // Now try to load and use the data
      hibernate = new HibernateUtil(true);
      session = hibernate.getSessionFactory().getCurrentSession();
      try {
         session.beginTransaction();
         List<Subject> subjects = session.createQuery("from Subject").list();
         List<Student> students = session.createQuery("from Student").list();
         // Try it with the getScore method
         for (Iterator<Student> iter = students.iterator();iter.hasNext();) {
            Student student = iter.next();
            for (Iterator<Subject> iter2 = subjects.iterator();iter2.hasNext();) {
               Subject subject = iter2.next();
               logger.debug("Student: "+student.getStudent_name()+" has "+student.getScore(subject)+" in "+subject.getSubject_name());
            }
         }
         // Just show all the date in the DB
         for (Iterator<Student> iter = students.iterator();iter.hasNext();) {
            Student student = iter.next();
            logger.debug("Student Name:"+student.getStudent_name());
            for (Iterator<StudentScore>iter2 = student.getScores().iterator();iter2.hasNext();) {
               StudentScore score = iter2.next();
               logger.debug("Subject: "+score.getSubject().getSubject_name()+" Score: "+score.getScore());
            }
         }
         session.getTransaction().commit();
      } catch (HibernateException e) {
         e.printStackTrace();
         session.getTransaction().rollback();
      } finally {
         hibernate.getSessionFactory().close();
      }
   }


I don't know if it is the best way, I'm quite a newby in Hibernate, but it seems to work in this case (no bi-directional relations).
Don't hesitate if you have any comment on this...
Enjoy!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 25, 2008 6:15 am 
Newbie

Joined: Tue May 24, 2005 1:14 am
Posts: 6
Thanks, Cameron and xtophe31. That helped.

I ended up doing something similar to xtophe31's solution.

I'd created a new StudentSubject class and mapped it to a set in both Student and Subject.

I'm not sure if this is the best way to go.. Comments welcome!

Student.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="mypackage">
   <class name="Student">
      <id name="student_id">
         <generator class="increment" />
      </id>
      <property name="student_name" />
      <set name="scores">
         <key column="student_id" />
         <one-to-many class="StudentSubject" />     
      </set>
   </class>
</hibernate-mapping>



Subject.hbm.xml

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="mypackage">
  <class name="Subject">
     <id name="subject_id">
        <generator class="increment" />
     </id>
     <property name="subject_name" />
     <set name="scores">
             <key column="subject_id" />
             <one-to-many class="StudentSubject" />     
     </set>
  </class>
</hibernate-mapping>


StudentSubject.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="mypackage">
<class name="StudentScore">
   <!-- Random id -->
   <id name="id" column="ID" type="int" access="field">
      <generator class="sequence">
         <param name="sequence">id_seq</param>
      </generator>
   </id>
   <property name="score"></property>
   <many-to-one name="studentId" class="Student" fetch="join">
      <column name="student_id" not-null="true" unique="true"/>
   </many-to-one>
   <many-to-one name="subjectId" class="Subject" fetch="join">
      <column name="subject_id" not-null="true" unique="true"/>
   </many-to-one>
</class>
</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 25, 2008 10:26 am 
Expert
Expert

Joined: Tue May 13, 2008 3:42 pm
Posts: 919
Location: Toronto & Ajax Ontario www.hibernatemadeeasy.com
Awesome!

I'm going to bookmark this post and use it as a reference as well. Thanks for posting back your solution. Trust me, I see this problem often. Having another solution will really help out.

I'm actually thinking of even documenting it with annoations in the next printing of my book. It's a challenge, but it's a good example of what Hibernate can do it you bend it in the right ways.

_________________
Cameron McKenzie - Author of "Hibernate Made Easy" and "What is WebSphere?"
http://www.TheBookOnHibernate.com Check out my 'easy to follow' Hibernate & JPA Tutorials


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 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.