MyBatis淺談

這裏寫圖片描述

轉載請註明出處:

http://blog.csdn.net/gane_cheng/article/details/52751206

http://www.ganecheng.tech/blog/52751206.html (瀏覽效果更好)

MyBatis 是支持定製化 SQL、存儲過程以及高級映射的優秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或註解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。

MyBatis讓程序將主要精力放在sql上,通過MyBatis提供的映射方式,自由靈活生成(半自動化,大部分需要程序員編寫sql)滿足需要sql語句。

MyBatis可以將向 preparedStatement中的輸入參數自動進行輸入映射,將查詢結果集靈活映射成java對象。(輸出映射)

MyBatis框架原理

這裏寫圖片描述

MyBatis框架執行過程:

1、配置MyBatis的配置文件,SqlMapConfig.xml(名稱不固定)

2、通過配置文件,加載MyBatis運行環境,創建SqlSessionFactory會話工廠
SqlSessionFactory 在實際使用時按單例方式。

3、通過SqlSessionFactory創建SqlSession
SqlSession 是一個面向用戶接口(提供操作數據庫方法),實現對象是線程不安全的,建議sqlSession應用場合在方法體內。

4、調用 sqlSession 的方法去操作數據。
如果需要提交事務,需要執行 SqlSession 的 commit() 方法。

5、釋放資源,關閉SqlSession

MyBatis常見用法

新建一個工程用於描述這些用法。

這裏寫圖片描述

編寫User.java類

package cn.itcast.mybatis.po;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {

    //屬性名和數據庫表的字段對應
    private int id;
    private String username;// 用戶姓名
    private String sex;// 性別
    private Date birthday;// 生日
    private String address;// 地址

    //用戶創建的訂單列表
    private List<Orders> ordersList;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", sex=" + sex
                + ", birthday=" + birthday + ", address=" + address + "]";
    }
    public List<Orders> getOrdersList() {
        return ordersList;
    }
    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

編寫User.xml映射文件

<?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命名空間,作用就是對sql進行分類化管理,理解sql隔離 
注意:使用mapper代理方法開發,namespace有特殊重要的作用
-->
<mapper namespace="test">

    <!-- 在 映射文件中配置很多sql語句 -->
    <!-- 需求:通過id查詢用戶表的記錄 -->
    <!-- 通過 select執行數據庫查詢
    id:標識 映射文件中的 sql
    將sql語句封裝到mappedStatement對象中,所以將id稱爲statement的id
    parameterType:指定輸入 參數的類型,這裏指定int型 
    #{}表示一個佔位符號
    #{id}:其中的id表示接收輸入 的參數,參數名稱就是id,如果輸入 參數是簡單類型,#{}中的參數名可以任意,可以value或其它名稱

    resultType:指定sql輸出結果 的所映射的java對象類型,select指定resultType表示將單條記錄映射成的java對象。
     -->
    <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">
        SELECT * FROM USER WHERE id=#{value}
    </select>

    <!-- 根據用戶名稱模糊查詢用戶信息,可能返回多條
    resultType:指定就是單條記錄所映射的java對象 類型
    ${}:表示拼接sql串,將接收到參數的內容不加任何修飾拼接在sql中。
    使用${}拼接sql,引起 sql注入
    ${value}:接收輸入 參數的內容,如果傳入類型是簡單類型,${}中只能使用value
     -->
    <select id="findUserByName" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User">
        SELECT * FROM USER WHERE username LIKE '%${value}%'
    </select>

    <!-- 添加用戶 
    parameterType:指定輸入 參數類型是pojo(包括 用戶信息)
    #{}中指定pojo的屬性名,接收到pojo對象的屬性值,MyBatis通過OGNL獲取對象的屬性值
    -->
    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
        <!-- 
        將插入數據的主鍵返回,返回到user對象中

        SELECT LAST_INSERT_ID():得到剛insert進去記錄的主鍵值,只適用與自增主鍵

        keyProperty:將查詢到主鍵值設置到parameterType指定的對象的哪個屬性
        order:SELECT LAST_INSERT_ID()執行順序,相對於insert語句來說它的執行順序
        resultType:指定SELECT LAST_INSERT_ID()的結果類型
         -->
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into user(username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})
        <!-- 
        使用MySQL的uuid()生成主鍵
        執行過程:
        首先通過uuid()得到主鍵,將主鍵設置到user對象的id屬性中
        其次在insert執行時,從user對象中取出id屬性值
         -->
        <!--  <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
            SELECT uuid()
        </selectKey>
        insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address}) -->


    </insert>

    <!-- 刪除 用戶
    根據id刪除用戶,需要輸入 id值
     -->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <!-- 根據id更新用戶
    分析:
    需要傳入用戶的id
    需要傳入用戶的更新信息
    parameterType指定user對象,包括 id和更新信息,注意:id必須存在
    #{id}:從輸入 user對象中獲取id屬性值
     -->
    <update id="updateUser" parameterType="cn.itcast.mybatis.po.User">
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} 
         where id=#{id}
    </update>

</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

配置SqlMapConfig.xml

<?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配置將廢除-->
    <environments default="development">
        <environment id="development">
        <!-- 使用jdbc事務管理,事務控制由MyBatis-->
            <transactionManager type="JDBC" />
        <!-- 數據庫連接池,由MyBatis管理-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <!-- 加載 映射文件 -->
    <mappers>
        <mapper resource="sqlmap/User.xml"/>

        <!--通過resource方法一次加載一個映射文件 -->
        <!-- <mapper resource="mapper/UserMapper.xml"/> -->

        <!-- 通過mapper接口加載單個 映射文件
        遵循一些規範:需要將mapper接口類名和mapper.xml映射文件名稱保持一致,且在一個目錄 中
        上邊規範的前提是:使用的是mapper代理方法
         -->
        <!-- <mapper class="cn.itcast.mybatis.mapper.UserMapper"/> -->

        <!-- 批量加載mapper
        指定mapper接口的包名,MyBatis自動掃描包下邊所有mapper接口進行加載
        遵循一些規範:需要將mapper接口類名和mapper.xml映射文件名稱保持一致,且在一個目錄 中
        上邊規範的前提是:使用的是mapper代理方法
         -->
        <package name="cn.itcast.mybatis.mapper"/>

    </mappers>
</configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

查找

    @Test
    public void testFindUserById() throws Exception 
    {
        // MyBatis配置文件
        String resource = "SqlMapConfig.xml";
        // 得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 創建會話工廠,傳入MyBatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通過工廠得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //通過SqlSession操作數據庫
        //第一個參數:映射文件中statement的id,等於namespace+"."+statement的id
        //第二個參數:指定和映射文件中所匹配的parameterType類型的參數
        //sqlSession.selectOne結果是與映射文件中所匹配的resultType類型的對象
        User user = sqlSession.selectOne("test.findUserById", 1);

        System.out.println(user);
        sqlSession.close();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

對應的User.xml中的statement

<mapper namespace="test">
    <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">
        SELECT * FROM USER WHERE id=#{value}
    </select>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

模糊查找

    @Test
    public void testFindUserByName() throws Exception 
    {
        // MyBatis配置文件
        String resource = "SqlMapConfig.xml";
        // 得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 創建會話工廠,傳入MyBatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通過工廠得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        List<User> list = sqlSession.selectList("test.findUserByName", "小明");

        System.out.println(list);
        sqlSession.close();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

對應的User.xml中的statement

<mapper namespace="test">
    <select id="findUserByName" parameterType="java.lang.String" resultType = "cn.itcast.mybatis.po.User" >
        SELECT * FROM USER WHERE username LIKE '%${value}%'
    </select>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

插入

    @Test
    public void testInsertUser() throws Exception 
    {
        // MyBatis配置文件
        String resource = "SqlMapConfig.xml";
        // 得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 創建會話工廠,傳入MyBatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通過工廠得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        User user=new User();
        user.setUsername("Forrest Gump");
        user.setBirthday(new Date());
        user.setSex("1");
        user.setAddress("Guangzhou,China");

        sqlSession.insert("test.insertUser", user);

        sqlSession.commit();
        sqlSession.close();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

對應的User.xml中的statement

<mapper namespace="test">
    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into user(username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})    
    </insert>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

刪除

    @Test
    public void testDeleteUser() throws Exception 
    {
        // MyBatis配置文件
        String resource = "SqlMapConfig.xml";
        // 得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 創建會話工廠,傳入MyBatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通過工廠得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        sqlSession.delete("test.deleteUser", 50);

        sqlSession.commit();
        sqlSession.close();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

對應的User.xml中的statement

<mapper namespace="test">
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

修改

    @Test
    public void testUpdateUser() throws Exception 
    {
        // MyBatis配置文件
        String resource = "SqlMapConfig.xml";
        // 得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 創建會話工廠,傳入MyBatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通過工廠得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        User user=new User();
        user.setId(50);
        user.setUsername("Forrest Gump");
        user.setBirthday(new Date());
        user.setSex("1");
        user.setAddress("Guangzhou,China");

        sqlSession.update("test.updateUser", user);

        sqlSession.commit();
        sqlSession.close();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

對應的User.xml中的statement

<mapper namespace="test">
    <update id="updateUser" parameterType="cn.itcast.mybatis.po.User">
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} 
         where id=#{id}
    </update>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

parameterType

在映射文件中通過parameterType指定輸入 參數的類型。
對於綜合查詢,建議parameterType使用包裝的pojo,(將各種pojo包裝在一個單獨的類)有利於系統 擴展。

resultType

在映射文件中通過resultType指定輸出結果的類型。查詢到的列名和resultType指定的pojo的屬性名一致,才能映射成功。

reusltMap

可以通過resultMap 完成一些高級映射。

如果查詢到的列名和映射的pojo的屬性名不一致時,通過resultMap設置列名和屬性名之間的對應關係(映射關係)。可以完成映射。

高級映射:
    將關聯查詢的列映射到一個pojo屬性中。(一對一)
    將關聯查詢的列映射到一個List<pojo>中。(一對多)

#{}和${}

#{}表示一個佔位符號,#{}接收輸入參數,類型可以是簡單類型,pojo、hashmap。
如果接收簡單類型,#{}中可以寫成value或其它名稱。





{}接收pojo對象值,通過OGNL讀取對象中的屬性值,通過屬性.屬性.屬性…的方式獲取對象屬性值。
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

${}表示一個拼接符號,會導致sql注入,所以不建議使用${}。
${}接收輸入參數,類型可以是簡單類型,pojo、hashmap。
如果接收簡單類型,${}中只能寫成value。
${}接收pojo對象值,通過OGNL讀取對象中的屬性值,通過屬性.屬性.屬性...的方式獲取對象屬性值。
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

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

MyBatis和Hibernate本質區別和應用場景

hibernate:是一個標準ORM框架(對象關係映射)。入門門檻較高的,不需要程序寫sql,sql語句自動生成了。

對sql語句進行優化、修改比較困難的。

應用場景:適用與需求變化不多的中小型項目,比如:後臺管理系統,erp、orm、oa。

MyBatis:專注是sql本身,需要程序員自己編寫sql語句,sql修改、優化比較方便。MyBatis是一個不完全 的ORM框架,雖然程序員自己寫sql,MyBatis 也可以實現映射(輸入映射、輸出映射)。

應用場景:
適用與需求變化較多的項目,比如:互聯網項目。

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

MyBatis開發dao的方法:

(1)、原始dao 的方法

需要程序員編寫dao接口和實現類
需要在dao實現類中注入一個SqlSessionFactory工廠。

(2)、mapper代理開發方法(建議使用)

只需要程序員編寫mapper接口(就是dao接口)
程序員在編寫mapper.xml(映射文件)和mapper.java需要遵循一個開發規範:
1、mapper.xml中namespace就是mapper.java的類全路徑。
2、mapper.xml中statement的id和mapper.java中方法名一致。
3、mapper.xml中statement的parameterType指定輸入參數的類型和mapper.java的方法輸入 參數類型一致。
4、mapper.xml中statement的resultType指定輸出結果的類型和mapper.java的方法返回值類型一致。

查詢緩存

MyBatis提供查詢緩存,用於減輕數據壓力,提高數據庫性能。
MyBatis提供一級緩存,和二級緩存。

這裏寫圖片描述

一級緩存是SqlSession級別的緩存。在操作數據庫時需要構造 sqlSession對象,在對象中有一個數據結構(HashMap)用於存儲緩存數據。不同的sqlSession之間的緩存數據區域(HashMap)是互相不影響的。

二級緩存是mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。

爲什麼要用緩存?
如果緩存中有數據就不用從數據庫中獲取,大大提高系統性能。

一級緩存工作原理(SqlSession級別)

這裏寫圖片描述

第一次發起查詢用戶id爲1的用戶信息,先去找緩存中是否有id爲1的用戶信息,如果沒有,從數據庫查詢用戶信息。
得到用戶信息,將用戶信息存儲到一級緩存中。

如果sqlSession去執行commit操作(執行插入、更新、刪除),會清空SqlSession中的一級緩存,這樣做的目的爲了讓緩存中存儲的是最新的信息,避免髒讀。

第二次發起查詢用戶id爲1的用戶信息,先去找緩存中是否有id爲1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。


正式開發,是將MyBatis和spring進行整合開發,事務控制在service中。
一個service方法中包括 很多mapper方法調用。

service
{
    //開始執行時,開啓事務,創建SqlSession對象
    //第一次調用mapper的方法findUserById(1)

   //第二次調用mapper的方法findUserById(1),從一級緩存中取數據
    //方法結束,sqlSession關閉
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果是執行兩次service調用查詢相同 的用戶信息,不走一級緩存,因爲session方法結束,sqlSession就關閉,一級緩存就清空。

二級緩存工作原理(mapper級別)

這裏寫圖片描述

首先開啓MyBatis的二級緩存。

sqlSession1去查詢用戶id爲1的用戶信息,查詢到用戶信息會將查詢數據存儲到二級緩存中。

如果SqlSession3去執行相同 mapper下sql,執行commit提交,清空該 mapper下的二級緩存區域的數據。

sqlSession2去查詢用戶id爲1的用戶信息,去緩存中找是否存在數據,如果存在直接從緩存中取出數據。

二級緩存與一級緩存區別,二級緩存的範圍更大,多個sqlSession可以共享一個UserMapper的二級緩存區域。
UserMapper有一個二級緩存區域(按namespace分) ,其它mapper也有自己的二級緩存區域(按namespace分)。
每一個namespace的mapper都有一個二級緩存區域,兩個mapper的namespace如果相同,這兩個mapper執行sql查詢到數據將存在相同 的二級緩存區域中。

useCache配置

在statement中設置useCache=false可以禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認情況是true,即該sql使用二級緩存。

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
  • 1
  • 1

總結:針對每次查詢都需要最新的數據sql,要設置成useCache=false,禁用二級緩存。

刷新緩存(就是清空緩存)

在mapper的同一個namespace中,如果有其它insert、update、delete操作數據後需要刷新緩存,如果不執行刷新緩存會出現髒讀。

設置statement配置中的flushCache=”true” 屬性,默認情況下爲true即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數據庫表中的查詢數據會出現髒讀。
如下:

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
  • 1
  • 1

總結:一般下執行完commit操作都需要刷新緩存,flushCache=true表示刷新緩存,這樣可以避免數據庫髒讀。

分佈式緩存框架Ehcache

Ehcache是一個分佈式緩存框架。(現在僅介紹單機應用在MyBatis,分佈式應用的研究後續補上)

我們系統爲了提高系統併發,性能、一般對系統進行分佈式部署(集羣部署方式)

這裏寫圖片描述

不使用分佈緩存,緩存的數據在各各服務單獨存儲,不方便系統 開發。所以要使用分佈式緩存對緩存數據進行集中管理。

MyBatis無法實現分佈式緩存,需要和其它分佈式緩存框架進行整合。

MyBatis提供了一個cache接口,如果要實現自己的緩存邏輯,實現cache接口開發即可。

MyBatis和ehcache整合,MyBatis和ehcache整合包中提供了一個cache接口的實現類。

Ehcache與MyBatis整合

配置mapper中cache中的type爲ehcache對cache接口的實現類型。

<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">

    <!-- 開啓本mapper的namespace下的二級緩存
    type:指定cache接口的實現類的類型,MyBatis默認使用PerpetualCache
    要和ehcache整合,需要配置type爲ehcache實現cache接口的類型
     -->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在classpath下配置ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore path="F:\develop\ehcache" />
    <defaultCache 
        maxElementsInMemory="1000" 
        maxElementsOnDisk="10000000"
        eternal="false" 
        overflowToDisk="false" 
        timeToIdleSeconds="120"
        timeToLiveSeconds="120" 
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

二級緩存應用場景

對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可採用MyBatis二級緩存技術降低數據庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。

實現方法如下:通過設置刷新間隔時間,由MyBatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,比如設置爲30分鐘、60分鐘、24小時等,根據需求而定。

侷限性 :MyBatis二級緩存對細粒度的數據級別的緩存實現不好,比如如下需求:對商品信息進行緩存,由於商品信息查詢訪問量大,但是要求用戶每次都能查詢最新的商品信息,此時如果使用MyBatis的二級緩存就無法實現當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因爲MyBatis的二級緩存區域以mapper爲單位劃分,當一個商品信息變化會將所有商品信息的緩存數據全部清空。解決此類問題需要在業務層根據需求對數據有針對性緩存。

逆向工程

MyBatis需要程序員自己編寫sql語句,MyBatis官方提供逆向工程 可以針對單表自動生成MyBatis執行所需要的代碼(mapper.java,mapper.xml、po..)

企業實際開發中,常用的逆向工程方式:
由數據庫的表生成java代碼。

代碼自動生成配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自動生成的註釋 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--數據庫連接的信息:驅動類、連接地址、用戶名、密碼 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
            password="mysql">
        </jdbcConnection>
        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
            connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
            userId="yycg"
            password="yycg">
        </jdbcConnection> -->

        <!-- 默認false,把JDBC DECIMAL 和 NUMERIC 類型解析爲 Integer,爲 true時把JDBC DECIMAL 和 
            NUMERIC 類型解析爲java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO類的位置 -->
        <javaModelGenerator targetPackage="cn.itcast.ssm.po"
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作爲包的後綴 -->
            <property name="enableSubPackages" value="false" />
            <!-- 從數據庫返回的值被清理前後的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="cn.itcast.ssm.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作爲包的後綴 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.itcast.ssm.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作爲包的後綴 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定數據庫表 -->
        <table tableName="items"></table>
        <table tableName="orders"></table>
        <table tableName="orderdetail"></table>
        <table tableName="user"></table>

        <!-- 有些表的字段需要指定java類型
         <table schema="" tableName="">
            <columnOverride column="" javaType="" />
        </table> -->
    </context>
</generatorConfiguration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

自動生成代碼的代碼。



import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlmap {

    public void generator() throws Exception{

        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        //指定 逆向工程配置文件
        File configFile = new File("generatorConfig.xml"); 
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                callback, warnings);
        myBatisGenerator.generate(null);

    } 
    public static void main(String[] args) throws Exception {
        try {
            GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
            generatorSqlmap.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

這裏寫圖片描述

打開UserMapper.java,默認生成的Mapper映射接口就是下面這麼多。

public interface UserMapper
{
    //根據查詢條件統計生成結果行數
    int countByExample(UserExample example);

    //根據實體類中字段不爲null的條件進行刪除,條件全部使用=號and條件
    int deleteByExample(UserExample example);

    //通過主鍵進行刪除,這裏最多隻會刪除一條數據
    //單個字段做主鍵時,可以直接寫主鍵的值
    //聯合主鍵時,key可以是實體類,也可以是Map
    int deleteByPrimaryKey(String UserId);

    //插入一條數據
    //支持Oracle序列,UUID,類似Mysql的INDENTITY自動增長(自動回寫)
    //優先使用傳入的參數值,參數值空時,纔會使用序列、UUID,自動增長
    int insert(User record);

    //插入一條數據,只插入不爲null的字段,不會影響有默認值的字段
    //支持Oracle序列,UUID,類似Mysql的INDENTITY自動增長(自動回寫)
    //優先使用傳入的參數值,參數值空時,纔會使用序列、UUID,自動增長
    int insertSelective(User record);

    //根據查詢條件進行查詢
    List<User> selectByExample(UserExample example);

    //根據主鍵進行查詢,必須保證結果唯一
    //單個字段做主鍵時,可以直接寫主鍵的值
    //聯合主鍵時,key可以是實體類,也可以是Map
    User selectByPrimaryKey(String UserId);

    //根據查詢條件進行更新
    //只會更新不是null的數據
    int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example);

    //根據查詢條件進行更新
    //更新所有的數據
    int updateByExample(@Param("record") User record, @Param("example") UserExample example);

    //根據主鍵進行更新
    //只會更新不是null的數據
    int updateByPrimaryKeySelective(User record);

    //根據主鍵進行更新
    //更新所有的數據
    int updateByPrimaryKey(User record);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

參考文獻

http://v.itcast.cn/course/8.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章