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.

Reply via email to