java框架之mybatis

1、簡介

一、基本概念

  • mybatis 是一個半自動輕量級的一個 orm 框架
  • 將 java 與 sql 分離,解決了 jdbc 的硬編碼問題;
  • sql 由開發人員控制,更加方便 sql 的修改調優;

二、入門程序

2.1 原始方法:經過 sqlsession 來操做數據庫

  • 建一個全局配置文件(mybatis-config.xml),配置數據源等運行環境信息;
  • 創建一個 sql 的映射文件(mapper.xml),並將這個文件註冊到全局配置中;
  • 加載全局配置文件得到一個 sqlsessionfactory;
  • 經過 factory 得到 sqlsession(非線程安全),一個sqlsession 維護一次會話;
  • sqlsession經過 sql 的惟一標識符(id)調用方法執行 sql;
  • 注意數據庫的字段名和bean的字段名要相同才能順利映射;
  • 具體的配置文件能夠參考後文;
public class MybatisTest {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("MyMapper.selectUser",1);
        System.out.println(user);
    }
}

2.2 接口式編程(動態代理方式):經過 sqlsession 得到 mapper 接口的代理對象,經過代理對象來操做數據庫

  • 定義一個接口,接口中定義操做數據庫的方法;
  • 將接口和sql的映射文件綁定:xml的namespace是接口全限定名,sql的id是接口中的方法名;
  • 爲避免出錯,最好mapper xml 文件和 mapper 接口放在同一個包下面,而且取一樣的名字;
  • idea 開發環境下,mapper xml 不能自動輸出到 target 的目錄下須要在 pom 文件中配置:
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

 

2.3 問題說明

2.3.1 沒法完成數據庫字段到 pojo 屬性的映射的解決方法:

  • sql 查詢的時候給數據庫字段起別名(別名就是pojo的屬性名);
  • 能夠給 pojo 中的屬性從新以數據庫的字段命名;
  • 當數據庫的字段相似(user_name),pojo 屬性名稱是(userName)時,開啓駝峯語法自動轉換便可;
<!--配置全局屬性-->
<settings>
    <!-- 開啓駝峯命名轉換 :{name_id}  -> {nameId}    -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

2.3.2 當使用 sqlsession 對數據庫進行增刪改的操做的時候,須要調用 commit 方法;

2.3.3 mybatis 的全局配置文件中各配置項的順序是有約束的;

 

三、運行原理簡介

(1)sqlsessionfactory 初始化

  • 把配置文件全部的信息解析並保存在 configuration 中,並返回一個包含 configuration 的 defaultSqlSessionFactory 對象;
  • 一個 mapperedStatement 表明一個增刪改查的信息;

(2)sqlsession 對象獲取

  • 返回一個 defaultSqlSession 對象,裏面包含了 configuration 和 executor;
  • executor的兩次包裝:二級緩存開啓的話會有一次包裝;攔截器鏈會有一次包裝;

(3)獲取接口代理對象

  • 調用 getMapper 方法其實是從一個 mapperproxyfactory 的工廠類中得到 mapperproxy 對象,裏面包含了defaultsqlsession;

(4)查詢實現:

  • 在四大對象的建立過程當中都會有插件的介入;html

 

2、配置文件

一、全局配置文件

(1)properties標籤:url引入網絡或者磁盤的資源;resource引入 classpath 下的資源;

(2)settings標籤:設置一些mybatis運行時的重要參數

(3)typeAliases標籤:就是給 java 類起別名用的

  • 別名不分大小寫;
  • 能夠單獨起也能夠批量起;
  • 默認別名就是類名;
  • 也可使用註解來起別名(@alias);
  • java 中的基本數據類型和一些經常使用的類已經取好了別名(基本類型就是前面加 _ ;引用類型就是原類名)

(4)typeHandlers標籤:

  • 就是處理 java 類型和數據庫類型的轉換的;
  • 經常使用的類型 mybatis 都給默認處理了,不用咱們本身去作;
  • 固然是支持自定義的了;

(5)plugins標籤:做用就是攔截四大對象(StatementHandler、ParameterHandler、ResultSetHandler、executor),改變他們的默認行爲

(6)environments標籤:

  • 用來配置環境的,能夠配置多個環境,在default屬性裏能夠指定默認的環境;
  • 每一個環境都應包含:transactionManager 和 dataSource

(7)databaseIdProvider標籤:

  • 此標籤就是用來標記 sql 語句做用的數據庫的;
  • 標籤內經過property標籤指定數據庫的別名;
  • 在相應的 sql 語句的標籤上用databaseId引入數據庫的別名;
  • 注意:主配置文件裏的標籤的書寫的順序是有要求的,databaseIdProvider應該寫在envir 和 mapper 之間;

(8)mappers標籤:註冊 sql 映射,裏面具體的 mapper 的屬性有三種寫法:

  • resource:classpath 路徑下;
  • url:網絡資源或者磁盤資源;
  • class:指定對應的映射的接口(條件是必須同包同名);
  • 批量的註冊直接用package 標籤(條件是必須同包同名);
<?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>
    <properties resource="conf/dbconfig.properties"></properties>
    
    <!-- <settings>
        <setting name="" value="" />
    </settings>

    <typeAliases>
        單獨起別名
        <typeAlias type="" alias="" />
        批量起別名
        <package name="" />
    </typeAliases>

    <typeHandlers>
        <typeHandler handler="" />
    </typeHandlers>

    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>     -->
    
    <environments default="development">
        <!-- <environment id="test">
            <transactionManager type=""></transactionManager>
            <dataSource type=""></dataSource>
        </environment> -->
        <environment id="development">
            <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>
    
    <databaseIdProvider type="DB_VENDOR">
        <property name="Oracle" value="oracle" />
        <property name="MySQL" value="mysql" />
    </databaseIdProvider>
    
    <mappers>
        <mapper resource="conf/TuserMapper.xml" />
    </mappers>
</configuration>

二、mapper文件概述

(1)CRUD測試(select、delete、update、insert標籤)

(2)數據插入得到自增主鍵

  一、mysql:支持自增主鍵

  (1)useGeneratedKeys 和 keyProperty 屬性配合使用便可獲取到自增主鍵的值;java

  (2)使用 selectkey 標籤也能夠實現,不過 order 的屬性值要設置爲 after 才能獲取獲得;mysql

<selectKey order="AFTER"keyProperty="id" resultType="String">select last_insert_id()</selectKey>

  二、oracle:不支持自增主鍵,靠序列來完成

  (1)在要執行的sql前用 selectKey 標籤來實現;git

  (2)相關的三個屬性分別是 keyProperty、resultType 和 order;github

  (3)有兩種寫法分別是 order 的屬性值爲 before 和 after;Oracle 用 before;web

<selectKey keyColumn="id" keyProperty="id" order="BEFORE" resultType="int">
    select max(id)+1 as myNo from emp
</selectKey>

<!--序列實現自增主鍵-->
SQL >create sequence autoID increment by 1 start with 1 maxvalue 999999 cycle;
SQL >insert into tablename values(autoID.nextval,...);

 (3)常見問題說明

  一、int、long 和 boolean 類型的返回值咱們能夠直接用;spring

  二、增刪改後必定要 commit,不然對於數據庫的修改不會生效;sql

 

三、mapper文件參數處理

(1)參數處理

  • 單個參數:單個參數能夠在 #{}中隨意寫;
  • 多個參數(默認封裝在 map 中):能夠寫 #{param1}和 #{param2};也能夠寫#{0}和#{1};還能夠用註解 @Param("id")Integer id 指定名稱;
  • 多參數能夠本身封裝一個 map,在 mapper 文件中用 #{鍵值}取值;
  • 業務相關的數據直接封裝在 pojo 中;
  • list 和 array 的處理:https://blog.csdn.net/zyxwvuuvwxyz/article/details/80238995

(2)關於 #{} 和 ${}兩種取值方法:

  • #{}是以預編譯的形式來取參數的值的,能夠防注入;而${}是直接拼接的;
  • 只有在原生的 jdbc 不支持佔位符的地方可使用 ${},好比order by ${age};
  • 當插入一個null值的時候,#{email} 在 mysql 中插入成功,在 oracle 中會插入失敗,插入 null 值時會轉化成 jdbcType 的 other 類型,oracle不支持此類型;解決:#{email,jdbcType=NULL}

 

四、mapper文件結果集映射

(1)resultType

  • resultType的做用就是指定結果集的封裝的規則;
  • 結果集封裝爲 list 類型直接寫 list 裏數據類型便可;
  • 封裝爲 map 單條記錄直接寫 map 的別名(map);多條記錄 mapper xml文件寫 pojo 的類型(pojo是map的值),對應接口方法上用註解@mapKey("pojo屬性")指定map的鍵;

(2)resultMap

  • resultMap也是用來定義結果集的封裝規則的(表的列名和pojo 的屬性名的對應關係);
  • resultMap標籤兩個屬性 type是pojo類型,id是惟一標識;表中的主鍵列用id 封裝,普通列用 result 封裝;
<resultMap id="myuser" type="com.stan.hello.User">
    <!--<id column="id" property="id"></id>-->
    <result column="user_name" property="userName"></result>
</resultMap>

<select id="queryUserById" resultMap="myuser" parameterType="java.lang.String">
  select * from tb_user where id = #{id}
</select>

 

3、動態sql

一、if 判斷

(1)OGNL(對象圖導航語言):數據庫

  • 有點相似於 jsp 中的 el 表達式,能夠用來取參數的值並做一些基本的運算;
  • OGNL使用:動態SQL表達式中(test等);${param}參數中;

(2)if 判斷編程

<select id="queryUserList" resultType="com.stan.pojo.User">
    select * from tb_user WHERE 1=1
    <if test="name!=null and name.trim()!=''">
      and name like '%${name}%'
    </if>
</select>

<!--
if 判斷拼接 where 條件存在多餘 and 的解決方法:加一個 1=1;使用where 或者 trim標籤;-->

二、choose 分支選擇

(1)語法:choose----when----when----otherwise-----choose;

(2)理解:相似於 java 中帶break 的 switch語句,只匹配一個條件;

<select id="queryUserByNameOrAge" resultType="com.stan.pojo.User">
    select * from tb_user WHERE 1=1
    <choose>
        <when test="name!=null and name.trim()!=''">
            and name like '%${name}%'
        </when>
        <when test="age!=null">
            and age = #{age}
        </when>
        <otherwise>
            and name='allen'
        </otherwise>
    </choose>
</select>

三、foreach 遍歷

(1)遍歷集合拼接 where 條件(如:in 集合):幾個經常使用的屬性 collection、item、separator、open、close、index(map的key)

List<User> queryUserByIds(@Param("ids") String[] ids);

<select id="queryUserByIds" resultType="com.stan.pojo.User">
    select * from tb_user where id in
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>

(2)數據批量插入

  • mysql 第一種:在 values 後面用 foreach 遍歷拼接 sql;
  • mysql 第二種:直接整個插入語句來循環,須要在數據庫的 url 後面傳參開啓多條語句執行的支持;
  • oracle 第一種:用 plsql ,在begin 和 end 中來遍歷;
  • oracle 第二種:循環用 select 語句將集合中的值從僞表中查出,再用查出的結果進行插入;

四、其餘標籤

(1)where標籤:自動去除 where 條件中多餘的 and

<select id="queryUserByNameAndAge" resultType="com.stan.pojo.User">
    select * from tb_user
    <where>
        <if test="name!=null and name.trim()!=''">
            and name like '%${name}%'
        </if>
        <if test="age!=null">
            and age = #{age}
        </if>
    </where>
</select>

(2)set標籤:在 update 語句中自動加上 set 和逗號分隔符

<update id="updateUser">
    update tb_user
    <set>
        <if test="userName != null and userName.trim() != ''">
            user_name=#{userName}
        </if>
        <if test="password != null and password.trim() != ''">
            password=#{password}
        </if>
    </set>
    where id = #{id}
</update>

(3)trim標籤:自動加 set,自動去掉多餘的逗號(僅針對示例)

update tb_user
<trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">
    <if test="userName != null and userName.length()>0"> user_name=#{userName} , </if>
    <if test="password != null and password.length()>0"> password=#{password} ,  </if>
</trim>

(4)bind標籤:做用就是將傳入的參數賦值給一個變量方便之後的調用,連個屬性name 和 value;

<select id="getEmpsTestInnerParameter" resultType="com.hand.mybatis.bean.Employee">
  <bind name="bindeName" value="'%'+userName+'%'"/> 
  SELECT * FROM tb_user 
  <if test="userName!=null">
    where user_name like #{bindeName}
  </if>
</select>

五、補充說明

(1)內置參數:

  • 除了咱們傳入的參數,還有內置的參數,這裏的內置參數能夠取到也能夠判斷
  • _parameter:封裝全部的參數,傳入的參數一個就是這個參數自己,傳入多個參數就是一個 map;
  • databaseId:全局配置文件裏面配置了 databaseproviderid這裏能夠取到;
  • _parameter 和 if結合能夠用來進行 where條件的拼接;
  • databaseId 和 if 結合使用能夠用來在一個 select 元素裏寫兩個數據庫的 sql;

(2)sql 片斷:

  • 用 sql 標籤抽取能夠重用的sql 片斷方便之後在其餘的sql 裏用include標籤用id引用,如查詢字段的重用;
<sql id="commonSql">id,user_name,password</sql>
<select id="queryUserById" resultMap="userResultMap">
    select <include refid="commonSql"></include> from tb_user where id = #{id}
</select>

 

 

 

4、緩存

一、介紹

(1)做用:就是提高查詢的效率;

(2)分類:

  • 一緩(本地緩存):session 級別的緩存,默認開啓,session關閉則失效;
  • 二緩(全局緩存):namespace級別的緩存,要手動開啓;

二、一級緩存

(1)一級緩存失效的四種狀況

  • sqlsession 不一樣,不一樣的session 之間不能共享數據;
  • 參數不一樣的狀況;
  • 兩次相同的查詢之間有 增刪改 的操做;
  • 第二次查詢以前手動清空緩存;

三、二級緩存

(1)做用:能夠實現不一樣的 session 之間的數據共享,一個 namespace(一個接口)對應一個map(二級緩存);

(2)原理:

  • 一次會話中查詢的數據會默認保存在一級緩存中;
  • 當對應的會話關閉的時候,若是開啓了二級緩存,在session關閉清空緩存數據以前會將數據存到二級緩存中;
  • 須要注意的是若是 session 沒有關閉,一級緩存的數據是不會自動存到二級緩存中的;

(3)使用:

  • 總開關(主配文件裏的 setting)和分開關(sql映射文件裏用 cache標籤來開啓)都要打開;
  • select 標籤裏的 useCache 屬性能夠單獨控制一個 select 是否使用二級緩存;

四、緩存原理和設置

(1)緩存相關設置

  • 全局的 cacheEnabled 和 select 標籤的 useCache 都是和二級緩存相關的設置,不會影響一級緩存;
  • flashCache屬性在增刪改的標籤裏是默認開啓,在 select 標籤裏是默認關閉的,這個屬性爲 true的話會清空一二級的緩存;
  • session 的 clearcache 方法只會清除當前 session 的一級緩存;
  • 全局 LocalcacheScope 能夠用來關閉一級緩存(通常不用);

(2)原理圖示

五、第三方緩存整合(ehcache)

(1)導入緩存的包和整合包;測試的時候採用的maven

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.5</version>
</dependency>

(2)classpath 下編寫 ehcache-xml 配置文件;

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
        <diskStore path="D:\ehcache" />
        <defaultCache
                maxElementsInMemory="1000"
                maxElementsOnDisk="10000000"
                eternal="false"
                overflowToDisk="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
        </defaultCache>
</ehcache>

(3)打開二級緩存的總開關,直接在mapper xml 中用 cache 標籤引用;

<cache type="org.mybatis.caches.ehcache.EhcacheCache" >
    <property name="timeToIdleSeconds" value="3600"/>
    <property name="timeToLiveSeconds" value="3600"/>
    <property name="maxEntriesLocalHeap" value="1000"/>
    <property name="maxEntriesLocalDisk" value="10000000"/>
    <property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>

(4)另外一個 mapper 能夠用 cache-ref 標籤來引用其餘 mapper 的緩存策略;

<cache-ref namespace="com.stan.crud.UserDaoMapper"/>

(5)相關配置參數的說明

diskStore標籤:指定數據在磁盤中的存儲位置。
defaultCache標籤:當藉助CacheManager.add("demoCache")建立Cache時,EhCache便會採用<defalutCache/>指定的的管理策略
如下屬性是必須的:
maxElementsInMemory - 在內存中緩存的element的最大數目 
maxElementsOnDisk - 在磁盤上緩存的element的最大數目,如果0表示無窮大
eternal - 設定緩存的elements是否永遠不過時。若是爲true,則緩存的數據始終有效,若是爲false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷
overflowToDisk - 設定當內存緩存溢出的時候是否將過時的element緩存到磁盤上
如下屬性是可選的:
timeToIdleSeconds - 當緩存在EhCache中的數據先後兩次訪問的時間超過timeToIdleSeconds的屬性取值時,這些數據便會刪除,默認值是0,也就是可閒置時間無窮大
timeToLiveSeconds - 緩存element的有效生命期,默認是0.,也就是element存活時間無窮大
diskSpoolBufferSizeMB 這個參數設置DiskStore(磁盤緩存)的緩存區大小.默認是30MB.每一個Cache都應該有本身的一個緩衝區.
diskPersistent - 在VM重啓的時候是否啓用磁盤保存EhCache中的數據,默認是false。
diskExpiryThreadIntervalSeconds - 磁盤緩存的清理線程運行間隔,默認是120秒。每一個120s,相應的線程會進行一次EhCache中數據的清理工做
memoryStoreEvictionPolicy - 當內存緩存達到最大,有新的element加入的時候, 移除緩存中element的策略。默認是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出)

 六、mybatis 二級緩存的侷限性

  • mybatis 的二級緩存是基於 namespace 的,若是同一個 namaspace 下增刪改的操做頻繁的話,二級緩存會經常被清空,基本也就是沒啥用了;

 

5、高級查詢

一、高級查詢 (示例:訂單-用戶-訂單詳情-商品)

(1)示例解讀

  • 一個訂單隻能屬於一我的(一對一);
  • 一個訂單能夠包含多個訂單詳情(一對多);
  • 一個訂單詳情包含一個商品信息;
  • 訂單和商品(多對多);

(2)總結說明

  • 一對一和一對可能是單向關係,多對可能是雙向關係;
  • 進行數據庫設計的時候,雙向的一對一關係的話一般主鍵相同便可(這種狀況直接包含在一個表裏會更簡單);
  • 一對多的關係是多方外鍵指向一方的主鍵;多對多的話須要創建中間表,將多對多轉化爲多對一,而後根據前面的原則進行建表;

(3)示例實現(resultMap)

一、具體的代碼和數據庫腳本參考:https://blog.csdn.net/hellozpc/article/details/80878563#12_1741

二、association 標籤用法:能夠用來實現一對一關聯查詢(Order 包含一個 User 屬性);

<resultMap id="orderUserRM" type="com.stan.pojo.OrderUser" autoMapping="true">
    <association property="user" javaType="com.stan.pojo.User" autoMapping="true">
        <id property="id" column="user_id"></id>
    </association>
</resultMap>
<select id="queryOrderUserByOrderNumber" resultMap="orderUserRM" parameterType="String">
  select * from tb_order o
  left join tb_user u on o.user_id = u.id
  where o.order_number = #{number}
</select>

三、collection 標籤用法:用 collection 標籤實現集合類型屬性的封裝(一對多),屬性 property爲相應的集合屬性名,而ofType 是集合元素類型;

<resultMap id="orderUserRM" type="com.stan.pojo.OrderUser" autoMapping="true">
    <association property="user" javaType="com.stan.pojo.User" autoMapping="true">
        <id property="id" column="user_id"></id>
    </association>
    <collection property="details" autoMapping="true" javaType="list" ofType="com.stan.pojo.OrderDetail" >
     <!--這裏的 OrderDetail 是訂單和商品關係的媒介--> <id property="id" column="detial_id"></id> <association property="item" javaType="com.stan.pojo.Item" autoMapping="true"> <id property="id" column="item_id"></id> </association> </collection> </resultMap> <select id="queryOrderItemByOrderNumber" resultMap="orderUserRM2" parameterType="String"> select *,d.id as detial_id from tb_order o left join tb_user u on o.user_id = u.id left join tb_orderdetail d on o.id = d.order_id left join tb_item i on i.id = d.item_id where o.order_number = #{number} </select>

四、 其餘說明:

  • pojo包裝pojo的狀況,用resultMap實現關聯查詢,還能夠設置 property 屬性的時候,採起級聯屬性的寫法(屬性點屬性);
  • resultmap 標籤有一個繼承的屬性(extends),能夠用來繼承另外一個resultMap,達到複用的目的;
  • sql 語句中若是出現 <  > 等符號可使用字符實體替代(&lt;&gt;)

 

二、延遲加載(分步查詢)

(1)主配置文件設置

<!--延遲加載關聯查詢總開關,association和collection標籤裏的fetchType也是設置加載的模式的-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--設置爲true的話,只要get一個關聯的屬性,全部的關聯查詢都會發出-->
<setting name="aggressiveLazyLoading" value="false"/>

(2)mapper xml 文件

<resultMap id="rm2" type="com.stan.pojo.OrderUser" autoMapping="true">
        <association property="user" javaType="com.stan.pojo.User" autoMapping="true" select="queryUserById" column="user_id">
     <!--這裏的select指定關聯的查詢的 id,這裏的column是傳過去的參數-->
     <!--多個參數的傳遞能夠用map,就是這樣的形式:{key1=col1,key2=col2}-->
     </
association> </resultMap> <select id="queryOrderByNumber" resultMap="rm2"> select * from tb_order where order_number = #{number} </select>

<!--這個方法不單獨使用的話能夠不在 mapper 接口中書寫方法定義--> <select id="queryUserById" resultType="com.stan.pojo.User"> select * from tb_user where id = #{number} </select>

(3)mapper 接口

OrderUser queryOrderByNumber(@Param("number") String number);

(4)測試用例

@Test
public void queryOrderByNumber() throws IOException {
    OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
    OrderUser orderUser = mapper.queryOrderByNumber("201807010001");
    System.out.println("----------------------");
    System.out.println(orderUser.getOrderNumber());
    System.out.println("--------------------");
    System.out.println(orderUser.getUser());
}

(5)注意:延遲加載依賴於 cglib ,須要在 pom 文件中加入依賴

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

 

 

6、整合spring(SSM整合)

一、導包

(1)核心包:spring包、springmvc包、mybatis包、spring-mybatis整合包

(2)其餘包:日誌包、鏈接池包

二、mybatis 配置

(1)主要就是配全局 settings 等;

三、springMVC 配置

(1)controller 掃描;

(2)視圖解析器;

(3)註解驅動;

(4)默認servlet 處理;

四、spring 配置

(1)包掃描;

(2)數據源的配置;

(3)事務管理(基於註解的事務);

(4)sqlSessionFactory配置;

(5)配置能夠批量執行的 sqlSession;

(6)配置Mapper 接口的掃描(不可少);

五、web.xml配置

(1)全局參數配置 spring配置文件的路徑;

(2)配置spring 監聽;

(3)配置 springmvc 的DispatcherServlet;

(4)spring框架會自動加載 web-inf 下的配置文件,classpath 路徑下的配置文件須要配置路徑;

(5)SSM框架整合示例

 

 

7、逆向工程

一、概念

  • 就是根據數據庫表自動生成 pojo、mapper 接口和 mapper xml 文件;
  • 逆向工程經常使用的方法有:配置插件生成;使用 java 工程來生成;

二、使用(java工程)

(1)導包

(2)配置文件(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>
    <!-- targetRuntime="MyBatis3Simple":生成簡單版的CRUD
        MyBatis3:豪華版    -->
  <context id="mysqlTables" targetRuntime="MyBatis3">
      <!-- jdbcConnection:指定如何鏈接到目標數據庫 -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true"
        userId="root"
        password="123456">
    </jdbcConnection>

<javaTypeResolver > <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- javaModelGenerator:指定javaBean的生成策略 targetPackage="test.model":目標包名 targetProject="\MBGTestProject\src":目標工程 --> <javaModelGenerator targetPackage="com.atguigu.mybatis.bean" targetProject=".\src"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- 指定 mapper xml 文件生成策略: --> <sqlMapGenerator targetPackage="com.atguigu.mybatis.dao" targetProject=".\conf"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <!-- 指定 mapper 接口的生成策略 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.dao" targetProject=".\src"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- 指定要逆向分析哪些表:根據表要建立javaBean --> <table tableName="tbl_dept" domainObjectName="Department"></table> <table tableName="tbl_employee" domainObjectName="Employee"></table> </context> </generatorConfiguration>

(3)運行生成器 java 代碼

public void testMbg() throws Exception {
    List<String> warnings = new ArrayList<String>();
    boolean overwrite = true;
    File configFile = new File("mbg.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);
}

三、注意事項

  • mybatis 的SSM集成和逆向工程的具體使用方法和示例代碼 github 上官方目錄都有;
  • 運行生成代碼的時候極可能會找不到配置文件(mbg xml),直接右鍵 copy path 便可;
  • 能夠生成簡單查詢和複雜查詢的代碼,複雜查詢的方式相似於 hibernate 的 QBC;
  • or 條件的拼接代碼示例
EmployeeExample example = new EmployeeExample();
Criteria criteria = example.createCriteria();
criteria.andLastNameLike("%e%");
criteria.andGenderEqualTo("1");

Criteria criteria2 = example.createCriteria();
criteria2.andEmailLike("%e%");
example.or(criteria2);

 

 

 8、插件擴展

一、插件開發

(1)原理

  • 四大對象的建立不是直接返回的,而是 intercepter 調用了 plugin 方法包裝生成代理對象;
  • 咱們可使用插件對四大對象進行包裝生成代理對象,這樣就能夠攔截到四大對象的每個執行;

(2)簡單實現:單個插件

  • 寫一個類繼承intercepter ,重寫 intercept(攔截)、plugin(包裝)、和 setProperties(屬性設置)方法;
  • 用 @intercepts 註解給插件簽名;
  • 在全局配置文件中引用插件;

(3)多個插件:攔截同一個對象同一個方法

  • 包裝:順序包裝;
  • 執行:逆序執行;
  • 特色:多個插件是對目標對象進行層層包裝的;

(4)開發示例:

@Intercepts({@Signature(type= ParameterHandler.class,method = "setParameters",args = {PreparedStatement.class})})
public class MyIntercepter implements Interceptor {
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("要攔截的方法:"+invocation.getMethod());
        MetaObject metaObject = SystemMetaObject.forObject(invocation.getTarget());
        Object para = metaObject.getValue("parameterObject");
        System.out.println("原來的參數是:"+para);
        metaObject.setValue("parameterObject","5");
        return invocation.proceed();
    }

    public Object plugin(Object o) {
        System.out.println("包裝的對象:"+o.toString());
        return Plugin.wrap(o,this);
    }

    public void setProperties(Properties properties) {
        System.out.println(properties.getProperty("test"));
    }
}

(5)PageHelper 插件使用:實現查詢數據的分頁功能

  • 使用:導包 > 主配置文件註冊插件 > mapper查詢前設置參數 > 返回一個 page 對象其中包含不少分頁信息(也能夠用pageinfo包裝查詢結果); 
<dependency>
 <groupId>com.github.pagehelper</groupId>
 <artifactId>pagehelper</artifactId>
 <version>4.1.4</version>
</dependency>
<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
        <property name="offsetAsPageNum" value="false"/>
        <property name="rowBoundsWithCount" value="false"/>
        <property name="pageSizeZero" value="true"/>
        <property name="reasonable" value="false"/>
        <property name="supportMethodsArguments" value="false"/>
        <property name="returnPageInfo" value="none"/>
    </plugin>
</plugins>
<select id="queryUserPage" resultType="com.stan.pojo.User">
  select * from tb_user
</select>

public List<User> queryUserPage();
@Test
public void queryPage() throws IOException {
    Page<User> page = PageHelper.startPage(2,4);
    List<User> users = mapper.queryUserPage();
    for (User user :
            users) {
        System.out.println(user);
    }
    System.out.println(page);
}

 

二、擴展學習

(1)批量執行sql語句

  • 正常使用就是在 openSession 傳入 executorType;與 Spring 集成則是以下圖所示配置;
  • mybatis 主配置文件的 setting 裏有一個批量執行的開關,可是不建議開啓,由於會影響全部的sql執行; 
<!--配置一個能夠進行批量執行的sqlSession  -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
    <constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>

(2)mybatis 調用存儲過程:示例參考

(3)自定義類型處理器處理枚舉類型

一、枚舉類型

  • 枚舉的本質就是一個類;枚舉的值本質就是枚舉對象;枚舉固然也能夠有構造方法;
  • 枚舉類型包含 name(名)和 ordinal(suoyin),使用 mybatis 自帶的類型處理器只能保存名字或者索引中的一個;

二、自定義類型處理器:參考示例

  • 步驟:類繼承typehandler > 重寫相應方法(實質就是調用ps和rs 的 get 和 set 方法)> 主配置文件中註冊;
  • 注意:能夠針對具體的字段設置 typehandler,可是必須保證保存和查詢的 typeHandler 是一致的;

三、關於 mybatis 的註解開發

  • 我的感受註解開發仍是將 sql 和 java 雜揉在一塊兒了,不符合框架將 sql 和 java 分離的初衷,通常不建議使用,瞭解便可,小項目有快速開發的需求勉強能夠考慮(我的感受,不用參考);
相關文章
相關標籤/搜索