Mybatis 入門

一、Mybatis 介紹

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

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

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

二、使用 JDBC 編程問題總結

  • JDBC 編程步驟
  1. 注冊數據庫驅動
  2. 創建并獲取數據庫連接 connection
  3. 創建 jdbc statement 對象
  4. 書寫 sql 語句
  5. 設置 sql 語句中的參數(使用 preparedStatement)
  6. 通過 statement 執行 sql 并獲取結果
  7. 對 sql 執行結果進行解析處理
  8. 釋放資源 (resultSet、preparedStatement、connection)
  • JDBC 程序示例
 public static void main(String[] args) {
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    try {
       // 注冊數據庫驅動
       Class.forName("com.mysql.jdbc.Driver");
       // 通過驅動管理類獲取數據庫連接
       connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
       // 定義sql語句  ?表示占位符
       String sql = "select * from user where username = ?";
       // 獲取預處理statement
       preparedStatement = connection.prepareStatement(sql);
       // 設置參數,第一個參數為sql語句中參數的序號(從1開始),第二個參數為設置的參數值
       preparedStatement.setString(1, "王五");
       // 向數據庫發出sql執行查詢,查詢出結果集
       resultSet = preparedStatement.executeQuery();
       // 遍歷查詢結果集
       while (resultSet.next()) {
           System.out.println(resultSet.getString("id") + "  " + resultSet.getString("username"));
       }
    } catch (Exception e) {
       e.printStackTrace();
    } finally {
       // 釋放資源
       if (resultSet != null) {
           try {
              resultSet.close();
           } catch (SQLException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
           }
       }
       if (preparedStatement != null) {
           try {
              preparedStatement.close();
           } catch (SQLException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
           }
       }
       if (connection != null) {
           try {
              connection.close();
           } catch (SQLException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
           }
       }
    }
}

上邊使用jdbc的原始方法(未經封裝)實現了查詢數據庫表記錄的操作。

  • JDBC 問題總結如下:
  1. 數據庫連接創建、釋放頻繁造成系統資源浪費,從而影響系統性能。可使用數據庫連接池解決此問題。
  2. Sql語句在代碼中硬編碼,造成代碼不易維護,實際應用中sql變化的可能較大,sql變動需要改變java代碼。
  3. 使用preparedStatement向占位符傳參數存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護。
  4. 對結果集解析存在硬編碼(查詢列名),sql變化導致解析代碼變化,系統不易維護,如果能將數據庫記錄封裝成pojo對象解析比較方便。

三、Mybatis 架構

  1. mybaits 配置 ------> SqlMapConfig.xml,此文件作為 mybaits 的全局配置文件,配置了 mybatis 的運行環境等信息。mapper.xml 文件即 sql 映射文件,文件中配置了操作數據庫的 sql 語句。此文件需要在 SqlMapConfig.xml 中加載。
  2. 通過mybatis環境等配置信息構造SqlSessionFactory(會話工廠)
  3. 由會話工廠創建sqlSession(會話)操作數據庫需要通過sqlSession進行
  4. mybaits底層自定義了 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編程中對結果的解析處理過程。
  • jar包介紹 (mybatis核心包、mybatis依賴包、數據庫驅動包)
mybatis核心包
mybaits依賴包
  • 配置文件參考

log4j.properties,mybatis默認使用log4j作為輸出日志信息。

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

db.properties

 jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root

SqlMapConfig.xml,mybatis核心配置文件,配置文件內容為數據源、事務管理。(引用db.properties)

  <?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>
    <!-- 使用resource屬性加載外部配置文件 -->
    <properties resource="db.properties">
       <!-- 在properties內部用property定義屬性 -->
       <!-- 如果外部配置文件有該屬性,則內部定義屬性被外部屬性覆蓋 -->
       <property name="jdbc.username" value="root123" />
       <property name="jdbc.password" value="root123" />
    </properties>

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

    <!-- 和spring整合后 environments配置將廢除 -->
    <environments default="development">
       <environment id="development">
           <!-- 使用jdbc事務管理 -->
           <transactionManager type="JDBC" />
           <!-- 數據庫連接池 -->
           <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>
       </environment>
    </environments>

    <!-- 加載映射文件 -->
    <mappers>
       <mapper resource="sqlmap/User.xml" />
       <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>
  • MyBatis 將按照下面的順序來加載屬性:
    1)、在 properties 元素體內定義的屬性首先被讀取。
    2)、然后會讀取properties 元素中resource或 url 加載的屬性,它會覆蓋已讀取的同名屬性。
  • 在mapper.xml配置文件中,就可以使用設置的別名了。(別名大小寫不敏感)

sql映射文件,需要 在SqlMapConfig中加載

<?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:
        1.命名空間,用于隔離sql,
        2.使用動態代理開發DAO,namespace必須和Mapper接口類路徑一致。 -->
<mapper namespace="test">
    <!-- id:statement的id 或者叫做sql的id-->
    <!-- parameterType:聲明輸入參數的類型 -->
    <!-- resultType:聲明輸出結果的類型,應該填寫pojo的全路徑 -->
    <!-- #{}:輸入參數的占位符,相當于jdbc的? -->
    <select id="queryUserById" parameterType="int" resultType="cn.itcast.mybatis.pojo.User">
       SELECT * FROM `user` WHERE id  = #{id}
    </select>

    <!-- 如果返回多個結果,mybatis會自動把返回的結果放在list容器中 -->
    <!-- resultType的配置和返回一個結果的配置一樣 -->
    <select id="queryUserByUsername1" parameterType="string" resultType="cn.itcast.mybatis.pojo.User">
       SELECT * FROM `user` WHERE username LIKE #{username}
    </select>

    <!-- 如果傳入的參數是簡單數據類型,${}里面必須寫value -->
    <select id="queryUserByUsername2" parameterType="string" resultType="cn.itcast.mybatis.pojo.User">
       SELECT * FROM `user` WHERE username LIKE '%${value}%'
    </select>

    <!-- 保存用戶 -->
    <insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">
       INSERT INTO `user` (username,birthday,sex,address) VALUES
       (#{username},#{birthday},#{sex},#{address})
    </insert>

    <!-- 保存用戶 自增主鍵返回-->
    <insert id="saveUser1" parameterType="cn.itcast.mybatis.pojo.User">
       <!-- selectKey 標簽實現主鍵返回 -->
       <!-- keyColumn:主鍵對應的表中的哪一列 -->
       <!-- keyProperty:主鍵對應的pojo中的哪一個屬性 -->
       <!-- order:設置在執行insert語句前執行查詢id的sql,孩紙在執行insert語句之后執行查詢id的sql -->
       <!-- resultType:設置返回的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>

    <!-- 保存用戶 使用 uuid實現主鍵-->
    <insert id="saveUser2" parameterType="cn.itcast.mybatis.pojo.User">
        <!-- selectKey 標簽實現主鍵返回 -->
        <!-- keyColumn:主鍵對應的表中的哪一列 -->
        <!-- keyProperty:主鍵對應的pojo中的哪一個屬性 -->
        <!-- order:設置在執行insert語句前執行查詢id的sql,孩紙在執行insert語句之后執行查詢id的sql -->
        <!-- resultType:設置返回的id的類型 -->
        <selectKey keyColumn="id" keyProperty="id" order="BEFORE" resultType="string">
           SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO `user` (username,birthday,sex,address) VALUES
        (#{username},#{birthday},#{sex},#{address})
    </insert>

    <!-- 更新用戶 -->
    <update id="updateUserById" parameterType="cn.itcast.mybatis.pojo.User">
        UPDATE `user` SET username = #{username} WHERE id = #{id}
    </update>

    <!-- 刪除用戶 -->
    <delete id="deleteUserById" parameterType="int">
       delete from user where id=#{id}
    </delete>
</mapper>

四、需要注意的點

  1. #{} 與 ${}

  2. parameterType 與 resultType
    parameterType:指定輸入參數類型,mybatis通過ognl從輸入對象中獲取參數值拼接在sql中。
    resultType:指定輸出結果類型,mybatis將sql查詢結果的一行記錄數據映射為resultType指定類型的對象。如果有多條數據,則分別進行映射,并把對象放到容器List中

  3. selectOne 和 selectList
    selectOne:查詢一條記錄,如果使用selectOne查詢多條記錄則拋出異常:
    org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
    selectList:可以查詢一條或多條記錄。

  4. 加載sql映射文件的方式

  • <mapper resource=" " />
    使用相對于類路徑的資源(常用方式)
    如:<mapper resource="sqlmap/User.xml" />

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

  • <package name=""/>
    注冊指定包下的所有mapper接口
    如:<package name="cn.itcast.mybatis.mapper"/>
    注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。

五、Mybatis解決jdbc編程的問題

  1. 數據庫連接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用數據庫連接池可解決此問題。
    解決:在SqlMapConfig.xml中配置數據連接池,使用連接池管理數據庫鏈接。
  2. Sql語句寫在代碼中造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
    解決:將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。
  3. 向sql語句傳參數麻煩,因為sql語句的where條件不一定,可能多也可能少,占位符需要和參數一一對應。
    解決:Mybatis自動將java對象映射至sql語句,通過statement中的parameterType定義輸入參數的類型。
  4. 對結果集解析麻煩,sql變化導致解析代碼變化,且解析前需要遍歷,如果能將數據庫記錄封裝成pojo對象解析比較方便。
    解決:Mybatis自動將sql執行結果映射至java對象,通過statement中的resultType定義輸出結果的類型。

六、mybatis與hibernate不同

  1. Mybatis和hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程序員自己編寫Sql語句。mybatis可以通過XML或注解方式靈活配置要運行的sql語句,并將java對象和sql語句映射生成最終執行的sql,最后將sql執行的結果再映射生成java對象。

  2. Mybatis學習門檻低,簡單易學,程序員直接編寫原生態sql,可嚴格控制sql執行性能,靈活度高,非常適合對關系數據模型要求不高的軟件開發,例如互聯網軟件、企業運營類軟件等,因為這類軟件需求變化頻繁,一但需求變化要求成果輸出迅速。但是靈活的前提是mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件則需要自定義多套sql映射文件,工作量大。
    Hibernate對象/關系映射能力強,數據庫無關性好,對于關系模型要求高的軟件(例如需求固定的定制化軟件)如果用hibernate開發可以節省很多代碼,提高效率。但是Hibernate的學習門檻高,要精通門檻更高,而且怎么設計O/R映射,在性能和對象模型之間如何權衡,以及怎樣用好Hibernate需要具有很強的經驗和能力才行。

  3. 總之,按照用戶的需求在有限的資源環境下只要能做出維護性、擴展性良好的軟件架構都是好架構,所以框架只有適合才是最好。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。