Spring整合Mybatis

一 、 Mybatis概覽

說到mybatis就不得不說ORM框架,即對象-關(guān)系映射(Object-Relational Mapping),面向?qū)ο蟮拈_發(fā)方法是當今企業(yè)級應用開發(fā)環(huán)境中的主流開發(fā)方法,關(guān)系數(shù)據(jù)庫是企業(yè)級應用環(huán)境中永久存放數(shù)據(jù)的主流數(shù)據(jù)存儲系統(tǒng)。對象和關(guān)系數(shù)據(jù)是業(yè)務實體的兩種表現(xiàn)形式,業(yè)務實體在內(nèi)存中表現(xiàn)為對象,在數(shù)據(jù)庫中表現(xiàn)為關(guān)系數(shù)據(jù)。內(nèi)存中的對象之間存在關(guān)聯(lián)和繼承關(guān)系,而在數(shù)據(jù)庫中,關(guān)系數(shù)據(jù)無法直接表達多對多關(guān)聯(lián)和繼承關(guān)系。因此,對象-關(guān)系映射(ORM)系統(tǒng)一般以中間件的形式存在,主要實現(xiàn)程序?qū)ο蟮疥P(guān)系數(shù)據(jù)庫數(shù)據(jù)的映射。

1. Mybatis的框架設計

MyBatis本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation遷移到了google code,并且改名為MyBatis 。iBATIS一詞來源于“internet”和“abatis”的組合,是一個基于Java的持久層框架


mybatis框架圖
接口層

Mybatis和數(shù)據(jù)庫交互的兩種方式:

  • 使用傳統(tǒng)的Mybatis提供的api
    傳遞查詢語句和查詢參數(shù)給SqlSession對象,使用SqlSession對象完成和數(shù)據(jù)庫的交互;Mybatis提供了非常方便的api,供用戶實現(xiàn)對數(shù)據(jù)庫的增刪改查。


    mybatis api

    具體的SqlSession提供的接口如下:

    public interface SqlSession extends Closeable {
    <T> T selectOne(String var1);
    
    <T> T selectOne(String var1, Object var2);
    
    <E> List<E> selectList(String var1);
    
    <E> List<E> selectList(String var1, Object var2);
    
    <E> List<E> selectList(String var1, Object var2, RowBounds var3);
    
    <K, V> Map<K, V> selectMap(String var1, String var2);
    
    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3);
    
    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);
    
    <T> Cursor<T> selectCursor(String var1);
    
    <T> Cursor<T> selectCursor(String var1, Object var2);
    
    <T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);
    
    void select(String var1, Object var2, ResultHandler var3);
    
    void select(String var1, ResultHandler var2);
    
    void select(String var1, Object var2, RowBounds var3, ResultHandler var4);
    
    int insert(String var1);
    
    int insert(String var1, Object var2);
    
    int update(String var1);
    
    int update(String var1, Object var2);
    
    int delete(String var1);
    
    int delete(String var1, Object var2);
    //事務提交
    void commit();
    
    void commit(boolean var1);
    //事務回滾
    void rollback();
    
    void rollback(boolean var1);
    
    List<BatchResult> flushStatements();
     //關(guān)閉連接
    void close();
    
    void clearCache();
    
    Configuration getConfiguration();
    
    <T> T getMapper(Class<T> var1);
    //連接數(shù)據(jù)庫
    Connection getConnection();
    }    
    

上述使用MyBatis 的方法,是創(chuàng)建一個和數(shù)據(jù)庫打交道的SqlSession對象,然后根據(jù)Statement Id 和參數(shù)來操作數(shù)據(jù)庫,這種方式固然很簡單和實用,但是它不符合面向?qū)ο笳Z言的概念和面向接口編程的編程習慣。由于面向接口的編程是面向?qū)ο蟮拇筅厔?,MyBatis 為了適應這一趨勢,增加了第二種使用MyBatis 支持接口(Interface)調(diào)用方式。

  • 使用Mapper接口
    MyBatis 將配置文件中的每一個<mapper> 節(jié)點抽象為一個 Mapper 接口,而這個接口中聲明的方法和跟<mapper> 節(jié)點中的<select|update|delete|insert> 節(jié)點項對應,即<select|update|delete|insert> 節(jié)點的id值為Mapper 接口中的方法名稱,parameterType 值表示Mapper 對應方法的入?yún)㈩愋?,而resultMap 值則對應了Mapper 接口表示的返回值類型或者返回結(jié)果集的元素類型。

    <?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">
    <mapper namespace="org.cloud.dao.RandomGener_Dao">
    
    <resultMap type="org.cloud.model.RandomId" id="getId_List">
      <id column="id" property="id"/>
    </resultMap>
    <select id="selectUnusingId" resultType="Integer">
      SELECT id FROM randomID where tag = 0 limit 1 for UPDATE
     </select>
      <update id="updateId" parameterType="Integer">
      UPDATE  randomID  set tag=1 where id = #{id}
    </update>
    </mapper>
    

然后編寫對應接口文件

  public interface RandomGener_Dao {
//搜索tag不為1的id
 Integer selectUnusingId();
//更新tag 為1
void updateId(@Param("id") Integer id);
}

根據(jù)MyBatis 的配置規(guī)范配置好后,通過SqlSession.getMapper(XXXMapper.class) 方法,MyBatis 會根據(jù)相應的接口聲明的方法信息,通過動態(tài)代理機制生成一個Mapper 實例,我們使用Mapper 接口的某一個方法時,MyBatis 會根據(jù)這個方法的方法名和參數(shù)類型,確定Statement Id,底層還是通過SqlSession.select("statementId",parameterObject);或者SqlSession.update("statementId",parameterObject); 等等來實現(xiàn)對數(shù)據(jù)庫的操作。

數(shù)據(jù)處理層

數(shù)據(jù)處理層是Mybatis 的核心,它主要完成三個功能:

  • 通過傳入?yún)?shù)構(gòu)建動態(tài)sql語句
    動態(tài)語句生成可以說是MyBatis框架非常優(yōu)雅的一個設計,MyBatis 通過傳入的參數(shù)值,使用 Ognl 來動態(tài)地構(gòu)造SQL語句,使得MyBatis 有很強的靈活性和擴展性。參數(shù)映射指的是對于java 數(shù)據(jù)類型和jdbc數(shù)據(jù)類型之間的轉(zhuǎn)換:這里有包括兩個過程:查詢階段,我們要將java類型的數(shù)據(jù),轉(zhuǎn)換成jdbc類型的數(shù)據(jù),通過 preparedStatement.setXXX() 來設值;另一個就是對resultset查詢結(jié)果集的jdbcType 數(shù)據(jù)轉(zhuǎn)換成java 數(shù)據(jù)類型。
  • sql語句的執(zhí)行
  • 封裝查詢結(jié)果轉(zhuǎn)化為List<E>
框架支撐層
  • 事務管理機制
  • 連接池管理機制
  • 緩存機制
    為了提高數(shù)據(jù)利用率和減小服務器和數(shù)據(jù)庫的壓力,MyBatis 會對于一些查詢提供會話級別的數(shù)據(jù)緩存,會將對某一次查詢,放置到SqlSession 中,在允許的時間間隔內(nèi),對于完全相同的查詢,MyBatis 會直接將緩存結(jié)果返回給用戶,而不用再到數(shù)據(jù)庫中查找。
引導層

引導層是配置和啟動MyBatis 配置信息的方式。MyBatis 提供兩種方式來引導MyBatis :基于XML配置文件的方式和基于Java API 的方式,我們將在下面介紹spring整合mybatis的配置信息。

2. Mybatis的主要構(gòu)件及相互關(guān)系

mybatis的主要核心部件:

組件 說明
SqlSession 每一個SqlSession實例代表應用程序和數(shù)據(jù)庫的一次連接 ,每次打開一個SqlSession都會綁定一個事務管理實例,執(zhí)行器實例。
Executor MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負責SQL語句的生成和查詢緩存的維護
MappedStatement MappedStatement維護了一條<select update delete insert>節(jié)點的封裝
Configuration MyBatis所有的配置信息都維持在Configuration對象之中。

二、Spring整合mybatis

1. 配置Mybatis

  • 在pom中添加mybatis包

    <!--mybatis 包-->
      <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.4.2</version>
      </dependency>
    <!--mybatis 整合spring的適配包-->
      <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>1.3.1</version>
      </dependency>
     <!--數(shù)據(jù)庫連接池-->
      <dependency>
          <groupId>c3p0</groupId>
          <artifactId>c3p0</artifactId>
          <version>0.9.1.2</version>
      </dependency>
      <!--java mysql驅(qū)動版本要與mysql數(shù)據(jù)庫的版本對應-->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.11</version>
      </dependency>
    
  • 在spring容器的配置文件中整合mybatis

      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
      <property name="jdbcUrl" value="${db.url}"></property>
      <property name="driverClass" value="${db.driver}"></property>
      <property name="user" value="${db.user}"></property>
      <property name="password" value="${db.pw}"></property>
    </bean>
    <!--整合 mybatis-->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <!--指定mybatis的全局配置文件-->
      <property name="configLocation" value="classpath:mybatis-config.xml"></property>
      <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
      <property name="dataSource" ref="dataSource"></property>
      <!--<property name="mapperLocations" value="classpath*:mapper/*.xml"></property>-->
      </bean>
    <!--配置掃描器 將mybatis接口實現(xiàn)加入到ioc容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="basePackage" value="com.hust.dao" ></property>
    </bean>
    <!--事務控制 為了便于管理數(shù)據(jù)庫-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <!--配置數(shù)據(jù)源-->
      <property name="dataSource" ref="dataSource"/>
      <!--開啟基于注解的事務 或者是xml配置事務-->
    </bean>
    <!--配置一個可以批量執(zhí)行的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
      <constructor-arg name="sqlSessionFactory" ref="sessionFactory"/>
      <constructor-arg name="executorType" value="BATCH"/>
    </bean>
    

    Spring 中的 DataSourceTransactionManager用來管理事務,它允許mybatis參與到Spring的事務管理中,而不是為mybatis創(chuàng)建一個新的特定的事務管理器。要注意, 為事務管理器指定的 DataSource 必須和用來創(chuàng)建 SqlSessionFactoryBean 的 是同一個數(shù)據(jù)源,否則事務管理器就無法工作了。

  • mybatis逆向生成工具
    mybatsi逆向生成工具可以根據(jù)數(shù)據(jù)庫中的的字段,創(chuàng)建相應的pojo類以及其對應的mapper.xml文件,mapper中包含一些常用的CRUD方法。
    首先在pom中加入依賴

     <!--mybatis 逆向生成工具-->
      <dependency>
          <groupId>org.mybatis.generator</groupId>
          <artifactId>mybatis-generator-core</artifactId>
          <version>1.3.5</version>
      </dependency>
    

    然后添加mbg.xml配置文件

    <?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>
    <properties resource="db.properties"></properties>
    <!--配置數(shù)據(jù)庫連接信息-->
    <context id="DB2Tables" targetRuntime="MyBatis3">
      <!--不讓生成注釋-->
      <commentGenerator>
          <property name="suppressAllComments" value="true"></property>
      </commentGenerator>
      <!--idea 將&當成了特殊符號 這里需要轉(zhuǎn)義 & = &amp;-->
      <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                      connectionURL="jdbc:mysql://localhost:3306/gm?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT&amp;useSSL=false"
                      userId="root"
                      password="root">
      </jdbcConnection>
    
      <javaTypeResolver >
          <property name="forceBigDecimals" value="false" />
      </javaTypeResolver>
      <!--指定javabean的生成位置-->
      <javaModelGenerator targetPackage="com.hust.bean" targetProject=".\src\main\java">
          <property name="enableSubPackages" value="true" />
          <property name="trimStrings" value="true" />
      </javaModelGenerator>
      <!--指定sql映射文件的位置-->
      <sqlMapGenerator targetPackage="mapper"  targetProject=".\src\main\resources">
          <property name="enableSubPackages" value="true" />
      </sqlMapGenerator>
      <!--指定dao接口的生成位置-->
      <javaClientGenerator type="XMLMAPPER" targetPackage="com.hust.dao"  targetProject=".\src\main\java">
          <property name="enableSubPackages" value="true" />
      </javaClientGenerator>
      <!--指定每個表的生成策略-->
      <table tableName="mg_employee" domainObjectName="Employee" ></table>
      <table tableName="mg_inventory" domainObjectName="Inventory"></table>
      <table tableName="mg_sale" domainObjectName="Sale"></table>
      <table tableName="mg_user" domainObjectName="User"></table>
    </context>
    </generatorConfiguration>
    

配置文件中的注釋對各個節(jié)點的作用進行了詳細的說明
編寫一個測試類生成對應的pojo類和mapper文件

public class Mbg {
public static void main(String[] args) throws Exception {
    List<String> warnings = new ArrayList<>();
    boolean overwrite = true;
    File configFile = new File("E:mbg.xml");
    ConfigurationParser cp = new ConfigurationParser(warnings);
    Configuration configuration = cp.parseConfiguration(configFile);
    //是否覆蓋已有的文件
    DefaultShellCallback callback = new DefaultShellCallback(overwrite);
    MyBatisGenerator generator = new MyBatisGenerator(configuration,callback,warnings);
    generator.generate(null);
    Logger logger = LogManager.getLogger("info");
    logger.info("mybatis逆向生成工具完成任務");
    }
}

三、pageHelper分頁工具

pagehelper是一款由國人編寫的開源免費的mybatis第三方物理插件,我們可以在github上看到它的源碼,下面我們介紹一下mybatis的基本使用以及它的實現(xiàn)原理。

1. PageHelper的配置

引入maven依賴

 <!--分頁工具-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.0.0</version>
    </dependency>

將pagehelper整合到mybatis中

 <!--配置分頁插件-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
    <!--指定數(shù)據(jù)庫-->
        <property name="dialect" value="mysql" />
<!-- 該參數(shù)默認為false -->
<!-- 設置為true時,會將RowBounds第一個參數(shù)offset當成pageNum頁碼使用 -->
<!-- 和startPage中的pageNum效果一樣 -->
<property name="offsetAsPageNum" value="false" />
<!-- 該參數(shù)默認為false -->
<!-- 設置為true時,使用RowBounds分頁會進行count查詢 -->
<property name="rowBoundsWithCount" value="true" />

<!-- 設置為true時,如果pageSize=0或者RowBounds.limit = 0就會查詢出全部的結(jié)果 -->
<!-- (相當于沒有執(zhí)行分頁查詢,但是返回結(jié)果仍然是Page類型) <property name="pageSizeZero" value="true"/> -->

<!-- 3.3.0版本可用 - 分頁參數(shù)合理化,默認false禁用 -->
<!-- 啟用合理化時,如果pageNum<1會查詢第一頁,如果pageNum>pages會查詢最后一頁 -->
<!-- 禁用合理化時,如果pageNum<1或pageNum>pages會返回空數(shù)據(jù) -->
<property name="reasonable" value="true" />
<!-- 3.5.0版本可用 - 為了支持startPage(Object params)方法 -->
<!-- 增加了一個`params`參數(shù)來配置參數(shù)映射,用于從Map或ServletRequest中取值 -->
<!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默認值 -->
<!-- 不理解該含義的前提下,不要隨便復制該配置 <property name="params" value="pageNum=start;pageSize=limit;"/> -->
    </plugin>
</plugins>

2. PageHelper原理分析

pagehelper通過mybatis的plugin實現(xiàn)了interceptor接口

分頁實現(xiàn)過程

實現(xiàn)原理:
pageHelper會使用ThreadLocal獲取到同一線程中的變量信息,各個線程之間的Threadlocal不會相互干擾,也就是Thread1中的ThreadLocal1之后獲取到Tread1中的變量的信息,不會獲取到Thread2中的信息
所以在多線程環(huán)境下,各個Threadlocal之間相互隔離,可以實現(xiàn),不同thread使用不同的數(shù)據(jù)源或不同的Thread中執(zhí)行不同的SQL語句
所以,PageHelper利用這一點通過攔截器獲取到同一線程中的預編譯好的SQL語句之后將SQL語句包裝成具有分頁功能的SQL語句,并將其再次賦值給下一步操作,所以實際執(zhí)行的SQL語句就是有了分頁功能的SQL語句
關(guān)于pagehelper的更詳細的實現(xiàn),可以查看這篇博文

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容