使用ibatis映射实体间一对多、多对一的关系

来源:互联网 发布:ubuntu虚拟机镜像 编辑:程序博客网 时间:2024/06/05 02:08

1.需求说明:现有学生类包含学生姓名、年龄、性别、及所在班级这几个属性。注:所在班级是班级类的一个实例,属于自定义类型的属性。下面将介绍如何在ibatis框架的
配置文件中映射这个属性。

2.假设现在需要根据某个学生的编号来获取该学生所在的班级的详细信息,在java中,学生类与班级类是两个类,学生类的"所在班级"这个属性是班级类的一个实例;在dbms
中,学生表的"所在班级"字段引用了班级表的"班级编号"字段。如何在ibatis中映射出这种关系,并使应用程序可以根据某个学生的编号获取他所在的班级的信息?

解决方案一:通过配置resultMap
 首先将班级类的属性与班级表的列通过resultMap节点一一对应起来,如下:
  <sqlMap namespace="s">
  <!--给有可能用到的java bean指定别名-->
  <typeAlias alias="grade" type="com.dongjak.Grade" />
  <typeAlias alias="student" type="com.dongjak.Student" />
  
  <resultMap id="grade-result" class="grade">
   <result property="gradeId" column="grade_id" />
   <result property="gradeName" column="grade_name" />
  </resultMap>
  
 然后定义一个查询语句,输入参数的类型是班级表的主键对应java中的数据类型,如班级表的主键类型是Number(Oracle),在java中就是int,配置如下:
  <statement id="getGrade" parameterClass="java.lang.Integer" resultMap="s.grade-result">
   <![CDATA[SELECT * FROM tb_grade WHERE grade_id=#value#]]>
  </statement>
 接着定义一个resultMap用于映射查询学生信息所返回的结果
  <resultMap id="student-result" class="student">
   <!--映射普通类型的属性,不必多说-->
   <result property="studentId" column="studnet_id" />
   <result property="studentAge" column="studnet_age" />
   <result property="studentSex" column="studnet_sex" />
   <!--关键!这个result节点通过使用select属性将学生类的所在班级这个属性与一个指定的查询的结果匹配,并把这个查询的结果映射成
    一个班级类的实例,也就是学生类的所在班级这个属性。-->
   <result property="grade" column="grade_id" select="s.getGrade"/>
  </resultMap>
 再配置一个select用来根据id查询学生信息就OK了
  <select id="getStudent" parameterClass="java.lang.Integer" resultMap="s.student-result">
   <![CDATA[SELECT student_id AS 'studnetId',student_name AS 'studentName',student_age AS 'studentAge',
       student_sex AS 'studentSex',grade_id FROM tb_student WHERE student_id=#value#
      ]]>
  </select>
  
请注意:在上面的select语句中,除了学生所在班级这个列没有指定别名之外,其他的列的名称都指定成了与它对应的java bean的属性名,这样ibatis会自动将这个列的值
对应到java bean的属性中,然后返回一个新的学生信息类。而学生所在班级这一列尚未指定是因此我们在配置<resultMap>时已经告诉了ibatis-学生类的对象属性
grade由s.getGrade这个查询语句负责构造并初始化。

解决方案二:与方案一概念相同,只不过将繁琐的resultMap配置换成typeAlais节点指定的类别名。


二:ibatis映射集合类型的属性
 1.假设又有一个新的需求,需要根据班级编号获得该班级所有学生的信息的集合。大概情况如下:
  班级类中有一个集合属性List<Student>表示该班级中的所有学生的集合,而数据表中无法表示这种关系,还是上面的实例中一样,学生表引用了班级表。那么,
 如何实现使用ibatis映射这种关系从而完成上面的需求呢?道理其实与上面一样,上面是映射学生与班级之间的多对一的关系,是从学生的角度上发起sql语句的,而
 现在是映射班级与学生之间的一对多的关系,是从班级的角度上发起sql语句的,也就是说,这两种关系的配置实际上只是sql语句的发起者的改变而已。
 
 首先,要根据班级的编号获取从学生表中查询所有属于该班级的学生信息,即student.grade_id=grade,这里涉及到一个问题,在上面的例子中也存在,在上面的例子
   中我们根据班级编号查询该班级的信息,问题在于,这个班级的编号从哪来的?我们在dao层的java代码里面只是执行getStudent这个sql语句,并未涉及到getGrade
 这个语句,那么这个班级编号到底从哪来的呢?首先看上面的例子的执行过程---在我们调用getStudent这个sql语句的时候,ibatis会发起sql语句根据用户指定的
 学生的编号查询这个学生的姓名,年龄,性别,及他所在的班级的编号这些信息并将这些列的值一一映射到一个新的学生类的对应的属性中去。当它映射到学生类的班级
 这个属性时,发现它是个对象类型,在查询出来的数据列中并没有与它匹配的列,这时,它看见了select=s.getGrade这句话,哦!了解了,原来是要将getGrade
 这个查询语句返回的结果映射成这个对象属性,紧接着,ibatis会发起getGrade这个sql语句,在执行这个sql语句时,ibatis又遇到了麻烦,grade_id哪来的?用
 户并没有指定啊!ibatis咣的一声,想到了,刚才查的grade_id干吗的?就是拿来当外键的!其实这个意思最清楚不过了,它查到grade_id这一列,而resultMap
 指定这一列要与grade匹配,但是它们根本不是一回事,但是与getGrade这个sql语句中条件的数据类型相同,于是,结果发生了!
 
 既然知道了是咋回事,那么就开始写代码吧
  首先,还是老规矩,先根据班级的编号从学生表中查询所有属于该班级的学生信息,班级的编号由ibatis自动匹配。
  配置如下:
   <statement id="getAllStudent" parameterClass="int" resultClass="student">
    <![CDATA[select * from tb_student s where s.grade_id=#value#]]>
   </statement>
  然后要配置查询班级信息返回的结果映射了
   <resultMap id="gradeRes" class="grade">
    <result property="gradeId" column="grade_id" />
    <result property="gradeName" column="grade_name" />
    <!--将指定的sql查询得到的结果映射到班级表的学生集合属性中去,column列指定了在执行
     这条sql语句时所需用到的外键。
    -->
    <result property="studentList" column="grade_id" select="g.getAllStudent"/>
   </resultMap>
   
  最后一个sql语句搞定
   <statement id="getGrade" parameterClass="int" resultMap="g.gradeRes">
    <![CDATA[SELECT * FROM tb_grade WHERE grade_id=#value#]]>
   </statement>
 
 我有一点不太明白,为什么在配置一对多的映射时,需要映射一的一方的全部列。在本例中就是,映射班级类到学生类的一对多的关系并配置用于查询班级信息
 返回的结果映射时,需要将班级表所有的字段全部列出,且在sql语句中要写select *而不能只能查部分列。如果只查部分列的话老是话grade_id未找到。郁闷啊,
 求大神指教
 

原创粉丝点击