ibatis-user-java mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Ingmar Lötzsch <iloetz...@asci-systemhaus.de>
Subject Re: 2 Way 1-1 relationship
Date Thu, 22 Jan 2009 11:12:51 GMT
> Actually I was simplifying the context from 1-M to 1-1 for discussion so 
> left groupBy attribute mistakenly.
> 
> The problem at hand is that Teacher has students (1:M). Student must 
> have some teacher assigned to him but teacher does not necessarily need 
> to have some student.
> 
> So I go ahead and load some teacher and it will load all students 
> associated with it as Teacher would have List<Student>. But what I want 
> is that student also have a reference to Teacher. Following code would 
> explain what I want:
> 
> class Teacher {
>    List<Student> students;
>    ....
> }
> 
> class Student {
>    Teacher teacher;
>    ....
> }
> 
> SQL Map config file
> 
> <resultMap id="TeacherResultMap" class="Teacher" groupBy="id" >
>     <result property="id" />
>     <result property="name" />   
>     <result property="students" resultMap="JOB.StudentResultMap"/>
> </resultMap>
> 
> <resultMap id="StudentResultMap" class="Student" >
>   <result property="id" column="stu_id"/>
>   <result property="name" column="stu_Name"/> 
>   <result property="teacher" resultMap="JOB.TeacherResultMap"/>   
> </resultMap>
> 
> 
> <select id="findTeacher"  parameterClass="int" 
> resultMap="TeacherResultMap11">
>     select t.id  , t.name , s.id as stu_id, s.name as stu_Name , s.tid 
> as stu_tid from Teacher t left join student s on t.id = s.tid where t.id 
> = #value#
> </select>
> 
> The line in red would make cyclic reference to associate teacher with 
> student and stack overflow so I can't do that but effectively I want to 
> do that.
> 
> I understand your point where you tell me I could associate teacher for 
> each student in business logic but I am wondering if there is any way 
> that both sides of relationship could hold a reference of other side of 
> relationship implicitly.

OK. As far as I know iBATIS cannot be used to make the association 
navigable in both directions without additional own code.

You should remove the "teacher" property from the StudenResultMap and 
after getting the Teacher instance iterate over the list of Student 
instances to invoke the setTeacher() method. The use of a RowHandler is 
an alternative.

For further discussion I share my experience. Our team prefere use 
LinkedHashMap<Integer, Student> instead of List<Student>. The advantage 
of the map is that you can get the student by ID without iterating. The 
ID is very often returned from some GUI component. We use LinkedHashMap 
instead of HashMap to keep the order and to sort the elements of the map 
when indicated. In some cases we use TreeMap, TreeMap<Date, ...> for 
example. To avoid LEFT JOINs and reduce the amount of statements needed 
to load complex objects in several variants we are loading objects from 
each table in a separate step. In your case this would look like.

<sqlMap namespace="teacher">

   <resultMap id="result" class="Teacher">
     <result property="id" />
     <result property="name" />
   </resultMap>

   <select id="select" parameterClass="int" resultMap="result">
     SELECT
       id,
       name
     FROM teacher
     WHERE id = #value#
   </select>
</sqlMap>

<sqlMap namespace="student">

   <resultMap id="result" class="Student" >
     <result property="id" />
     <result property="name" />
   </resultMap>

   <select id="selectByTeacher" parameterClass="int" resultMap="result">
     SELECT
       id,
       name
     FROM student
     WHERE tid = #value#
   </select>

</sqlMap>

StudentDAOImpl
{
	public List<Student> selectByTeacher(Integer teacherId)
	{
		return List<Student> 
getSqlMapClientTemplate().queryForList("student.selectByTeacher", 
teacherId)	
	}
}

TeacherDAOImpl
{
	public Teacher select(Integer id)
	{
		return (Teacher) 
getSqlMapClientTemplate().queryForList("teacher.select", id)	
	}
}

Integer teacherId = ...
Teacher teacher = this.teacherDAO.select(teacherId);
List<Student> studentList = this.studentDAO.selectByTeacher(teacherId);
Map<Integer, Student> studentMap = teacher.getStudentMap();
for (Student student : studentList)
{
	student.setTeacher(teacher);
	studentMap.put(student.getId(), student);
}

This is simplified because allows to query for 1 teacher only.

Mime
View raw message