mybatis懶載入特性詳解,以及多對多對映詳解
阿新 • • 發佈:2019-02-02
注意講解都在程式碼中
準備資料庫,測試資料,各位自己新增,因為是多對多,所以使用中間表:
用到的實體:
學生類:
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());
}
}
懶載入日誌顯示:
一級查詢
懶載入查詢