1. 程式人生 > >mybatis懶載入特性詳解,以及多對多對映詳解

mybatis懶載入特性詳解,以及多對多對映詳解

注意講解都在程式碼中

準備資料庫,測試資料,各位自己新增,因為是多對多,所以使用中間表:
這裡寫圖片描述
用到的實體:
學生類:

public class Student {
    private Integer sid;
    private String name;
    private Integer age;
    //一個學生有多個老師,一個老師有多個學生
    private List<Teacher> teachers=new ArrayList<Teacher>();
        setter..
        getter....
        toString
}

老師類:

public class Teacher {
    private Integer tid;
    private String tname;
    private String subject;
    //一個學生有多個老師,一個老師有多個學生
    private List<Student> students=new ArrayList<Student>();
        setter..
        getter....
        toString
}

sql工具類:

package com.leige.test;

import
java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; /** * @author sqlsession工具類 * */ public class SqlUtils
{
static SqlSessionFactory factory; //靜態載入session工廠 static{ InputStream inputStream; try { inputStream = Resources.getResourceAsStream("com/leige/config/configuration.xml"); //1:例項化sqlsessionfactory工廠 factory= new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { } } public static SqlSession getSession() { try{ //開啟session return factory.openSession(); }catch(Exception e){ throw new RuntimeException(e); } } /** * @param session * @param mapper * @return * 保證會話session一致,所以當做引數傳過來 */ public static Object getmaMapper(SqlSession session,Class mapper){ //註冊對映介面 factory.getConfiguration().addMapper(mapper); //返回操作例項 return session.getMapper(mapper); } }

全域性配置不在全部貼出來了,需要說明的是,使用懶載入需要開啟懶載入開關,還需要日誌檢測懶載入,還需要動態代理的cglib包,以及依賴包asm,和日誌包

開啟懶載入以及掃描包,自動分配名空間,在全域性配置中需加入的配置

<!--  首先在全域性配置中配置支援懶載入 -->

    <settings>  
        <setting name="lazyLoadingEnabled" value="true"/>  
        <setting name="aggressiveLazyLoading" value="false"/>  
          <setting name="logImpl" value="LOG4J"/>
    </settings>  


    <typeAliases>
     <!--    宣告po類別名 -->
        <typeAlias alias="Student" type="com.leige.domain.Student"/>
<!--         宣告組合查詢類 -->
       <!--  <typeAlias alias="StudentQueryVo" type="com.leige.domain.StudentQueryVo" />
        <typeAlias alias="Teacher" type="com.leige.domain.Teacher"/> -->
<!--         配置多個掃描,預設類的簡單名稱,不區分大小寫 -->
        <package name="com.leige.domain"/>
    </typeAliases>

日誌配置:

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
#log4j.logger.org.mybatis.example.BlogMapper=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

Student介面對映:



<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--  名稱空間就是實現sql的隔離,把對於一個物件的操作sql語句放在一個名稱空間中
 這裡的呼叫查詢就是Student.selectStudentById -->
<mapper namespace="Student">

<!-- 根據學生id查詢學生資訊及所有老師資訊, 
當然大家也可以不使用巢狀查詢,也可以直接使用三表查詢,效率較低,
我是直接複製過來的
注意sql小問題哦
      SELECT student.*,teacher.* FROM  student,t_s,teacher 
      WHERE student.`sid`=t_s.`sid` AND teacher.`tid`=t_s.`tid` AND t_s.sid=1 -->
<!--       定義結果map -->
      <resultMap type="Student" id="Student_Teacher">
            <id column="sid" property="sid"/>
          <result column="name" property="name"/>
          <result column="age" property="age"/>

      <collection property="teachers" ofType="Teacher">
         <!--  property表示集合屬性,ofType表示集合型別 -->
             <id column="tid" property="tid"/>
          <result column="tname" property="tname"/>
          <result column="subject" property="subject"/>
      </collection>
      <!-- 
      association的使用和collection類似,多用於一對一對映或者一對多一方的resultMap的輸出對映
              <association property=""></association> -->
          </resultMap>
          <!-- 懶載入 ,由於是多對多,所以需要加查詢中間表,所以,不能直接使用單表id查詢-->
<select id="selectStudentLazy" parameterType="int" resultType="Student" >

        <![CDATA[  
          SELECT student.* FROM student,t_s
           WHERE 
           t_s.sid=student.sid AND t_s.tid=#{value}
          ]]>

</select>
<!-- 多對多級聯查詢,其實個人感覺,mybatis的對映關係主要體現在資料庫設計和sql語法上面如果資料庫基礎知識過硬,沒必要花費時間去學習 -->
<select id="select_student_teacher" parameterType="int" resultMap="Student_Teacher">
               <![CDATA[  
       SELECT tt.*,teacher.`tname`,teacher.`subject` FROM teacher,
    (SELECT student.*,t_s.`tid` FROM student,t_s WHERE student.`sid`=t_s.`sid`) tt 
    WHERE 
    tt.tid=teacher.`tid`  AND sid=#{value} ]]>
</select>
</mapper>

Teacher介面對映,大家可以對比下,使用懶載入和未使用懶載入時resultMap的區別,仔細思考就會發現其實很簡單

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  <mapper namespace="Teacher">
<!--     未使用懶載入時的resultMap -->
      <resultMap type="Teacher" id="Teacher_Student">
          <id column="tid" property="tid"/>
          <result column="tname" property="tname"/>
          <result column="subject" property="subject"/>
      <collection property="students" ofType="Student">
         <!--  property表示集合屬性,ofType表示集合型別 -->
          <id column="sid" property="sid"/>
          <result column="name" property="name"/>
          <result column="age" property="age"/>
      </collection>
      <!-- 
      association的使用和collection類似,多用於一對一對映或者一對多一方的resultMap的輸出對映
              <association property=""></association> -->
          </resultMap>


        <select id="selectTeahcerById" parameterType="int" resultType="Teacher" >
          <![CDATA[
          select * from teacher where id=#{value}
          ]]>
      </select>


      <!-- 未使用懶載入時,聯合查詢關鍵是sql語句的調優,
      這裡簡單介紹一下mybatis的物件對映關係,其實學習mybatis的物件對映,個人大家不必花費時間去學習,
      因為都是相通的,主要是資料庫表的設計,具體的查詢對映,主要體現在sql語法上,資料庫知識過硬,沒有學習的必要,
      這裡由於本人資料庫知識有限,抱歉沒有優化,能查出來,我就很高興了
      當然大家不使用巢狀查詢,也可以直接使用三表查詢,效率較低
      SELECT student.*,teacher.* FROM  student,t_s,teacher 
      WHERE student.`sid`=t_s.`sid` AND teacher.`tid`=t_s.`tid` AND t_s.sid=1
       -->
      <select id="selectNoLazy" parameterType="int" resultMap="Teacher_Student">
            <![CDATA[
        SELECT tt.*,student.name,student.age FROM 
(SELECT teacher.*,t_s.sid FROM teacher,t_s WHERE teacher.tid=t_s.tid) tt,student
WHERE tt.sid=student.sid AND tid=#{value}
          ]]>
      </select>

      <!--       mybatis使用懶載入需使用resultMap,在resultMap中有collections和associatation標籤都支援懶載入
resultType不可以將屬性列和集合對映,所以需要使用resultMap
這裡最重要的就是懶載入的實現,mybatis對於懶載入的實現就是延遲使用子查詢的發出,這與hibernate的機制是一樣的
所以子查詢是懶載入的靈魂,
下面是定義懶載入的resultMap,懶載入和非懶載入的resultMap的區別就是關聯對映使用的是子查詢
,子查詢呼叫的時外部Student中的懶載入子查詢,需要加上名稱空間
 -->
 <resultMap type="Teacher" id="Teacher_Student_lazy">
        <id column="tid" property="tid"/>
          <result column="tname" property="tname"/>
          <result column="subject" property="subject"/>
 <!--      select:基於column列發出的子查詢語句,即SELECT student.* FROM student where sid=?;
 column:就是與select相關聯的另一個表中的列
 例如這裡完整的查詢是:
   父查詢
   SELECT * FROM teacher WHERE tid=?
   子查詢根據父查詢的tid再去查詢學生表,因為是多對多,所以需要查詢中間表
   SELECT student.* FROM student,t_s
           WHERE 
           t_s.sid=student.sid AND t_s.tid=#{value}-->
      <collection property="students" ofType="Student" select="Student.selectStudentLazy" column="tid" >
       <!-- 用於懶載入關聯的查詢 -->


      </collection>
 </resultMap>   

 <select id="selectTeacherLazy" parameterType="int" resultMap="Teacher_Student_lazy">
<!--      由於這是多對多對映,所以查詢多關聯關係,需要藉助中間表,所以使用resultMap -->
       <![CDATA[
      select * from teacher where tid=#{value}
       ]]>
 </select>
  </mapper>

多對多對映詳解
這裡簡單介紹一下mybatis的物件對映關係,其實學習mybatis的物件對映,個人大家不必花費時間去學習,
因為都是相通的,主要是資料庫表的設計,具體的查詢對映,主要體現在sql語法上,資料庫知識過硬,沒有學習的必要,多對多對映的解釋請檢視測試程式碼具體感受下:
測試程式碼

package com.leige.test;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import com.leige.domain.Student;
import com.leige.domain.Teacher;

public class App2 {
    @Test
    public void testSelectLazy(){
        //獲取session
        SqlSession session=SqlUtils.getSession();
        //查詢
        Teacher teacher=session.selectOne("Teacher.selectTeacherLazy", 1);
        //列印
        //測試懶載入時可以在此處打斷點,就會發現只有在使用子集合時才會載入
        System.out.println(teacher);
        //真正載入的位置
        System.out.println(teacher.getStudents());
    }
    /**
     * 測試立即載入,實現多對多,獲取實現
     */
    @Test
    public void testSelectNoLazy(){
        //獲取session
                SqlSession session=SqlUtils.getSession();
                /*//根據id查詢老師資訊,以及老師教的所有學生的資訊,
                當然這種例子是不合理的,現實中要麼是學生單項關聯老師,
                或者以班級為單位,老師學生都關聯班級,但是這裡只是演示多對多以及懶載入,至於物件和具體問題的處理還需要大家自己設計*/
                Teacher teacher=session.selectOne("Teacher.selectNoLazy", 1);
                //列印
                System.out.println(teacher);
                System.out.println(teacher.getStudents());
                System.out.println("---------------------------我是分割線-------------------");
                //根據學生id查詢學生資訊,以及學生的所有老師資訊
                Student student=session.selectOne("Student.select_student_teacher",1);
                //輸出
                System.out.println(student);
                System.out.println(student.getTeachers());
    }
}

懶載入日誌顯示:
一級查詢
這裡寫圖片描述
懶載入查詢
這裡寫圖片描述