天天看點

Mybatis從入門到精通 (一)Mybatis

Mybatis

Mybatis簡介

MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,并且改名為MyBatis 。2013年11月遷移到Github。

MyBatis 是支援普通SQL、存儲過程以及進階映射的優秀的持久層架構。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設定參數以及擷取結果集,使開發者隻需要關注sql本身。MyBatis 使用簡單的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成資料庫中的記錄。

Mybatis架構

Mybatis從入門到精通 (一)Mybatis
1、mybatis配置SqlMapConfig.xml,此檔案作為mybatis的全局配置檔案,配置了mybatis的運作環境等資訊。mapper.xml檔案即sql映射檔案,檔案中配置了操作資料庫的sql語句。此檔案需要在SqlMapConfig.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程式設計中對結果的解析處理過程。
           

Mybatis入門

我們先來寫一個入門程式,來了解一下Mybatis具體的使用流程,具體的細節會在後面一一講解。

第一步:建立資料庫及資料庫表,以供我們來操作。  


CREATE DATABASE mybatis; 
CREATE TABLE `user` (
  `id` INT() NOT NULL AUTO_INCREMENT,
  `username` VARCHAR() NOT NULL COMMENT '使用者名稱',
  `birthday` DATE DEFAULT NULL COMMENT '生日',
  `sex` CHAR() DEFAULT NULL COMMENT '性别',
  `address` VARCHAR() DEFAULT NULL COMMENT '位址',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT= DEFAULT CHARSET=utf8;


INSERT INTO `user` VALUES ('1', '王五', NULL, '2', NULL);
INSERT INTO `user` VALUES ('10', '張三', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES ('16', '張小明', NULL, '1', '河南鄭州');
INSERT INTO `user` VALUES ('22', '陳小明', NULL, '1', '河南鄭州');
INSERT INTO `user` VALUES ('24', '張三豐', NULL, '1', '河南鄭州');
INSERT INTO `user` VALUES ('25', '陳小明', NULL, '1', '河南鄭州');
INSERT INTO `user` VALUES ('26', '王五', NULL, NULL, NULL);

           
第二步:建立java項目,建立Mybatis核心配置檔案sqlMapConfig.xml 
       SqlMapConfig.xml是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配置将廢除 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事務管理 -->
            <transactionManager type="JDBC" />
            <!-- 資料庫連接配接池 -->
            <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="123456" />
            </dataSource>
        </environment>
    </environments>
</configuration>
           
第三步:建立和資料庫表對應的pojo類 


public class User implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = L;
    private Integer id;
    private String username;// 使用者姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 位址


    public Integer getId() {
        return id;
    }
    public void setId(Integer 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 + "]";
    }
}
           
第四步:建立sql映射檔案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,還有一個很重要的作用,後面會講 --> 
<mapper namespace="test"> 
    <!--id為sql語句的id,parameterType為參數類型,resultType為結果類型,#{}為占位符-->
    <!--根據id查找使用者,#{}裡面可以随便寫-->
    <select id="findUserById" parameterType="Integer" resultType="com.cad.domain.User">
        select * from user where id =#{id}
    </select>
</mapper>
           
第五步:sqlMapConfig配置檔案中要引入映射檔案   

    <mappers>
        <mapper resource="sqlmap/User.xml"/>
    </mappers>
           
第六步,測試程式,簡單的操作 

public class MybatisTest {

    @Test
    public void test() throws IOException { 

        String resource="config/sqlMapConfig.xml"; 
        //加配置檔案
        InputStream in=Resources.getResourceAsStream(resource);
        //建立SqlSessionFactoryBuilder對象用來建立工廠
        SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
        //建立工廠
        SqlSessionFactory factory=builder.build(in); 
        //建立Session
        SqlSession sqlSession=factory.openSession(); 
        //進行查詢
        User user=(User)sqlSession.selectOne("test.findUserById",);
        System.out.println(user);
        sqlSession.close();
    }
}
           

模糊查詢

<select id="findUserByUsername" parameterType="String" resultType="com.cad.domain.User">
        <!-- ${}裡面隻能是value -->
        select * from user where username like '%${value}%'
    </select>

           
public class MybatisTest {

    @Test
    public void test() throws IOException {
        String resource="config/sqlMapConfig.xml";
        InputStream in=Resources.getResourceAsStream(resource);
        SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
        SqlSessionFactory factory=builder.build(in);
        SqlSession sqlSession=factory.openSession(); 
        List<User> users=sqlSession.selectList("findUserByUsername","五");
        for(User user:users) {
            System.out.println(user);
        }
        sqlSession.close();
    } 

}
           

添加

<!-- 添加操作 -->
    <insert id="insertUser" parameterType="com.cad.domain.User">
        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>
           
public class MybatisTest {

    @Test
    public void test() throws IOException {
        String resource="config/sqlMapConfig.xml";
        InputStream in=Resources.getResourceAsStream(resource);
        SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
        SqlSessionFactory factory=builder.build(in);
        SqlSession sqlSession=factory.openSession();  
        User user=new User();
        user.setUsername("齊天大聖");
        user.setBirthday(new Date()); 
        user.setSex("男");
        user.setAddress("花果山");
        sqlSession.insert("insertUser", user);
        sqlSession.commit();
        sqlSession.close();
    } 

}
           

添加并傳回資料庫自增主鍵

<!-- 儲存使用者 -->
<insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">
    <!-- selectKey 标簽實作主鍵傳回 -->
    <!-- keyColumn:主鍵對應的表中的哪一列 -->
    <!-- keyProperty:主鍵對應的pojo中的哪一個屬性 -->
    <!-- order:設定在執行insert語句前執行查詢id的sql,還是在執行insert語句之後執行查詢id的sql -->
    <!-- resultType:設定傳回的id的類型 -->
    <!--LAST_INSERT_ID():是mysql的函數,傳回auto_increment自增列新記錄id值。-->
    <selectKey keyColumn="id" keyProperty="id" order="AFTER"
        resultType="int">
        SELECT LAST_INSERT_ID()
    </selectKey>
    INSERT INTO `user`
    (username,birthday,sex,address) VALUES
    (#{username},#{birthday},#{sex},#{address})
</insert>
           

修改

<!-- 修改操作 -->
<update id="updateUser" parameterType="com.cad.domain.User">
    update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
           
public class MybatisTest {

    @Test
    public void test() throws IOException {
        String resource="config/sqlMapConfig.xml";
        InputStream in=Resources.getResourceAsStream(resource);
        SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
        SqlSessionFactory factory=builder.build(in);
        SqlSession sqlSession=factory.openSession();  
        User user=new User();
        user.setId();
        user.setUsername("天蓬元帥");
        user.setBirthday(new Date()); 
        user.setSex("女");
        user.setAddress("銀河");
        sqlSession.update("updateUser", user);
        sqlSession.commit();
        sqlSession.close();
    } 

}
           

删除

<!-- 删除操作 -->
<delete id="deleteUser" parameterType="Integer">
    delete from user where id=#{id}
</delete>
           
public class MybatisTest {

    @Test
    public void test() throws IOException {
        String resource="config/sqlMapConfig.xml";
        InputStream in=Resources.getResourceAsStream(resource);
        SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
        SqlSessionFactory factory=builder.build(in);
        SqlSession sqlSession=factory.openSession();  
        sqlSession.delete("deleteUser", );
        sqlSession.commit();
        sqlSession.close();
    } 

}
           

Mapper動态代理方式

Mapper接口開發方法隻需要程式員編寫Mapper接口(相當于Dao接口),由Mybatis架構根據接口定義建立接口的動态代理對象。

1.編寫Mapper檔案 

<?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="com.cad.domain.UserMapper">
    <select id="findUserById" parameterType="int" resultType="com.cad.domain.User">
        select * from user where id =#{id}
    </select>

    <!-- 模糊查詢 -->
    <select id="findUserByUsername" parameterType="String" resultType="com.cad.domain.User">
        <!-- ${}裡面隻能是value -->
        select * from user where username like '%${value}%'
    </select> 

    <!-- 添加操作 -->
    <insert id="insertUser" parameterType="com.cad.domain.User">
        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>

    <!-- 修改操作 -->
    <update id="updateUser" parameterType="com.cad.domain.User">
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
    </update>

    <!-- 删除操作 -->
    <delete id="deleteUser" parameterType="Integer">
        delete from user where id=#{id}
    </delete>
</mapper>
           
建立Mapper接口 

public interface UserMapper {
    public User findUserById(int id);
    public List<User> findUserByUsername(String username); 
    public void insertUser(User user); 
    public void deleteUser(Integer id);
}
           
3.核心配置檔案中引入映射檔案 

    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
           
測試  
public class MybatisTest {

    @Test
    public void test() throws IOException {
        String resource="config/sqlMapConfig.xml";

        InputStream in=Resources.getResourceAsStream(resource);
        SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
        SqlSessionFactory factory=builder.build(in);
        SqlSession sqlSession=factory.openSession();  
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User user=userMapper.findUserById();
        System.out.println(user);
    } 

}
           

Mapper接口開發需要遵循以下規範:

1、  Mapper.xml檔案中的namespace與mapper接口的類路徑相同,否則會找不到接口,也就無法生成代理對象。
2、  Mapper接口方法名和Mapper.xml中定義的每個方法的id相同 
3、  Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
4、  Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同 
           

開發中使用這種方式來進行開發,簡便快捷,代碼複用性高,免去很多重複繁瑣代碼。

Mybatis一些小細節

通過入門例子可以知道,每個基于 MyBatis 的應用都是以一個 SqlSessionFactory 的執行個體為中心的。SqlSessionFactory 的執行個體可以通過 SqlSessionFactoryBuilder 獲得。而 SqlSessionFactoryBuilder 則可以從 XML 配置檔案或一個預先定制的 Configuration 的執行個體建構出 SqlSessionFactory 的執行個體。

從 XML 中建構 SqlSessionFactory

我們可以直接通過加載核心配置檔案來建立SqlSessionFactory 

    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
           

不使用XML建構 SqlSessionFactory

//自己實作擷取資料源
    DataSource dataSource = xxx; 
    //擷取事務
    TransactionFactory transactionFactory = new JdbcTransactionFactory();
    Environment environment = new Environment("development", transactionFactory, dataSource);
    Configuration configuration = new Configuration(environment);
    configuration.addMapper(BlogMapper.class);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
           
configuration 添加了一個映射器類(mapper class)。映射器類是 Java 類,它們包含 SQL 映射語句的注解進而避免了 XML 檔案的依賴。不過,由于 Java 注解的一些限制加之某些 MyBatis 映射的複雜性,XML 映射對于大多數進階映射來說仍然是必須的。有鑒于此,如果存在一個對等的 XML 配置檔案的話,MyBatis 會自動查找并加載它(這種情況下, BlogMapper.xml 将會基于類路徑和 BlogMapper.class 的類名被加載進來)。

    了解即可,開發中使用xml格式。 
           

主要類的生命周期

SqlSessionFactoryBuilder,這個類可以被執行個體化、使用和丢棄,一旦建立了 SqlSessionFactory,就不再需要它了,可以重用 SqlSessionFactoryBuilder 來建立多個 SqlSessionFactory 執行個體,但是最好還是不要讓其一直存在以保證所有的 XML 解析資源開放給更重要的事情。

SqlSessionFactory 一旦被建立就應該在應用的運作期間一直存在,沒有任何理由對它進行清除或重建。使用 SqlSessionFactory 的最佳實踐是在應用運作期間不要重複建立多

每個線程都應該有它自己的 SqlSession 執行個體。SqlSession 的執行個體不是線程安全的,是以是不能被共享的。

每執行一次請求都應該開啟一次sqlSession執行個體。當然要記得在finally中關閉來節省資源。

Mybatis的核心配置檔案

MyBatis 的配置檔案包含了MyBatis的設定和屬性資訊。文檔的結構和順序如下:

properties 屬性
settings 設定
typeAliases 類型命名
typeHandlers 類型處理器
objectFactory 對象工廠
plugins 插件
environments(環境集合屬性對象)
    environment(環境子屬性對象)
        transactionManager(事務管理)
        dataSource(資料源)
databaseIdProvider 資料庫廠商辨別
mappers 映射器 
           

properties(屬性)

我們可以将資料源等資料配置在外部檔案中,然後引用。

//db.peoperties檔案

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:/mybatis?characterEncoding=utf-
    jdbc.username=root
    jdbc.password=root
           
//引入檔案
    <properties resource="db.properties"/>
    <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </dataSource>
           

如果屬性在不隻一個地方進行了配置,那麼 MyBatis 将按照下面的順序來加載:

  • 在 properties 元素體内指定的屬性首先被讀取。
  • 然後根據 properties 元素中的 resource 屬性讀取類路徑下屬性檔案或根據 url 屬性指定的路徑讀取屬性檔案,并覆寫已讀取的同名屬性。

typeAliases(類型别名)

類型别名是為 Java 類型設定一個短的名字。它隻和 XML 配置有關,存在的意義僅在于用來減少類完全限定名的備援。

<typeAliases>
        <!-- 單個别名定義 -->
        <typeAlias alias="user" type="cn.cad.pojo.User" /> 
        <!-- 批量别名定義,掃描整個包下的類,别名為類名(大小寫不敏感) -->
        <package name="cn.itcast.mybatis.pojo" />
    </typeAliases>

    在mapper檔案中就可以使用别名user。
           

Mybatis已經為許多常見的 Java 類型内建了相應的類型别名。它們都是大小寫不敏感的,例如Srting,integer等。

插件(plugins)

MyBatis 允許你在已映射語句執行過程中的某一點進行攔截調用。預設情況下,MyBatis 允許使用插件來攔截的方法調用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
           

這些類中方法的細節可以通過檢視每個方法的簽名來發現,或者直接檢視 MyBatis 的發行包中的源代碼。 假設你想做的不僅僅是監控方法的調用,那麼你應該很好的了解正在重寫的方法的行為。 因為如果在試圖修改或重寫已有方法的行為的時候,你很可能在破壞 MyBatis 的核心子產品。 這些都是更低層的類和方法,是以使用插件的時候要特别當心

配置環境(environments)

MyBatis 可以配置成适應多種環境,這種機制有助于将 SQL 映射應用于多種資料庫之中, 現實情況下有多種理由需要這麼做。例如,開發、測試和生産環境資料庫需要有不同的配置;

不過要記住:盡管可以配置多個環境,每個 SqlSessionFactory 執行個體隻能選擇其一。是以,如果你想連接配接兩個資料庫,就需要建立兩個 SqlSessionFactory 執行個體,每個資料庫對應一個。

配置環境的關鍵點:

預設的環境 ID(比如:default=”development”)。
    每個 environment 元素定義的環境 ID(比如:id=”development”)。
    事務管理器的配置(比如:type=”JDBC”)。
    資料源的配置(比如:type=”POOLED”) 
           
  • MyBatis 中有兩種類型的事務管理器(也就是 type=”[JDBC|MANAGED]”)
  • Mybatis三種内建的資料源類型(也就是 type=”[UNPOOLED|POOLED|JNDI]”)

我們都是使用預設的,具體的參數意思可以檢視官方文檔。

映射器(mappers)

映射器配置會告訴了 MyBatis 去哪裡找映射檔案。

<mapper resource=" " />:使用相對于類路徑的資源(現在的使用方式)

<mapper url="file:///var/mappers/AuthorMapper.xml"/>:使用絕對路徑,基本不會用 

<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>:使用mapper接口類路徑。注意:此種方法要求mapper接口名稱和mapper映射檔案名稱相同,且放在同一個目錄中。 

<package name="xxx"/>:注冊指定包下的所有mapper接口,此種方法要求mapper接口名稱和mapper映射檔案名稱相同,且放在同一個目錄中。