天天看點

Mybatis入門操作一、Mybatis入門二、MyBatis開發DAO三、mybatis-config.xml四、mapper.xml五、關聯查詢六、延遲加載

Mybatis入門操作一、Mybatis入門二、MyBatis開發DAO三、mybatis-config.xml四、mapper.xml五、關聯查詢六、延遲加載

一、Mybatis入門

1. jdbc程式設計問題總結

1.1 jdbc程式

@Override
public void insertUser(User user){
    Connection conn = DBUtil.getConnection();
    PreparedStatement stmt = null;
    String sql = "insert into sys_user(NAME,ACCT,PWD,CRTIME,UPTIME) values(?a,?c,?d,now(),now())";
    try {
        stmt = conn.prepareStatement(sql);
        stmt.setString(1, user.getName());
        stmt.setString(2, user.getAcct());
        stmt.setString(3, user.getPwd());
        stmt.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        DBUtil.closeAll(conn, stmt, null);
    }
}
           

1.2 jdbc程式設計步驟

  1. 加載資料庫驅動
  2. 建立并擷取資料庫連結
  3. 建立PreparedStatement對象
  4. 設定Sql語句中的占位符參數
  5. 通過PreparedStatement執行Sql并擷取結果
  6. 對Sql執行結果進行解析處理
  7. 釋放資源(Connection、Preparedstatement、ResultSet)

1.3 jdbc問題總結如下

  1. 資料庫連結建立、釋放頻繁造成系統資源浪費進而影響系統性能,如果使用資料庫連結池可解決此問題
  2. Sql語句在代碼中寫死,造成代碼不易維護,實際應用Sql變化的可能較大,Sql變動需要改變java代碼
  3. 使用PreparedStatement向占位符号傳參數存在寫死,因為Sql語句的where條件不一定,可能多也可能少,修改Sql還要修改代碼,系統不易維護
  4. 對結果集解析存在寫死(查詢列名),Sql變化導緻解析代碼變化,系統不易維護,如果能将資料庫記錄封裝成pojo對象解析比較友善

2. MyBatis介紹

  MyBatis本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation遷移到了google code,并且改名為MyBatis,實質上MyBatis對iBatis進行一些改進

  MyBatis是一個優秀的持久層架構,它對jdbc的操作資料庫的過程進行封裝,使開發者隻需要關注 SQL 本身,而不需要花費精力去處理例如注冊驅動、建立Connection、建立Statement、手動設定參數、結果集檢索等jdbc繁雜的過程代碼

  Mybatis通過xml或注解的方式将要執行的各種Statement(Statement、PreparedStatemnt、CallableStatement)配置起來,并通過java對象和Statement中的Sql進行映射生成最終執行的Sql語句,最後由MyBatis架構執行Sql并将結果映射成java對象并傳回

3. MyBatis架構

Mybatis入門操作一、Mybatis入門二、MyBatis開發DAO三、mybatis-config.xml四、mapper.xml五、關聯查詢六、延遲加載
  1. MyBatis配置檔案mybatis-config.xml(名稱不固定),此檔案作為MyBatis的全局配置檔案,配置了MyBatis的運作環境等資訊。mapper.xml檔案即Sql映射檔案,檔案中配置了操作資料庫的Sql語句。此檔案需要在mybatis-config.xml中加載
  2. 通過MyBatis環境等配置資訊構造SqlSessionFactory即會話工廠
  3. 由會話工廠建立SqlSession即會話,操作資料庫需要通過SqlSession進行
  4. MyBatis底層自定義了Executor執行器接口操作資料庫,Executor接口有兩個實作,一個是基本執行器、一個是緩存執行器
  5. Mapped Statement也是MyBatis一個底層封裝對象,它包裝了MyBatis配置資訊及Sql映射資訊等。mapper.xml檔案中一個Sql對應一個Mapped Statement對象,Sql的id即是Mapped statement的id
  6. Mapped Statement對Sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行Sql前将輸入的java對象映射至Sql中,輸入參數映射就是jdbc程式設計中對PreparedStatement設定參數
  7. Mapped Statement對Sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql後将輸出結果映射至java對象中,輸出結果映射過程相當于jdbc程式設計中對結果的解析處理過程

4. 搭建MyBatis項目

4.1 建立Java項目

  MyBatis是一個持久層架構,操作資料庫時使用的。無需建立JavaWEB項目,建立Java項目即可

4.2 導入MyBatis架構jar包

Mybatis入門操作一、Mybatis入門二、MyBatis開發DAO三、mybatis-config.xml四、mapper.xml五、關聯查詢六、延遲加載
  • mybatis-3.4.6.jar為核心jar包,必須引入
  • lib目錄下的jar包為工具包,可有可無
  • mysql或oracle資料庫的驅動包,必須引入

maven依賴配置

<dependencies>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>
    <!--  mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
    </dependency>
    <!-- log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
           

4.3 編寫MyBatis中全局配置檔案

  作用:配置了資料源、事務等MyBatis運作環境等

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 配置環境:與資料庫的連接配接資訊
	    此配置了解即可,後期與spring架構整合之後,此配置會消失
	 -->
    <environments default="mysql">
        <environment id="oracle">
            <!-- 配置事務:使用jdbc的事務管理 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置資料源:連接配接資料庫的資訊
                type : 表示連接配接是否使用連接配接池,POOLED表示mybatis中自帶的連接配接池
             -->
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
                <property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
                <property name="username" value="tom"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
           

4.4 編寫實體類

public class Emp {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    //get,set方法省略...
}
           

4.5 編寫映射檔案

<?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">
<!--
    namespace : 命名空間,作用是mapper檔案進行分類管理
    注意:如果使用mapper代理的方式進行開發,namespace有特殊的作用
-->
<mapper namespace="emp">
    <!-- 在映射檔案中編寫sql語句 -->
    <!-- 1.通過主鍵查詢員工資訊 -->
    <!--
        通過<select>标簽編寫查詢語句
        id : 映射檔案中SQL語句的唯一辨別
             mybatis會将SQL語句封裝到Mapped Statement對象中,是以此處的id也可以辨別Mapped Statement對象的id
             注意:同一個mapper檔案中id不能重複,而且id在mapper代理模式下有着重要作用
        parameterType : 輸入參數的類型
            sql語句的占位符:#{}
            #{empno}:其中empno表示接收輸入的參數值,參數名稱為empno
                但是如果參數的類型為簡單類型(基本資料類型+包裝類+字元串類型),參數名稱可以任意指定
       resultType : 輸出參數的類型
                    需要指定輸出資料為Java中的資料類型(實體類的全限定名)
    -->
    <select id="findEmpById" parameterType="java.lang.Integer" resultType="com.mybatis.entity.Emp">
      select empno,ename,job,mgr,hiredate,sal,comm from emp where empno=#{abc}
    </select>
</mapper>
           

4.6 加載映射檔案

  在MyBatis中全局配置檔案中添加映射檔案位置

<!-- 加載映射檔案的位置 -->
<mappers>
    <mapper resource="com/mybatis/entity/Emp.xml"/>
</mappers>
           

4.7 編寫測試程式

//1.建立讀取全局配置檔案的流
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

//2.通過配置檔案流建立會話工廠
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

//3.通過會話工廠建立會話對象(SqlSession)
SqlSession session = factory.openSession();

/*
4.通過會話對象操作資料庫
selectOne(String statementId, Object param)查詢單條資料
參數1:映射檔案中的statementId,命名空間名.statementId
參數2:向sql語句中傳入的資料,注意:傳入的資料類型必須與映射檔案中配置的parameterType保持一緻
傳回值:就是映射檔案中配置的resultType的類型
*/
Emp emp = session.selectOne("emp.findEmpById",8001);
System.out.println(emp);
//5.關閉資源
session.close();
           

5. 增删改查的基本操作

5.1 查詢操作

mapper檔案:

<!--
        查詢到資料傳回多條記錄,每一條封裝在一個實體類對象中,所有的實體類對象封裝在List集合中
        resultType :指定的并不是集合的類型,而是單條資料所對應實體類類型
        resultType="java.util.List" 錯誤的配置方式
    -->
    <select id="findEmp" resultType="com.mybatis.entity.Emp">
      select empno,ename,job,mgr,hiredate,sal,comm from emp order by empno desc
    </select>
           

Java代碼:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
List<Emp> list = session.selectList("emp.findEmp");
for (Emp e : list) {
    System.out.println(e);
}
session.close();
           

5.2 新增操作

mapper檔案:

<insert id="insertEmp" parameterType="com.mybatis.entity.Emp" >
    insert into emp(empno,ename,job,mgr,hiredate,sal,comm)
    values(#{empno},#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm})
</insert>
           

Java代碼:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();

Emp emp = new Emp();
emp.setEname("tom");
emp.setJob("clrek");
emp.setMgr(1);
emp.setHiredate(new Date());
emp.setSal(6500.0);
emp.setComm(1200.0);

//在mybaits中事務預設需要手動送出
int result = session.insert("emp.insertEmp",emp);
System.out.println(result);
//執行送出的方法
session.commit();
session.close();
           

插入資料的主鍵傳回:

  • select last_insert_id(),表示得到剛insert進去記錄的主鍵值,适用與自增主鍵的資料庫
  • select seq_demo.nextval from dual,表示擷取下一個序列生成的值,适用于存在序列的資料庫
  • keyProperty:将查詢到主鍵值設定到parameterType指定的對象的哪個屬性
  • order:selectKey标簽中Sql語句,相對于insert語句來說的執行順序
  • resultType:指定selectKey标簽中Sql語句的結果類型
<!--mysql-->
<selectKey resultType="java.lang.Integer" keyProperty="empno" order="AFTER">
    select last_insert_id()
</selectKey>
<!--oracle-->
<selectKey resultType="java.lang.Integer" keyProperty="empno" order="BEFORE">
    select seq_demo.nextval from dual
</selectKey>
           

如果是主鍵自增型資料庫還可以使用:

5.3 修改操作

mapper檔案:

<update id="updateEmp" parameterType="com.mybatis.entity.Emp">
    update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},sal=#{sal},comm=#{comm}
    where empno=#{empno}
</update>
           

Java代碼:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();

Emp emp = new Emp();
emp.setEmpno(52);
emp.setEname("jerry");
emp.setJob("manager");
emp.setMgr(10);
emp.setHiredate(new Date());
emp.setSal(8500.0);
emp.setComm(1200.0);

int result = session.update("emp.updateEmp",emp);
System.out.println(result);
//執行送出的方法
session.commit();
session.close();
           

5.4 删除操作

mapper檔案:

<delete id="deleteEmp" parameterType="java.lang.Integer">
    delete from emp where empno=#{empno}
</delete>
           

Java代碼:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();

int result = session.delete("emp.deleteEmp",8000);
System.out.println(result);
//執行送出的方法
session.commit();
session.close();
           

5.5 條件查詢

mapper檔案:

<!--
    #{}占位符,需要在Java中将傳入資料的前後拼接%符号
    ${}Sql語句拼接,在其中隻能寫value
-->
<select id="findEmpByEname" parameterType="java.lang.String" resultType="com.mybatis.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where ename like #{abc}
</select>

<select id="findEmpByEname2" parameterType="java.lang.String" resultType="com.mybatis.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where ename like '%${value}%'
</select>
           

Java代碼:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();

List<Emp> list = session.selectList("emp.findEmpByEname", "%tom%");
//List<Emp> list = session.selectList("emp.findEmpByEname", "tom");
for (Emp e : list) {
    System.out.println(e);
}

session.close();
           

5.6 總結

5.6.1 parameterType和resultType

  • parameterType:指定輸入參數類型,MyBatis通過ognl從輸入對象中擷取參數值設定在Sql中
  • resultType:指定輸出結果類型,MyBatis将Sql查詢結果的一行記錄資料映射為resultType指定類型的對象

5.6.2 #{} 和 ${}

  • #{}表示一個占位符号,#{}接收輸入參數,類型可以是簡單類型、pojo、HashMap

    #{}接收簡單類型,#{}中可以寫成value或其它名稱

    #{}接收pojo對象值,通過OGNL讀取對象中的屬性值,通過屬性.屬性.屬性…的方式擷取對象屬性值

  • ${}表示一個拼接符号,會引用Sql注入,是以不建議使用${}

    ${}接收輸入參數,類型可以是簡單類型、pojo、HashMap

    ${}接收簡單類型,${}中隻能寫成value

    ${}接收pojo對象值,通過OGNL讀取對象中的屬性值,通過屬性.屬性.屬性…的方式擷取對象屬性值

5.6.3 selectOne和selectList

  • selectOne表示查詢出一條記錄進行映射

    如果使用selectOne可以實作使用selectList也可以實作(list中隻有一個對象)

  • selectList表示查詢出一個清單(多條記錄)進行映射

    如果使用selectList查詢多條記錄,不能使用selectOne

    如果使用selectOne報錯:

    org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

6. MyBatis和Hibernate本質差別和應用場景

  • Hibernate:是一個标準ORM架構(對象關系映射)

    入門門檻較高的,不需要程式寫Sql,Sql語句自動生成,對sql語句進行優化、修改比較困難的

    應用場景:适用與需求變化不多的中小型項目,比如:背景管理系統,erp、crm、oa…

  • MyBatis:專注是Sql本身,需要程式員自己編寫Sql語句,Sql修改、優化比較友善

    MyBatis是一個不完全的ORM架構,雖然程式員自己寫Sql,MyBatis也可以實作映射(輸入映射、輸出映射)

    應用場景:适用與需求變化較多的項目,比如:網際網路項目

  • 企業進行技術選型,以低成本高回報作為技術選型的原則,根據項目組的技術力量進行選擇

二、MyBatis開發DAO

1. MyBatis API

1.1 SqlSessionFactoryBuilder

  SqlSessionFactoryBuilder用于建立SqlSessionFacoty,SqlSessionFacoty一旦建立完成就不需要SqlSessionFactoryBuilder了,因為SqlSession是通過SqlSessionFactory生産,是以可以将SqlSessionFactoryBuilder當成一個工具類使用,最佳使用範圍是方法範圍即方法體内局部變量

1.2 SqlSessionFactory

  SqlSessionFactory是一個接口,接口中定義了openSession的不同重載方法,SqlSessionFactory的最佳使用範圍是整個應用運作期間,一旦建立後可以重複使用,通常以單例模式管理SqlSessionFactory

1.3 SqlSession

  SqlSession是一個面向使用者(程式員)的接口,其中提供了很多操作資料庫的方法。如:selectOne(傳回單個對象)、selectList(傳回單個或多個對象)、insert、update、delete。

  SqlSession的執行個體不能共享使用,它是線程不安全的,每個線程都應該有它自己的SqlSession執行個體,是以最佳的範圍是請求或方法範圍。絕對不能将SqlSession執行個體的引用放在一個類的靜态字段或執行個體字段中。

2. MyBatis工具類

  為了簡化MyBatis的開發,可将MyBatis進一步封裝

public class MybatisUtil {
    /**
     * 不讓使用者在外界建立工具類對象
     */
    private MybatisUtil(){}
    /**
     * 初始化工廠
     */
    private static SqlSessionFactory factory;
    static {
        try {
            InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
            factory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 擷取SqlSession對象的方法
     */
    public static SqlSession openSession(){
        return factory.openSession();
    }
}
           

3. 原始DAO開發方式

  原始Dao開發方法需要程式員編寫Dao接口和Dao實作類

接口:

public interface DeptDao {
    public List<Dept> findDept();
    public Dept findDeptById(Integer deptno);
    public void insertDept(Dept dept);
    public void updateDept(Dept dept);
    public void deleteDept(Integer deptno);
}
           

實作類:

public class DeptDaoImpl implements DeptDao {
    @Override
    public List<Dept> findDept() {
        SqlSession session = MybatisUtil.openSession();
        List<Dept> list = session.selectList("dept.findDept");
        session.close();
        return  list;
    }
    @Override
    public Dept findDeptById(Integer deptno) {
        SqlSession session = MybatisUtil.openSession();
        Dept dept = session.selectOne("dept.findDeptById",deptno);
        session.close();
        return dept;
    }
    @Override
    public void insertDept(Dept dept) {
        SqlSession session = MybatisUtil.openSession();
        session.insert("dept.insertDept",dept);
        session.commit();
        session.close();
    }
    @Override
    public void updateDept(Dept dept) {
        SqlSession session = MybatisUtil.openSession();
        session.update("dept.updateDept",dept);
        session.commit();
        session.close();
    }
    @Override
    public void deleteDept(Integer deptno) {
        SqlSession session = MybatisUtil.openSession();
        session.delete("dept.deleteDept",deptno);
        session.commit();
        session.close();
    }
}
           

原始DAO開發問題

  • dao接口實作類方法中存在大量模闆方法,設想能否将這些代碼提取出來,大大減輕程式員的工作量
  • 調用SqlSession方法時将Statement的id寫死了
  • 調用SqlSession方法時傳入的變量,由于SqlSession方法使用泛型,即使變量類型傳入錯誤,在編譯階段也不報錯,不利于程式員開發

4. Mapper代理方式

  Mapper代理開發方式隻需要程式員編寫Mapper接口(相當于Dao接口),由MyBatis架構根據接口定義建立接口的動态代理對象,代理對象的方法體同上邊Dao接口實作類方法

  程式員編寫Mapper接口需要遵循一些開發規範,MyBatis可以自動生成Mapper接口實作類代理對象

開發規範:

  1. 在mapper.xml中namespace等于Mapper接口的全限定名
  2. Mapper接口中的方法名和mapper.xml中statement的id一緻
  3. Mapper接口中的方法輸入參數類型和mapper.xml中parameterType指定的類型一緻
  4. Mapper接口中的方法傳回值類型和mapper.xml中resultType指定的類型一緻

Mapper接口:

public interface EmpMapper {
    public List<Emp> findEmp();
    public Emp findEmpByEmpno(Integer empno);
    /**
     * 增删改方法的傳回值
     * 1.void 無傳回值
     * 2.int 傳回操作資料庫表的記錄數
     * 3.boolean 根據傳回資料庫表影響的記錄數,來判斷是否成功
     *          注意:影響的記錄數大于等于1,傳回值為true,否則為false
     */
    public void insertEmp(Emp emp);
    public void updateEmp(Emp emp);
    public void deleteEmp(Integer empno);
}
           

映射檔案:

<!--namespace : 與Mapper接口的全限定名保持一緻-->
<mapper namespace="com.mybatis.mapper.EmpMapper">
    <!--
        statementId與Mapper接口的方法名稱保持一緻
        parameterType的類型必須與方法的參數類型保持一緻
        resultType的類型必須與方法的傳回值類型保持一緻
    -->
    <select id="findEmpByEmpno" parameterType="java.lang.Integer" resultType="com.mybatis.entity.Emp">
      select empno,ename,job,mgr,hiredate,sal,comm from emp where empno=#{abc}
    </select>

    <!-- 2.查詢所有員工 -->
    <select id="findEmp" resultType="com.mybatis.entity.Emp">
      select empno,ename,job,mgr,hiredate,sal,comm from emp order by empno desc
    </select>

    <!-- 4.新增員工 -->
    <insert id="insertEmp" parameterType="com.mybatis.entity.Emp" >
      insert into emp(ename,job,mgr,hiredate,sal,comm)
      values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm})
    </insert>
    <!-- 5. 修改員工-->
    <update id="updateEmp" parameterType="com.mybatis.entity.Emp">
        update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},
        sal=#{sal},comm=#{comm} where empno=#{empno}
    </update>

    <!-- 6. 删除員工-->
    <delete id="deleteEmp" parameterType="java.lang.Integer">
       delete from emp where empno=#{empno}
    </delete>
</mapper>
           

測試:

SqlSession session = MybatisUtil.openSession();
//通過SqlSession對象獲得到你指定Mapper接口的代理對象(就是接口的實作類對象)
EmpMapper mapper = session.getMapper(EmpMapper.class);
           

三、mybatis-config.xml

  MyBatis的全局配置檔案mybatis-config.xml,配置内容如下:

  • properties(屬性)
  • settings(全局配置參數)
  • typeAliases(類型别名)
  • typeHandlers(類型處理器)
  • objectFactory(對象工廠)
  • plugins(插件)
  • environments(環境集合屬性對象)
    • environment(環境子屬性對象)
      • transactionManager(事務管理)
      • dataSource(資料源)
  • mappers(映射器)

1. properties(屬性)

  将資料庫連接配接參數單獨配置在db.properties中,隻需要在mybatis-config.xml中加載db.properties的屬性值。在mybatis-config.xml中就不需要對資料庫連接配接參數寫死

mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mybatis
mysql.username=root
mysql.password=123456
           

在mybatis-config.xml加載屬性檔案:

<!-- 加載外部資源檔案 -->
<properties resource="db.properties">
    <!--properties中還可以配置一些屬性-->
    <!--<property name="mysql.url" value="jdbc:mysql://localhost:3306/mybatis"/>-->
</properties>
           

注意: MyBatis将按照下面的順序來加載屬性:

  1. 在properties标簽體内定義的屬性首先被讀取
  2. 然後會讀取properties标簽中resource或url加載的屬性,它會覆寫已讀取的同名屬性
  3. 最後讀取parameterType傳遞的屬性,它會覆寫已讀取的同名屬性

建議:

  1. 不要在properties标簽體内添加任何屬性值,隻将屬性值定義在properties檔案中
  2. 在properties檔案中定義屬性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX

2. settings(全局配置參數)

  MyBatis架構在運作時可以調整一些運作參數

  比如:開啟二級緩存、開啟延遲加載

  可配置參數如下:

Mybatis入門操作一、Mybatis入門二、MyBatis開發DAO三、mybatis-config.xml四、mapper.xml五、關聯查詢六、延遲加載
Mybatis入門操作一、Mybatis入門二、MyBatis開發DAO三、mybatis-config.xml四、mapper.xml五、關聯查詢六、延遲加載

3. typeAliases(類型别名)

  在mapper.xml中,定義很多的statement,statement需要parameterType指定輸入參數的類型、需要resultType指定輸出結果的映射類型。如果在指定類型時輸入類型全路徑,不友善進行開發,可以針對parameterType或resultType指定的類型定義一些别名,在mapper.xml中通過别名定義,友善開發

3.1 MyBatis預設支援别名

别名 映射的類型 别名 映射的類型
_byte byte byte java.lang.Byte
_short short short java.lang.Short
_int int int java.lang.Integer
_integer int integer java.lang.Integer
_long long long java.lang.Long
_float float float java.lang.Float
_double double double java.lang.Double
_boolean boolean boolean java.lang.Boolean
string java.lang.String date java.util.Date
map java.util.Map hashmap java.util.HashMap
list java.util.List arraylist java.util.ArrayList
object java.lang.Object

3.2 自定義别名

單個定義别名:

<typeAliases>
    <!--
        <typeAlias>單個别名配置
        type : Java中類型的全限定名
        alias : 自定義别名
    -->
    <typeAlias type="com.mybatis.entity.Dept" alias="abc"/>
</typeAliases>

<!--在resultType使用的abc為自定義類型别名-->
<select id="findDeptById" parameterType="int" resultType="abc">
    select deptno,dname,loc from dept where deptno=#{deptno}
</select>
           

批量定義别名:

<typeAliases>
    <!--
        <package>批量别名配置
        name : 需要配置别名的實體類所在包的包名
        預設别名為該類的類名,其首字母大小均可
    -->
    <package name="com.mybatis.entity"/>
</typeAliases>
           

4. typeHandlers(類型處理器)

  MyBatis中通過typeHandlers完成jdbc類型和Java類型的轉換,MyBatis自帶的類型處理器基本上滿足日常需求,不需要單獨定義

MyBatis支援類型處理器:

類型處理器 Java類型 JDBC類型
BooleanTypeHandler Boolean,boolean 任何相容的布爾值
ByteTypeHandler Byte,byte 任何相容的數字或位元組類型
ShortTypeHandler Short,short 任何相容的數字或短整型
IntegerTypeHandler Integer,int 任何相容的數字和整型
LongTypeHandler Long,long 任何相容的數字或長整型
FloatTypeHandler Float,float 任何相容的數字或單精度浮點型
DoubleTypeHandler Double,double 任何相容的數字或雙精度浮點型
BigDecimalTypeHandler BigDecimal 任何相容的數字或十進制小數類型
StringTypeHandler String CHAR和VARCHAR類型
ClobTypeHandler String CLOB和LONGVARCHAR類型
NStringTypeHandler String NVARCHAR和NCHAR類型
NClobTypeHandler String NCLOB類型
ByteArrayTypeHandler byte[] 任何相容的位元組流類型
BlobTypeHandler byte[] BLOB和LONGVARBINARY類型
DateTypeHandler java.util.Date TIMESTAMP類型
DateOnlyTypeHandler java.util.Date DATE類型
TimeOnlyTypeHandler java.util.Date TIME類型
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP類型
SqlDateTypeHandler java.sql.Date DATE類型
SqlTimeTypeHandler java.sql.Time TIME類型
ObjectTypeHandler 任意 其他或未指定類型
EnumTypeHandler Enumeration類型 VARCHAR-任何相容的字元串類型,作為代碼存儲(而不是索引)

5. mappers(映射器)

5.1 單個加載映射檔案

通過resource加載單個映射檔案

<mappers>
    <!--
        <mapper>單個加載映射檔案
        resource : 通過相對路徑加載檔案(項目源目錄下的檔案)
        url : 通過絕對路徑加載檔案(檔案系統中檔案)
    -->
    <mapper resource="com/mybatis/mapper/DeptMapper.xml"/>
    <mapper resource="com/mybatis/mapper/EmpMapper.xml"/>
</mappers>
           

通過mapper接口加載單個映射檔案

  • 前提:使用的是mapper代理方法
  • 遵循規範:需要将Mapper接口類名和mapper.xml映射檔案名稱保持一緻且在一個目錄中
<mappers>
    <!--
        <mapper>單個加載映射檔案
        class : 配置mapper接口的全限定名,通過Java中的Mapper接口來加載映射檔案
    -->
    <mapper class="com.mybatis.mapper.DeptMapper"/>
    <mapper class="com.mybatis.mapper.EmpMapper"/>
</mappers>
           

5.2 批量加載映射檔案

  指定Mapper接口的包名,MyBatis自動掃描包下邊所有Mapper接口進行加載

  遵循規範:與mapper接口加載單個映射檔案一緻

<mappers>
    <!--
        <package>批量加載映射檔案
        name : 存放Mapper接口與mapper.xml檔案的包名
    -->
    <package name="com.mybatis.mapper"/>
</mappers>
           

四、mapper.xml

  mapper.xml映射檔案中定義了操作資料庫的Sql,每個Sql是一個statement,映射檔案是MyBatis的核心

1. parameterType輸入映射

1.1 簡單類型

<select id="findUsersByRealname" parameterType="string" resultType="Users">
    select userid,username,password,realname from users where realname like #{realname}
</select>
           

1.2 實體類或pojo類型

  開發中通過實體類或pojo類型傳遞查詢條件,查詢條件是綜合的查詢條件,不僅包括實體類中查詢條件還包括其它的查詢條件,這時可以使用包裝對象傳遞輸入參數

實體類:Users和Pagebean

//實體類
public class Users{
    private Integer userid;
    private String username;
    private String password;
    private String realname;
    //get,set方法省略...
}
//頁時使用的pojo對象
public class Pagebean {
    private int page;
    private int pageSize;
    private int maxCount;
    private int maxPage;
    private int offset;
    //get,set方法省略...
}
           

複合實體類:UsersQuery

public class UsersQuery {
    private Users users;
    private Pagebean pagebean;
    //get,set方法省略...
}
           

映射檔案:

<select id="findUsersByRealnameAndPage" parameterType="UsersQuery" resultType="Users">
    select 
        userid,username,password,realname from users
    where 
        realname like #{users.realname}
    order by userid 
    limit #{pagebean.offset},#{pagebean.pageSize}
</select>
           

1.3 Map類型

<select id="findUsersWithMap" parameterType="map" resultType="Users">
    select 
        userid,username,password,realname from users
    where 
        realname like #{map_realname}
    order by userid 
    limit #{map_offset},#{map_size}
</select>
           

1.4 多輸入參數

  MyBatis中允許有多個輸入參數,可使用@Param注解來表示

/**
* @Param注解可以實作多個輸入參數
* 每個輸入參數前必須使用@Param注解
* 注解中value屬性的值為擷取此參數值的key
*
* 多參數使用了注解之後,mybatis就将這些注解中value屬性值與參數值存放一個map集合中
* 其中value屬性值為map集合的key
* 參數值為map集合value
*
* 使用此種方法可以省略paramterType的配置
*/
public Users login(@Param(value = "uname") String username, @Param("pwd") String password);
           

  這種做法類似與Map類型的輸入參數,其中@Param注解的value屬性值為Map的key,在映射檔案中通過ognl可擷取對應的value,并且parameterType可以不指定類型

<select id="login" resultType="Users">
    select userid,username,password,realname from users
    where username=#{uname} and password=#{pwd}
</select>
           

2. resultType輸出映射

2.1 簡單類型

  查詢出來的結果集隻有一行且一列,可以使用簡單類型進行輸出映射

<select id="findUsersCount" resultType="int">
    select count(*) from users
</select>
           

2.2 實體類對象和清單

  不管是輸出的實體類是單個對象還是一個清單(list中包括實體類對象),在mapper.xml中resultType指定的類型是一樣的

  在原始Dao的方式中,通過selectOne和selectList方法來區分傳回值為單個對象或集合清單,而在mapper代理中,則通過接口中定義的方法傳回值來區分

public Users findUsersByUserid(Integer userid);
public List<Users> findUsers();
           
<select id="findUsersByUserid" parameterType="int" resultType="Users">
    select userid,username,password,realname from users where userid=#{userid}
</select>
<select id="findUsers" resultType="Users">
    select userid,username,password,realname from users order by userid
</select>
           

2.3 resultMap

  • resultType可以指定将查詢結果映射為實體類,但需要實體類的屬性名和Sql查詢的列名一緻方可映射成功
  • 如果Sql查詢字段名和實體類的屬性名不一緻,可以通過resultMap将字段名和屬性名作一個對應關系,resultMap實質上還會将查詢結果映射到實體類對象中
  • resultMap可以實作将查詢結果映射為複合型的實體類,比如在查詢結果映射對象中包括實體類和list實作一對一查詢和一對多查詢

定義resultMap

<!--
    resultType : 自動映射
    resultMap : 手動映射
    屬性解析:
    type : 對應Java中實體類的類型,可以使用全限定名也是使用類型别名
    id : <resultMap>标簽的唯一辨別,在<select>的resultMap屬性中使用
    子标簽:
    <id /> 配置對應表中的主鍵
    <result /> 配置對應表中的普通字段
    property : 實體類中的屬性名
    column : 表或結果集中的字段名
    javaType : 屬性類型
    jdbcType : 字段類型
    其中javaType與jdbcType可不配置由mybaits自動識别
-->
<resultMap id="usersResultMap" type="Users">
    <id property="userid" column="id"/>
    <result property="username" column="uname"/>
    <result property="password" column="pwd"/>
    <result property="realname" column="rname"/>
</resultMap>
           

使用resultMap作為statement的輸出映射類型

<select id="findUsersWithResultMap" resultMap="usersResultMap">
    select userid id,username uname,password pwd,realname rname from users
</select>
           

3. 動态Sql

3.1 什麼是動态Sql

  動态Sql是指MyBatis核心對Sql語句進行靈活操作,通過表達式進行判斷,對Sql進行靈活拼接、組裝

3.2 if标簽

  将實體類不為空的屬性作為where條件

<select id="findEmpUseIf" parameterType="map" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where 1=1
    <!--test:判斷條件,條件成立if标簽中的Sql語句拼接-->
    <if test="ename!=null and ename!=''">
            and ename like #{ename}
    </if>
    <if test="sal!=null and sal!=0.0">
        and sal=#{sal}
    </if>
    <if test="deptno!=null and deptno!=0">
        and deptno=#{deptno}
    </if>
</select>
           

3.2 where标簽

  where标簽中包含的任意if條件如果成立,它就插入一個where關鍵字。此外,如果标簽傳回的内容是以AND 或OR 開頭的,則它會自動剔除掉

<select id="findEmpUseWhere" parameterType="map" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    <where>
    <if test="ename!=null and ename!=''">
        and ename like #{ename}
    </if>
    <if test="sal!=null and sal!=0.0">
        and sal=#{sal}
    </if>
    <if test="deptno!=null and deptno!=0">
        and deptno=#{deptno}
    </if>
    </where>
</select>
           

3.3 set标簽

  當在update語句中使用if标簽時,如果前面的if沒有執行,則或導緻逗号多餘錯誤。使用set标簽可以将動态的配置SET關鍵字,和剔除追加到條件末尾的任何不相關的逗号

  注意:如果set包含的内容為空的話則會出錯

<update id="updateEmpUseSet" parameterType="Emp">
    update emp
    <set>
        <if test="ename!=null">
            ename=#{ename},
        </if>
        <if test="job!=null">
            job=#{job},
        </if>
        <if test="mgr!=null">
            mgr=#{mgr},
        </if>
        <if test="sal!=null">
            sal=#{sal},
        </if>
        <if test="comm!=null">
            comm=#{comm},
        </if>
        <if test="hiredate!=null">
            hiredate=#{hiredate},
        </if>
    </set>
        where empno=#{empno}
</update>
           

3.4 trim标簽

trim标簽屬性解析:

  • prefix:字首,包含内容前加上某些字元
  • suffix:字尾,包含内容後加上某些字元
  • prefixOverrides:剔除包含内容前的某些字元
  • suffixOverrides:剔除包含内容後的某些字元

trim來代替where标簽的功能

<select id="findEmpUseTrim" parameterType="Emp" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    <trim prefix="where" prefixOverrides="and|or">
        <if test="ename!=null">
            and ename like #{ename}
        </if>
        <if test="sal!=null and sal!=0.0">
            and sal=#{sal}
        </if>
        <if test="job!=null">
            and job=#{job}
        </if>
    </trim>
</select>
           

trim來代替set标簽的功能

<update id="updateEmpUseTrim" parameterType="Emp">
    update emp
    <trim prefix="set" suffixOverrides=",">
        <if test="ename!=null">
            ename=#{ename},
        </if>
        <if test="job!=null">
            job=#{job},
        </if>
        <if test="mgr!=null">
            mgr=#{mgr},
        </if>
        <if test="sal!=null">
            sal=#{sal},
        </if>
        <if test="comm!=null">
            comm=#{comm},
        </if>
        <if test="hiredate!=null">
            hiredate=#{hiredate},
        </if>
    </trim>
    where empno=#{empno}
</update>
           

trim實作insert語句

<insert id="insertEmpUseTrim" parameterType="Emp">
    insert into emp
    <trim suffix="(" prefix=")" prefixOverrides=",">
        <if test="ename!=null">
            ename,
        </if>
        <if test="job!=null">
            job,
        </if>
        <if test="mgr!=null">
            mgr,
        </if>
        <if test="hiredate!=null">
            hiredate,
        </if>
        <if test="sal!=null">
            sal,
        </if>
        <if test="comm!=null">
            comm,
        </if>
    </trim>
    <trim prefix=" values(" suffix=")" suffixOverrides=",">
        <if test="ename!=null">
            #{ename},
        </if>
        <if test="job!=null">
            #{job},
        </if>
        <if test="mgr!=null">
            #{mgr},
        </if>
        <if test="hiredate!=null">
            #{hiredate},
        </if>
        <if test="sal!=null">
            #{sal},
        </if>
        <if test="comm!=null">
            #{comm},
        </if>
    </trim>
</insert>
           

3.5 foreach标簽

  • 向Sql傳遞數組或list,MyBatis使用foreach解析
  • Sql隻接收一個數組參數,這時sql解析參數的名稱MyBatis固定為array
  • Sql隻接收一個List參數,這時sql解析參數的名稱MyBatis固定為list
  • 如果是通過一個實體類或pojo的屬性傳遞到Sql的數組或list,則參數的名稱為實體類或pojo中的屬性名

屬性解析:

  • index:為數組的下标
  • item:每個周遊生成對象中
  • open:開始周遊時拼接的串
  • close:結束周遊時拼接的串
  • separator:周遊的兩個對象中需要拼接的串
public void deleteEmpUseForeachArray(int[] empnos);
public List<Emp> findEmpUseForeachList(List<Integer> empnos);
public List<Emp> findEmpUseForeachPojo(EmpQuery query);
           
<delete id="deleteEmpUseForeachArray" parameterType="int">
    delete from emp where empno in
    <foreach collection="array" open="(" close=")" separator="," item="id">
        #{id}
    </foreach>
</delete>

<select id="findEmpUseForeachList" parameterType="int" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where empno in
    <foreach collection="list" open="(" close=")" separator="," item="empno">
        #{empno}
    </foreach>
</select>

<select id="findEmpUseForeachPojo" parameterType="EmpQuery" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where deptno in
    <foreach collection="deptnos" open="(" close=")" separator="," item="deptno">
        #{deptno}
    </foreach>
</select>
           

3.6 choose,when,otherwise标簽

  if-else-if判斷

<select id="">
    select...
    <choose>
        <when test="">

        </when>
        <when test="">

        </when>
        <otherwise>

        </otherwise>
    </choose>
</select>
           

3.7 Sql片段

  将實作的動态Sql判斷代碼塊抽取出來,組成一個Sql片段,其它的statement中就可以引用Sql片段,友善程式員進行開發

  注意:在sql片段中不要包括where标簽

<select id="findEmpUseSql" parameterType="map" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    <where>
        <!--  引入sql片段 -->
        <include refid="empSql"/>
    </where>
</select>

<sql id="empSql">
    <if test="ename!=null">
        and ename like #{ename}
    </if>
    <if test="sal!=null and sal!=0.0">
        and sal=#{sal}
    </if>
    <if test="deptno!=null and deptno!=0">
        and deptno=#{deptno}
    </if>
</sql>
           

五、關聯查詢

1. 資料模型分析

Mybatis入門操作一、Mybatis入門二、MyBatis開發DAO三、mybatis-config.xml四、mapper.xml五、關聯查詢六、延遲加載

表功能介紹

  • 使用者表:記錄使用者的基本資訊
  • 訂單表:記錄使用者所建立的訂單(購買商品的訂單)
  • 訂單詳情表:記錄訂單的詳細資訊即購買商品的資訊
  • 商品表:記錄商品的基本資訊

表與表之間的業務關系

  1. 使用者表和訂單表:

    使用者表---->訂單表:一個使用者可以建立多個訂單,一對多關系

    訂單表---->使用者表:一個訂單隻由一個使用者建立,一對一關系

  2. 訂單表和訂單詳情表:

    訂單表---->訂單詳情表:一個訂單可以包含多個訂單詳情,因為一個訂單可以購買多個商品,每個商品的購買資訊在訂單詳情表中記錄,一對多關系

    訂單詳情表----->訂單表:一個訂單詳情隻能包括在一個訂單中,一對一關系

  3. 訂單詳情表和商品表:

    訂單詳情表---->商品表:一個訂單詳情隻對應一個商品資訊,一對一關系

    商品表---->訂單詳情表:一個商品可以包括在多個訂單詳情,一對多關系

  4. 訂單表和商品表:

    訂單表<---->商品表:一個訂單中包含多個商品,一個商品可以添加在多個訂單中,兩者是通過訂單詳情表建立關系,多對多關系

2. 一對一查詢

2.1 resultType

2.1.1 Sql語句

SELECT 
    id, order_number,totalprice,status,u.userid,username,password,realname
FROM 
    orders o,users u 
WHERE 
    o.userid=u.userid
           

2.1.2 實體類

public class Users {
    private Integer userid;
    private String username;
    private String password;
    private String realname;
    //get,set方法省略...
}
public class Orders {
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    //get,set方法省略...
}
           

原始的Orders類不能映射全部字段,需要新建立的實體類,建立一個包括查詢字段較多的實體類

OrdersUsers中包含了Orders以及Users需要查詢的屬性

public class OrdersUsers {
    /**
     * 對應orders表中的字段
     */
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    /**
     * 對象users表中的字段
     */
    private Integer userid;
    private String username;
    private String password;
    private String realname;
}
           

2.1.3 Mapper接口

2.1.4 mapper.xml

<select id="findOrdersUseResultType" resultType="OrdersUsers">
    SELECT 
        id, order_number,totalprice,status,u.userid,username,password,realname
    FROM 
        orders o,users u 
    WHERE 
        o.userid=u.userid
</select>
           

2.1.5 測試

SqlSession session = MybatisUtil.openSession();

OrderMapper mapper = session.getMapper(OrderMapper.class);
List<OrdersUsers> list = mapper.findOrdersUseResultType();
for (OrdersUsers ou : list) {
    System.out.println(ou);
}
session.close();
           

2.2 resultMap

2.2.1 Sql語句

SELECT 
    id, order_number,totalprice,status,u.userid,username,password,realname
FROM 
    orders o,users u 
WHERE 
    o.userid=u.userid
           

2.2.2 實體類

  在Orders類中加入Users屬性,Users屬性用于存儲關聯查詢的使用者資訊,因為訂單關聯查詢使用者是一對一關系,是以這裡使用單個Users對象存儲關聯查詢的使用者資訊

public class Users {
    private Integer userid;
    private String username;
    private String password;
    private String realname;
    //get,set方法省略...
}
public class Orders {
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    /**
     * 一對一/多對一依賴關系屬性
     */
    private Users users;
    //get,set方法省略...
}
           

2.2.3 Mapper接口

2.2.4 mapper.xml

<resultMap id="ordersUsersResultMap" type="Orders">
    <id column="id" property="id"/>
    <result column="order_number" property="order_number"/>
    <result column="totalprice" property="totalPrice"/>
    <result column="status" property="status"/>
    <association property="users" javaType="Users">
        <id column="userid" property="userid"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="realname" property="realname"/>
    </association>
</resultMap>
<select id="findOrdersUseResultMap" resultMap="ordersUsersResultMap">
    SELECT 
        id, order_number,totalprice,status,u.userid,username,password,realname
    FROM 
        orders o,users u 
    WHERE 
        o.userid=u.userid
</select>
           

2.2.5 測試

SqlSession session = MybatisUtil.openSession();

OrderMapper mapper = session.getMapper(OrderMapper.class);
List<Orders> list = mapper.findOrdersUseResultMap();
for (Orders o : list) {
    System.out.println(o);
}
session.close();
           

2.3 resultType和resultMap實作一對一查詢小結

  • resultType:使用resultType實作較為簡單,如果實體類中沒有包括查詢出來的列名,需要增加列名對應的屬性,即可完成映射。如果查詢結果沒有特殊要求,建議使用resultType
  • resultMap:需要單獨定義resultMap,實作有點麻煩,如果對查詢結果有特殊的要求,使用resultMap可以完成将關聯查詢映射到實體類的屬性中
  • resultMap可以實作延遲加載,resultType無法實作延遲加載

3. 一對多查詢

3.1 Sql語句

SELECT 
	o.id,order_number,totalprice,status,d.id detailid,amount,goods_id 
FROM 
	orders o,ordersdetail d
WHERE 
    o.id=d.orders_id
           

3.2 實體類

  在Order類中加入List details屬性,details屬性用于存儲關聯查詢的訂單詳情,因為訂單關聯查詢訂單詳情是一對多關系,是以這裡使用集合對象存儲關聯查詢的訂單詳情資訊

public class OrdersDetail {
    private Integer id;
    private Integer amount;
    private Integer goods_id;
    //get,set方法省略...
}
public class Orders {
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    /**
     * 一對多,多對多依賴關系屬性
     */
    private List<OrdersDetail> details;
    //get,set方法省略...
}
           

3.2 Mapper接口

3.3 mapper.xml

<resultMap id="ordersAndDetailResultMap" type="Orders">
    <id column="id" property="id"/>
    <result column="order_number" property="order_number"/>
    <result column="totalprice" property="totalPrice"/>
    <result column="status" property="status"/>
    <!--
        映射依賴關系屬性
        <collection>用于描述一對多或多對多關系屬性
        property : 關系屬性的名稱
        OrdersDetail : 關系屬性集合中存放的實體類類型
    -->
    <collection property="details" ofType="OrdersDetail">
        <id column="detailid" property="id"/>
        <result column="amount" property="amount"/>
        <result column="goods_id" property="goods_id"/>
    </collection>
</resultMap>
<select id="findOrdersAndOrdersDetail" resultMap="ordersAndDetailResultMap">
    SELECT 
        o.id,order_number,totalprice,status,d.id detailid,amount,goods_id 
    FROM 
        orders o,ordersdetail d
    WHERE 
        o.id=d.orders_id
</select>
           

3.4 測試

SqlSession session = MybatisUtil.openSession();

OrderMapper mapper = session.getMapper(OrderMapper.class);
List<Orders> list = mapper.findOrdersAndOrdersDetail();
for (Orders orders : list) {
    System.out.println(orders);
}
session.close();
           

4. 多對多查詢

4.1 訂單與商品之間的多對多關系

4.1.1 Sql語句

SELECT
    o.id,order_number,totalprice,status,d.id detailid,amount,goods_id,gname,descr,price
FROM
    orders o,ordersdetail d,goods g
WHERE
    o.id=d.orders_id 
AND 
    d.goods_id=g.id
           

4.1.2 實體類

  将Orderdetail類中Integer goods_id屬性修改為Goods goods屬性,goods屬性用于存儲關聯查詢的商品資訊。訂單與訂單詳情是一對多關系,訂單詳情與商品是一對一關系,反之商品與訂單詳情是一對多關系,訂單詳情與訂單是一對一關系,是以訂單與商品之前為多對多關系

public class Goods {
    private Integer id;
    private String gname;
    private String descr;
    private Double price;
    //get,set方法省略...
}
public class OrdersDetail {
    private Integer id;
    private Integer amount;
    /**
     * 一對一依賴關系屬性
     */
    private Goods goods;
    //get,set方法省略...
}
public class Orders {
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    /**
     * 一對多,多對多依賴關系屬性
     */
    private List<OrdersDetail> details;
    //get,set方法省略...
}
           

4.1.3 Mapper接口

4.1.4 mapper.xml

<resultMap id="ordersAndGoodsResultMap" type="Orders">
    <id column="id" property="id"/>
    <result column="order_number" property="order_number"/>
    <result column="totalprice" property="totalPrice"/>
    <result column="status" property="status"/>
    <collection property="details" ofType="OrdersDetail">
        <id column="detailid" property="id"/>
        <result column="amount" property="amount"/>
        <association property="goods" javaType="Goods">
            <id column="goods_id" property="id"/>
            <result column="gname" property="gname"/>
            <result column="descr" property="descr"/>
            <result column="price" property="price"/>
        </association>
    </collection>
</resultMap>
<select id="findOrdersAndGoods" resultMap="ordersAndGoodsResultMap">
    SELECT
        o.id,order_number,totalprice,status,d.id detailid,amount,goods_id,gname,descr,price
    FROM
        orders o,ordersdetail d,goods g
    WHERE
        o.id=d.orders_id 
    AND 
        d.goods_id=g.id
</select>
           

4.1.5 測試

SqlSession session = MybatisUtil.openSession();

OrderMapper mapper = session.getMapper(OrderMapper.class);
List<Orders> list = mapper.findOrdersAndGoods();
for (Orders orders : list) {
    System.out.println(orders);
}
session.close();
           

4.2 學生與課程之間的多對多關系

4.2.1 Sql語句

SELECT 
    s.id,name,gender,major,course_id,cname 
FROM 
    student s,student_course sc,course c
WHERE 
    s.id=sc.student_id
AND 
    sc.course_id=c.id
           

4.2.2 實體類

public class Course {
    private Integer id;
    private String cname;
    //get,set方法省略...
}
public class Student {
    private Integer id;
    private String name;
    private String gender;
    private String major;
    /**
     * 多對多的依賴關系屬性
     */
    private List<Course> courses;
    //get,set方法省略...
}
           

4.2.3 Mapper接口

4.2.4 mapper.xml

<resultMap id="studentAndCourseResultMap" type="Student">
    <id column="id" property="id"/>
    <result column="NAME" property="name"/>
    <result column="gender" property="gender"/>
    <result column="major" property="major"/>
    <collection property="courses" ofType="Course">
        <id column="course_id" property="id"/>
        <result column="cname" property="cname"/>
    </collection>
</resultMap>
<select id="findStudent" resultMap="studentAndCourseResultMap">
    SELECT 
        s.id,name,gender,major,course_id,cname 
    FROM 
        student s,student_course sc,course c
    WHERE 
        s.id=sc.student_id
    AND 
        sc.course_id=c.id
</select>
           

4.2.5 測試

SqlSession session = MybatisUtil.openSession();

StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> list = mapper.findStudent();
for (Student student : list) {
    System.out.println(student);
}
session.close();
           

5. 關聯查詢總結

5.1 resultType

  作用:将查詢結果按照Sql列名與實體類屬性名一緻性映射到實體類對象中

  場合:常見一些明細記錄的展示,比如使用者購買商品明細,将關聯查詢資訊全部展示在頁面時,此時可直接使用resultType将每一條記錄映射到實體類中,在前端頁面周遊list(list中是實體類)即可

5.2 resultMap

  使用association和collection完成一對一和一對多進階映射(對結果有特殊的映射要求)

5.2.1 association

  作用:将關聯查詢資訊映射到一個實體類對象中

  場合:為了友善查詢關聯資訊可以使用association将關聯資訊映射為目前對象的一個屬性,比如:查詢訂單以及關聯使用者資訊

5.2.2 collection

  作用:将關聯查詢資訊映射到一個list集合中

  場合:為了友善查詢周遊關聯資訊可以使用collection将關聯資訊映射到list集合中,比如:查詢使用者權限範圍子產品及子產品下的菜單,可使用collection将子產品映射到子產品list中,将菜單清單映射到子產品對象的菜單list屬性中,這樣的作的目的也是友善對查詢結果集進行周遊查詢。如果使用resultType無法将查詢結果映射到list集合中

5.2.3 resultMap的繼承

  resultMap标簽可以通過extends屬性來繼承一個已有的或公共的resultMap,避免重複配置的出現,減少配置量。例子如下:

<!-- 父resultMap标簽-->
<resultMap id="ordersRusultMap" type="Orders">
    <id column="id" property="id"/>
    <result column="order_number" property="order_number"/>
    <result column="totalprice" property="totalPrice"/>
    <result column="status" property="status"/>
</resultMap>
<!-- 繼承父resultMap标簽中的配置,避免重複配置 -->
<resultMap id="ordersUsersResultMap" type="Orders" extends="ordersRusultMap">
    <association property="users" javaType="Users">
        <id column="userid" property="userid"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="realname" property="realname"/>
    </association>
</resultMap>
           

六、延遲加載

1. 什麼是延遲加載

  • 需要查詢關聯資訊時,使用MyBatis延遲加載特性可有效的減少資料庫壓力,首次查詢隻查詢主要資訊,關聯資訊等使用者擷取時再加載
  • resultMap可以實作進階映射(使用association、collection實作一對一及一對多映射),association、collection具備延遲加載功能

2. 打開延遲加載開關

  在MyBatis核心配置檔案中配置:lazyLoadingEnabled、aggressiveLazyLoading

設定項 描述 允許值 預設值
lazyLoadingEnabled 全局性設定懶加載。如果設為‘false’,則所有相關聯的都會被初始化加載 true \ false false
aggressiveLazyLoading 當設定為‘true’的時候,懶加載的對象可能被任何懶屬性全部加載。否則,每個屬性都按需加載 true \ false true
<!-- 全局參數設定 -->
<settings>
    <!-- 開啟延遲加載 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
           

3. 使用association實作延遲加載

  查詢員工以及相關聯的部門資訊

3.1 Mapper接口

3.2 mapper.xml

  查詢員工資訊

<select id="findEmp" resultMap="empResultMap">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
</select>
           

  查詢關聯的部門資訊

  通過上邊查詢到的員工資訊中deptno外鍵去關聯查詢部門資訊,是以需要定義一個根據ID查詢部門的語句

<select id="findDeptByDeptno" parameterType="int" resultType="Dept">
    select deptno,dname,loc from dept where deptno=#{deptno}
</select>
           

  resultMap配置

<resultMap id="empResultMap" type="Emp">
    <id column="empno" property="empno"/>
    <result column="ename" property="ename"/>
    <result column="job" property="job"/>
    <result column="mgr" property="mgr"/>
    <result column="hiredate" property="hiredate"/>
    <result column="sal" property="sal"/>
    <result column="comm" property="comm"/>
    <!-- 關系屬性描述-->
    <!--
    <association>實作延遲加載
        select : 執行延遲加載時關聯資料查詢的sql對應的statementId
                1.執行的關聯查詢語句在同一mapper檔案中,可以直接使用statementId
                2.執行的關聯查詢語句在不同的mapper檔案中,namespace.statementId
        column : 在執行關聯查詢時,使用到主表的一個字段名稱(外鍵字段的名稱)
    -->
    <association property="dept" javaType="Dept" select="com.mybatis.mapper.DeptMapper.findDeptByDeptno" column="deptno">
    </association>
</resultMap>
           

4. 使用collection實作延遲加載

4.1 Mapper接口

4.2 mapper.xml

  查詢部門資訊

<select id="findDept" resultMap="deptResultMap">
    select deptno,dname,loc from dept
</select>
           

  查詢關聯的員工詳情資訊

  通過查詢到的部門資訊中deptno去關聯查詢員工詳情,是以需要定義一個根據部門ID查詢員工詳情的語句

<select id="findEmpByDeptno" parameterType="int" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where deptno=#{deptno}
</select>
           

  resultMap配置

<resultMap id="deptResultMap" type="Dept">
    <id column="deptno" property="deptno"/>
    <result column="dname" property="dname"/>
    <result column="loc" property="loc"/>
    <collection property="empList" ofType="Emp" select="com.mybatis.mapper.EmpMapper.findEmpByDeptno" column="deptno"/>
</resultMap>