1. 程式人生 > >MyBatis框架概述

MyBatis框架概述

實現 property 五個 上下 sources insert truct version except

MyBatis是一個優秀的持久層框架,它對jdbc的操作數據庫的過程進行封裝,使開發者只需要關註SQL本身,而不需要花費精力去處理例如註冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。

實現基本的數據庫操作功能的流程如下:

  1. 編寫xml文件,配置運行環境。
  2. 通過IO流載入xml文件,創建SqlSessionFactory對象(會話工廠)。
  3. 由會話工廠,創建SqlSession對象(會話)。
  4. 通過SqlSession對象,操作數據庫。註意增刪改操作需要提交事務,否則對數據庫做出的修改不會更改數據庫中的記錄。
  5. 最後需要關閉SqlSession對象和IO流,釋放資源。

一、xml配置文件

Mybatis通過xml或註解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射成java對象並返回。

1.MybatisConfig.xml

Mybatis的全局配置文件,主要用於配置Mybatis的運行環境(事務管理器、數據源等)。具體詳情可見Mybatis說明文檔。

下面通過一個簡單的示例,來簡要說明這個配置文件。

 1 <?xml version="1.0" encoding="UTF-8"
?> 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> 3 <configuration> 4 <!--引入外部properties文件 --> 5 <properties resource="db.properties"></properties> 6 <!-- 和spring整合後 environments配置將廢除-->
7 <environments default="development"> 8 <environment id="development"> 9 <!-- 使用jdbc事務管理,事務控制由mybatis管理--> 10 <transactionManager type="JDBC" /> 11 <!-- 數據庫連接池,由mybatis管理--> 12 <dataSource type="POOLED"> 13 <property name="username" value="${username}"/> 14 <property name="password" value="${password}"/> 15 <property name="url" value="${url}"/> 16 <property name="driver" value="${driver}"/> 17 <property name="poolMaximumActiveConnections" value="${maxActive}"/> 18 </dataSource> 19 </environment> 20 </environments> 21 <!-- 加載映射文件 --> 22 <mappers> 23 <mapper resource="StudentMapper.xml"/> 24 </mappers> 25 </configuration>
  1. 頭文件(1~2行):第1行是xml聲明,聲明該xml文件的字符集為UTF-8;第2行是DTD文件類型聲明(外部DTD),用於約束該xml文件的結構。引用的DTD約束的格式為<!DOCTYPE 根元素 SYSTEM "DTD文件路徑">(本地文件),或<!DOCTYPE 根元素 PUBLIC "DTD名稱" "DTD文件URL">(公共文件),這裏是約束configuaration元素的結構為鏈接中的DTD文件所約束的那樣。
  2. properties元素(5行):這裏是用於引入外部properties文件。其實還可以在properties元素中定義一些屬性,但不建議這麽做,最好還是把所有屬性放在外部文件中。
  3. environments元素(7~20行):用於配置要創建的SqlSessionFactory實例的環境。每個數據庫對應一個SqlSessionFactory實例,每個SqlSessionFactory實例只能選擇一種環境(environments元素中可以定義多個environment元素)。
    • 第7行:默認的環境id。
    • 第8行:定義一個environment元素,並設定環境id。
    • 第10行:事務管理器的配置,可選"JDBC"或"MANAGED"。【註:Spring+Mybatis不需要配置事務管理器,因為Spring會用自帶管理器覆蓋這些配置】
      • "JDBC":直接使用了JDBC的提交和回滾設置,它依賴於從數據源得到的連接來管理事務作用域。
      • "MANAGED":不提交或回滾一個連接,而是讓容器來管理事務的整個生命周期,默認情況下它會關閉連接。
    • 第12~18行:數據源的配置。第12行是配置數據源類型,內置了"UNPOOLED","POOLED"和"JNDI";第13~18行是設定數據源。
      • "UNPOOLED":不使用數據庫連接池。只有driver,url,username,password,defaultTransactionIsolationLevel五個屬性,其中最後一個屬性是指默認的連接事務隔離級別。
      • "POOLED":使用數據庫連接池。除了"UNPOOLED"中的5個屬性之外,還多了一些連接池屬性,比如poolMaximumActiveConnections(最大活動連接數)、poolMaximumIdleConnections(最大空閑連接數)等(詳見說明文檔)。
      • "JNDI":為了能在如EJB或應用服務器這類容器中使用,容器可以集中或在外部配置數據源,然後放置一個JNDI上下文的引用。只需"initial_context"和"data_source"兩個屬性。
      • 另外,也可以將type設置為一個數據源類,使用任何第三方數據源。
  4. mappers元素(22~24行):用於設定映射文件路徑。可以通過classpath相對路徑、文件系統絕對路徑設定映射文件,還可以通過類名、包名設定映射接口。

2.StudentMapper.xml

sql映射文件,主要用於實現數據庫操作的具體細節。此文件需要在MybatisConfig.xml中加載。

下面通過一個簡單的示例,來簡要說明這個配置文件。

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 3 <mapper namespace="cn.cage.student.StudentDAO">
 4 
 5     <insert id="addStu" parameterType="cn.cage.student.Student">
 6         INSERT INTO students
 7         (name,qq,major,entrytime,gra_school,id_jnshu,daily_url,desire,bro_jnshu,knowfrom)
 8         VALUES
 9         (#{name},#{qq},#{major},#{entryTime},#{school},#{jnshuId},#{dailyUrl},#{desire},#{jnshuBro},#{knowFrom})
10     </insert>
11 
12     <delete id="delStuById" parameterType="long">
13         DELETE FROM students WHERE
14         id=#{id}
15     </delete>
16     <delete id="delStu" parameterType="cn.cage.student.Student"></delete>
17 
18     <update id="updateStu">
19         UPDATE students SET
20         name=#{stu.name},qq=#{stu.qq},major=#{stu.major},entrytime=#{stu.entryTime},gra_school=#{stu.school},id_jnshu=#{stu.jnshuId}
21         ,daily_url=#{stu.dailyUrl},desire=#{stu.desire},bro_jnshu=#{stu.jnshuBro},knowfrom=#{stu.knowFrom}
22         WHERE id=#{id}
23     </update>
24 
25     <select id="queryStuById" resultMap="stuMap">
26         SELECT
27         id,create_at,update_at,name,qq,major,entrytime,gra_school,id_jnshu,daily_url,desire,bro_jnshu,knowfrom
28         FROM students WHERE id=#{id}
29     </select>
30     <select id="queryStuByName" resultMap="stuMap">
31         SELECT
32         id,create_at,update_at,name,qq,major,entrytime,gra_school,id_jnshu,daily_url,desire,bro_jnshu,knowfrom
33         FROM students WHERE name=#{name}
34     </select>
35     <select id="queryStuByJnshu" resultMap="stuMap">
36         SELECT
37         id,create_at,update_at,name,qq,major,entrytime,gra_school,id_jnshu,daily_url,desire,bro_jnshu,knowfrom
38         FROM students WHERE major=#{major} and id_jnshu=#{jnshuId}
39     </select>
40     <select id="queryStu"></select>
41 
42     <resultMap type="cn.cage.student.Student" id="stuMap">
43         <constructor>
44             <arg column="name" javaType="String" jdbcType="VARCHAR" />
45             <arg column="major" javaType="String" jdbcType="VARCHAR" />
46             <arg column="id_jnshu" javaType="int" jdbcType="INTEGER" />
47         </constructor>
48         <id property="id" column="id" />
49         <result property="createTime" column="create_at" />
50         <result property="updateTime" column="update_at" />
51         <result property="qq" column="qq" />
52         <!-- 此處雖然javaType為String,jdbcType為DATE,但並不需要特別的轉換就可以映射 -->
53         <result property="entryTime" column="entrytime" />
54         <result property="school" column="gra_school" />
55         <result property="dailyUrl" column="daily_url" />
56         <result property="desire" column="desire" />
57         <result property="jnshuBro" column="bro_jnshu" />
58         <result property="knowFrom" column="knowfrom" />
59     </resultMap>
60     
61 </mapper>

>>關於類文件的別名:在MybatisConfig.xml的Configuration中,添加屬性typeAlias即可:

<typeAlias type="cn.cage.Student" alias="Student"/>
  1. 頭文件(1~2行):同上,這裏是約束mapper元素的結構為鏈接中的DTD文件所約束的那樣。
  2. 命名空間(3行):最基本的意義,是給這個mapper命名用於區分;更為高級的用法,則是接口綁定(面向接口編程)。namespace的命名方式分為兩種:完全限定名和短名稱。本例中使用的就是完全限定名。使用短名稱時,必須確保這個短名稱在系統中是唯一的,否則只能使用完全限定名。
    • 接口綁定:將namespace設為DAO接口,將接口中的方法都通過mapper中的元素實現(元素id與接口方法一一對應),就可以不用寫DAO實現類,Mybatis會通過綁定自動找到要執行的sql語句。
  3. mapper頂級元素(4~61行):mapper中有8個頂級元素,分別是insert,delete,update,select(映射增刪改查語句),sql(重用sql語句塊),resultMap(描述如何從結果集中加載對象),cache(緩存配置),cache-ref(其他namespace緩存配置)。
    1. insert、delete、update(5~23行):基本格式就是<元素名 元素屬性>sql語句</元素名>。其中,元素名即為insert/delete/update中的一個,可設定的元素屬性有9個,這裏只列出5個,一般設定1,2即可。在sql語句中,用#{}表示占位符,執行時將#{}替換為?,然後將括號內的參數傳遞給?。
      常用元素屬性:(詳見說明文檔)
      1. id:元素標識。
      2. parameterType:接受的參數類型,可為完全限定名或別名。默認為unset。傳入多個參數時,這個屬性應忽略,在#{}中直接以各參數名表示。(如:18~23行)
      3. flushCache:為true時,調用sql會清空本地緩存和二級緩存。在insert,delete,update中默認為true,在select中默認為false。
      4. timeout:驅動程序等待數據庫返回結果的最大時間,超出則拋出異常。默認為unset。
      5. statementType:可選STATEMENT/PREPARED/CALLABLE,分別讓Mybatis使用Statement,PreparedStatement,CallableStatement。默認為PREPARED。
    2. select(25~40行):基本格式同上。select可設定的元素屬性有13個,除了上述的5個屬性之外,還有2個常用屬性。當數據庫列名與POJO屬性名不一致時,可以在sql語句中使用別名(as),或使用resultMap。
      1. resultType:此select語句的返回值類型。如果返回集合,應該寫集合包含的類型。
      2. resultMap:外部resultMap的引用。和resultType不能同時使用。
    3. resultMap(42~59行):基本屬性有id,type和autoMapping,分別是本resultMap的標識,對應的POJO類,是否自動映射。
      resultMap有6個可用元素:(詳見說明文檔)
      1. constructor:在類實例化時,註入結果到構造方法中。其中,idArg是ID參數(詳見下一條),arg是註入到構造函數的普通結果。
        其中,idArg和arg常用屬性如下:
        • column:對應數據庫中的列名。
        • javaType:完全限定名/別名。映射到HashMap時必須指定,其他時候可以省略。
        • name:構造函數的形式參數名。
      2. id:id會將結果標記為標識符(給結果取個名字),以便在比較對象時使用。這可以提升整體性能,特別是緩存和嵌入結果映射(比如聯合映射) 。
      3. result:註入到屬性的普通結果。
        id和result的常用屬性如下:
        • property:POJO類中的屬性名。
        • column:對應數據庫中的列。
        • javaType:完全限定名/別名。映射到HashMap時必須指定,其他時候可以省略。
      4. association:關聯。
      5. collection:集合。
        主要處理多個表之間的聯合映射。
      6. discriminator:鑒別器。可以根據某一列結果的值的不同,來決定接下來的行為(比如將另一列的結果映射到某個POJO屬性)。
        4,5,6三個屬性本例中無需使用,暫不詳細介紹。詳情見說明文檔。

二、Java代碼

通過StudentMapper.xml進行DAO接口綁定後,不需要編寫接口的實現類,就能直接根據接口規定的參數列表傳入參數,進行數據操作。值得註意的是,如果不編寫實現類的話,接口中增刪改函數的返回值設定是無用的,只能返回SqlSession類中對應函數指定的返回值類型。

數據庫操作在代碼中的實現步驟大體如下:

  1. 創建IO流,通過Resources類中的getResourceAsStream方法載入全局配置文件(MybatisConfig.xml)。
  2. 由SqlSessionFactoryBuilder對象的build方法,創建SqlSessionFactory對象。
  3. 由SqlSessionFactory對象的openSession方法,創建SqlSession對象。
  4. 由SqlSession對象,操作數據庫。
  5. 關閉SqlSession對象,然後關閉IO流。

下面主要講解一下通過SqlSession對象操作數據庫的內容。

1.SqlSession類中的常用方法

目前主要用到過的方法有:

  1. insert,delete,update:對數據庫進行增/刪/改操作。註意:進行了這些操作之後,必須調用commit()提交事務,否則數據庫記錄不會改變。
    返回值:int,操作所影響的行數。
    參數:("sqlID",param):
    1. sqlID:StudentMapper.xml中對應的sql元素的id,若有多個mapper,可以通過mapper.sqlID的形式指定。
    2. param:若對應的接口函數只需要傳入一個參數,直接將這個參數傳入即可。【最好用變量的形式傳入。直接傳常量可能會導致類型轉換異常】
      若需傳入多個參數,則應該用HashMap<String,Object>傳入參數。其中,String是mapper.xml中的參數名(#{xx}),Object是參數值。將多個參數放入map後,將map作為參數傳入即可。
  2. commit():刷新批處理語句並提交數據庫連接。在增刪改之後必須調用。
  3. selectOne("sqlID",param):參數意義同上。返回值:mapper.xml中規定的返回值類型。
  4. selectList("sqlID",param):同上。返回值:List<E>,其中E為mapper.xml中規定的返回值類型。
  5. close():關閉會話,釋放資源。

2.註意事項

  1. POJO類中的構造方法的參數類型,如果是基本數據類型,應該寫成其包裝類的形式(比如int寫為Integer)。因為mapper.xml中的javaType會自動轉為完全限定名(比如int轉為java.lang.Integer)。如果在resultMap中定義了constructor元素,映射到POJO類時會是完全限定名的類型,如果構造方法中的參數類型不是包裝類,就會報錯(找不到參數類型為xxx的構造函數)。
  2. 務必在insert/delete/update之後執行commit!否則一切操作都不會在數據庫中生效!

最後,對上述內容舉例說明:上述配置文件的測試代碼。

技術分享
  1 /**
  2  * @FileName:MybatisStuImpl.java
  3  * @description:
  4  * @author Cage Yang
  5  * @version 
  6  * Modified Date:2017年8月23日
  7  * Why & What is modified: <修改原因描述>
  8  */
  9 package cn.cage.student;
 10 
 11 import static org.junit.Assert.assertEquals;
 12 import static org.junit.Assert.assertNull;
 13 
 14 import java.io.InputStream;
 15 import java.util.HashMap;
 16 import java.util.Iterator;
 17 import java.util.List;
 18 
 19 import org.apache.ibatis.io.Resources;
 20 import org.apache.ibatis.session.SqlSession;
 21 import org.apache.ibatis.session.SqlSessionFactory;
 22 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 23 import org.junit.AfterClass;
 24 import org.junit.BeforeClass;
 25 import org.junit.Test;
 26 
 27 /**
 28  * @ClassName MybatisStuImpl
 29  * @description 測試通過Mybatis接口綁定自動生成的數據操作類的運行情況。
 30  * @author Cage Yang
 31  */
 32 public class MybatisStuImpl {
 33     static InputStream in = null;
 34     static SqlSession sqlSession = null;
 35 
 36     /**
 37      * @description 創建出SqlSession實例
 38      * @throws java.lang.Exception
 39      */
 40     @BeforeClass
 41     public static void setUpBeforeClass() throws Exception {
 42         String resource = "MybatisConfig.xml";
 43         in = Resources.getResourceAsStream(resource);
 44         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
 45         sqlSession = sqlSessionFactory.openSession();
 46     }
 47 
 48     /**
 49      * @description 釋放資源
 50      * @throws java.lang.Exception
 51      */
 52     @AfterClass
 53     public static void tearDownAfterClass() throws Exception {
 54         sqlSession.close();
 55         in.close();
 56     }
 57 
 58     @Test
 59     public void testAddStu() {
 60         Student stu = RandomStudent.getStudent();
 61         stu.setMajor("java");
 62         stu.setJnshuId(1501);
 63         assertEquals("插入失敗!", 1, sqlSession.insert("addStu", stu));
 64         sqlSession.commit();
 65         HashMap<String, Object> param = new HashMap<String, Object>();
 66         param.put("major", "java");
 67         param.put("jnshuId", 1501);
 68         Student stu2 = sqlSession.selectOne("queryStuByJnshu", param);
 69         assertEquals("插入錯誤,或查詢byJnshu出錯", stu, stu2);
 70     }
 71 
 72     @Test
 73     public void testDelStuById() {
 74         long id = 4;
 75         Student student = sqlSession.selectOne("queryStuById", id);
 76         System.out.println(student.getEntryTime());
 77         assertEquals("刪除失敗!", 1, sqlSession.delete("delStuById", id));
 78         sqlSession.commit();
 79         assertNull("刪除錯誤,或查詢byId出錯", sqlSession.selectOne("queryStuById", id));
 80     }
 81 
 82     @Test
 83     public void testUpdateStu() {
 84         long id = 5;
 85         Student stu = sqlSession.selectOne("queryStuById", id);
 86         stu.setDesire("哈哈哈哈哈哈哈哈");
 87         HashMap<String, Object> param = new HashMap<String, Object>();
 88         param.put("stu", stu);
 89         param.put("id", id);
 90         assertEquals("更新失敗!", 1, sqlSession.update("updateStu", param));
 91         sqlSession.commit();
 92         assertEquals("更新錯誤,或查詢byId出錯", "哈哈哈哈哈哈哈哈", ((Student) sqlSession.selectOne("queryStuById", id)).getDesire());
 93     }
 94 
 95     @Test
 96     public void testQueryStuByName() {
 97         List<Student> list = sqlSession.selectList("queryStuByName", "王五");
 98         for (Iterator<Student> iterator = list.iterator(); iterator.hasNext();) {
 99             Student student = (Student) iterator.next();
100             if (student.getJnshuId() == 1111) {
101                 assertEquals("查詢byName出錯", "2017-08-06", student.getEntryTime());
102             }
103         }
104     }
105 }
JUnit4測試代碼

三、與Spring-JdbcTemplate的比較

1.Mybatis必須用IO流載入xml配置文件,JdbcTemplate可以直接載入。

2.Mybatis可以無需編寫數據操作類、通過配置文件綁定接口,JdbcTemplate必須編寫類實現數據操作接口。

3.Mybatis可使用自帶數據源類,JdbcTemplate只能使用外部數據源類。

4.mapper中的元素:增刪改查元素對應StudentDAOImpl中的各實現方法,resultMap對應JdbcTemplate中的QueryStuRowMapper。

Mybatis說明文檔:http://www.mybatis.org/mybatis-3/zh/getting-started.html

l

MyBatis框架概述