MyBatis?
archetypeCatalog = internal
本文檔單獨出現的_parameter都標識為變量名
一.三個基本要素:
核心接口和類
MyBatis 核心配置文件
SQL映射文件
二.核心接口和類:
結構圖:
(1)每個MyBatis的喲ing有都以一個SqlSessionFactory對象的實例為核心
(2)首先獲取SqlSessionFactoryBuilder對象,可以根據XML配置文件或Configuration類的實例構建該對象
(3)然后獲取SqlSessionFactory對象,該對象實例可以通過SqlSessionFactoryBuilder對象來獲得
(4)有了SqlSessionFactory對象之后,就可以進而獲取SqlSession實例,SqlSession對象中完全包含以數據庫為背景的所有執行SQL操作方法。可以用該實例來直接執行已映射的SQL語句
1. SqlSessionFactoryBuilder
作用:
構建SqlSessionFactory
Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sfb = new SqlSessionFactoryBuilder().build(reader);
特點:
用過即丟
聲明周期和作用域:
SqlSessionFactoryBuilder是利用XML或者Java編碼獲得資源來購進啊SqlSessionFactory的,通過它可以構建多個SessionFactory。它的作用就是一個構建器,一旦我們構建了SqlSessionFactory,它的作用就已經完結,失去了存在的意義,這時我們就應該毫不猶豫的廢棄它,將它回收。所有它的生命周期只存在與方法的局部,它的作用就是生成SqlSessionFactory對象
2. SqlSessionFactory
作用:
創建SqlSession實例
SqlSession sqlSession = sfb.openSession();
生命周期和作用域
SqlSessionFactory對象一旦創建,就會在整個應用過程中始終存在。沒有理由去銷毀或再創建它,並且應用運行中也不建議多次創建實例,因此,最佳作用域是Application
創建SqlSessionFactory實例時一般放在靜態代碼塊中
而,最佳方案是使用依賴注入---Spring框架來管理SqlSessionFactory的單例生命周期
3. SqlSession
作用:
是用於執行持久化操作的對象,類似於JDBC終端Connection。提供了面向數據庫執行SQL命令所需的所有方法,可以通過SqlSession實例直接運行已映射的SQL語句
生命周期和作用域:
一個SqlSession對象對應着一次會話。非永久,用完即關,及時釋放資源(close()方法)
線程方面:
每個線程都有自己的SqlSession實例,SqlSession實例不能被共享,也不是線程安全的。因此最佳的作用域范圍是request作用域或者方法體作用域
兩種使用方式:
1. 通過SqlSession實例來直接執行已映射的SQL語句
(1) 添加xx.xml 文件 SQL映射節點
(2) 根據需求利用SqlSession實例直接點出需要使用的方法,(方法傳參SQL映射節點的id值)
2. 基於mapper接口方式操作數據
(1) 添加xx.xml 文件 操作SQL節點
(2) 創建綁定映射語句的接口xxx.java 並提供---> 和SQL映射節點id值相同的接口方法
(3) 利用SqlSession實例調用getMapper()方法 ,傳參為接口名.class 然后點出相應接口方法
注意:SqlSession實例用完即關
三.核心配置文件:
Mybatis-config.xml
文檔結構:
注意先后順序!!!
Configuration 配置 (根節點)
Properties 可以配置在Java屬性配置文件中
Settings 修改MyBatis在運行時的行為方式
TypeAliases 為Java類型命名一個別名
TypeHandlers 類型處理器
ObjectFactory 對象工廠
Plugins 插件
Environments 環境
Environment 環境變量
transactionManager 事物處理器
dataSource 數據源
Mappers 映射器
1. configuration元素:
整個xml文件的根節點,相當於MyBatis的總管,所有配置信息都會存放在它里面,MyBatis還提供了設置這些配置信息的方法:
(1) 可以從配置文件李獲取屬性值
(2) 通過程序直接設置
2. Properties元素:
其描述都是外部化,可替代的屬性
屬性獲取方式:
1. 引入外部properties文件
<properties resource=”數據源路徑” />
引入外部文件后,在本xml文件中可直接使用${key值}來獲取對應的value值
2. 內部配置 --->元素中直接配置property屬性
<properties> <property name=”key值” value=”value值” /> ...... </properties>
配置節點完成后,在本xml文件中可直接使用${key值}來獲取對應的value值
3. 內外部結合使用
<properties resource=”外部數據源路徑”> <property name=”key值” value=”value值” /> ...... </properties>
配置節點完成后,在本xml文件中可直接使用${key值}來獲取對應的value值
如果外部配置和內部配置 配置了相同的key 那么:resource屬性值的優先級高於property子節點配置的值
3. Settings元素
作用是設置一些非常重要的設置選項,用於設置和改變MyBatis運行中的行為
設置項 |
描述 |
允許值 |
默認值 |
cacheEnabled |
對在此配置文件下的所有cache進行全局性開/關設置 |
True|false |
True |
lazyLoadingEnabled |
全局性設置懶加載。如果設為false,則所有相關聯的都會被初始化加載 |
True|false |
True |
autoMappingBehavior |
MyBatis對於resultMap自動映射的匹配級別 |
NONE|PARTIAL|FULL |
PARTIAL |
Settings標簽配置的是配置MyBatis框架運行時的一些行為,例如:
(1)緩存
(2)延遲加載
(3)結果集控制
(4)執行器
(5)分頁設置
(6)命名規則
等一系列控制性參數,其所有的settings配置都放在父標簽settings標簽中
4. typeAliases元素
作用是配置類型別名,通過與MyBatis的SQL映射文件相關聯,減少輸入多余的完整類名
兩種方式:
1. 直接指定到類
<typeAliases> <typeAlias alias=”別名” type=”類的全路徑” /> ...... </typeAliases>
弊端:
如果一個項目有多個POJO的時候需要一一配置
2. 指定包,檢索類
解決上面直接指定到類的弊端
通過package的name屬性直接指定包名,MyBatis會自動掃描指定包下的JavaBean,並默認設置一個別民,默認名稱為JavaBean的非限定類名
<typeAliases> <typeAlias alias=”別名” type=”某包的全路徑” /> </typeAliases>
Xx.xml配置:
直接寫包下的某個類名即可
另外:
對於基礎數據類型等,Mybatis已經為許多常見的Java類型內建了相應的類型別名,一般都是與其映射類型一致,並且它們都是大小寫不敏感的
5. Environments元素:
MyBatis可以配置多套運行環境,如:
(1)開發環境
(2)測試環境
(3)生成環境
等,我們可以靈活選擇不同的配置,從而將SQL映射應用到不同的數據庫環境上。
這些不同的運行環境,就可以聽過environments元素來配置,但是不管增加幾套運行環境,都必須要明確選擇出當前的唯一一個運行環境。
Because:
每個數據庫都是對應一個SqlSessonFactory實例,需要指明哪個運行環境將被創建,並把運行環境中設置的參數傳遞給SqlSessionFactoryBuilder。
<environments default="development"> <!-- 開發環境 --> <environment id="development"> <transactionManager type="JDBC"> <property name="" value=""/> </transactionManager> <dataSource type="UNPOOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${pwd}"/> </dataSource> </environment> <!-- 測試環境 --> <environment id="test"> <transactionManager type=""></transactionManager> <dataSource type=""></dataSource> </environment> </environments>
幾個關鍵點:
(1)默認的運行ID
通過environments節點的default屬性來指定當前的運行環境ID,對於子節點environment環境ID的命名要確保唯一性
(2)TransactionManager事物管理器
設置其類型為JDBC(MyBatis有兩種事物管理類型,即JDBC,MANAGED),直接使用JDBC的提交和回滾功能,依賴於數據源獲得連接來管理事物的聲明周期
(3)配置數據庫連接對象
DataSource元素使用標准的JDBC數據源接口來配置JDBC連接對象的資源。
MyBatis提供了三種數據源類型
(1)UNPOOLED
(2)POOLED
(3)JNDI
一般使用POOLED數據源類型。該類型的實現利用“池”的概念將JDBC連接對象組織起來,避免了創建新的連接實例時所必需的初始化和認證時間,是MyBatis實現的簡單的數據庫連接池類型,它使數據庫的連接可被復用,不必在每次請求時都去創建一個物理連接
對於高並發的Web應用是一種流行的處理方式,有利於快速響應請求
6. Mappers元素:
映射器,用來定義SQL的映射語句,我們只需要告訴MyBatis去哪里找到這些SQL語句即可,即去哪里找相應的SQL映射文件(可以使用類資源或者URL資源等方式)
僅只是找到指定映射文件,其更詳細的信息配置在每個SQL映射文件中
常用的兩種映射方式:
1. 使用類資源路徑獲取路徑
<mappers> <mapper resource="本項目中SQL映射文件 資源路徑"/> </mappers>
2. 使用URL獲取資源
<mappers> <mapper url="本地或者網絡上的SQL映射文件 資源路徑"/> </mappers>
四.DTD文件的引入:
用IDEA 不需要手動引入DTD文件 略略略!!!
五.SQL映射文件:
MyBatis強大之處:SQL映射語句 魅力所在 非常簡單
使用SQL映射文件配置可減少50%以上的代碼量
MyBatis專注於SQL 極大限度地進行SQL調優,保證性能
幾個頂級元素配置:
1. mapper:
映射文件的根元素節點,只有一個屬性namespace(命名空間)
namespace作用:
1. 區分不同的mapper,全局唯一
2. 保定DAO接口,即面向接口。
① 當namespace綁定一個接口后,可以不用寫該接口的實現類,MyBatis會通過接口的完整限定名查找到對應的mapper配置來執行SQL語句。因此namespace的命名必須要跟接口同名(包結構+接口名)
namespace命名要求:
1. 命名必須跟某個DAO接口命名,同屬於DAO層,故代碼結構上,映射文件與該DAO接口放置在同一package下,如果不放在同一package下,需指定其對應接口的資源路徑,,接口命名,配置文件命名習慣上以Mapper結尾
2. 在不同的mapper文件中,子元素的id可以相同,MyBatis通過namespace和子元素的id進行聯合區分。接口中的方法與映射文件中SQL語句id應一一對應
2. chche:
配置給定命名空間的緩存
3. cache-ref:
從其他命名空間引用的緩存配置
4. resultMap:
用來描述數據庫結果集和對象的對應關系
5. Sql:
通過sql片段達到代碼重復利用
可以重用的SQL塊,也可以被其他語句使用
我一般用來封裝常用的表字段
如:
<sql id="co"> `SUBWAYNAME`,`STARTSTATION`,`ENDSTATION`,`STATIONNUM`,`STARTIME`,`PRICE` </sql>
使用時:
在標簽內插入語句
<include refid="co"/>
6. Insert
映射插入語句
<insert id="add" parameterType="com.metro.entity.SubwayInfo"> INSERT INTO `subwayInfo`( <include refid="co"/> ) VALUES (#{subwayName},#{startStation},#{endStation},#{stationNum},#{starTime},#{price}) </insert>
7. Update:
映射更新語句
<update id="up" parameterType="com.metro.entity.SubwayInfo"> UPDATE `subwayInfo` <set> <if test="subwayName!=null and "".equals(subwayName.trim())">SUBWAYNAME=#{subwayName},</if> <if test="startStation!=null and "".equals(startStation.trim())">STARTSTATION=#{startStation},</if> </set> WHERE id=#{id} </update>
8. Delete:
映射刪除語句
<delete id="del" parameterType="INTEGER"> DELETE from subwayInfo where id=#{id} </delete>
9. Select:
映射查詢語句
<select id="listAll" resultMap="subwayInfo"> SELECT id,<include refid="co"/> FROM subwayInfo </select>
別名與Java類型映射:
別名 |
映射類型 |
string |
String |
byte |
Byte |
long |
Long |
short |
Short |
int |
Integer |
integer |
Integer |
arraylist |
Arraylist |
double |
Double |
float |
Float |
boolean |
Boolean |
date |
Date |
map |
Map |
hashmap |
HashMap |
list |
List |
SQL映射標簽詳解:
1. resultMap
映射查詢結果,兩種方案:
1)使用resultType做自動映射
<!--resultType 大小寫不敏感:不需resultMap標簽映射,,依靠實體類字段名與數據庫相應字段名相同進行映射
嚴重受限
-->
字段名與POJO的屬性名必須一致。若不一致,則需要給字段起別名,保證別名與屬性名一致
不推薦使用
2)通過resultMap來映射自定義結果
使用resultMap標簽做自定義結果映射,字段名可以不一致,並且還可以指定要顯示的列,比較靈活,應用廣泛
<resultMap type="com.metro.entity.SubwayInfo" id="subwayInfo"> <!--typeHandler="" 可做數據類型轉換--> <!--column="" 數據庫字段property=”” 實體類字段--> <id column="ID" jdbcType="INTEGER" property="id" /> <result column="SUBWAYNAME" jdbcType="VARCHAR" property="subwayName"/> <result column="STARTSTATION" jdbcType="VARCHAR" property="startStation"/> <result column="ENDSTATION" jdbcType="VARCHAR" property="endStation"/> <result column="STATIONNUM" jdbcType="INTEGER" property="stationNum"/> <result column="STARTIME" jdbcType="VARCHAR" property="starTime"/> <result column="PRICE" jdbcType="INTEGER" property="price"/> </resultMap>
jdbcType:插入空值時,需要指定jdbcType
Mybatis insert空值報空值異常,但是pl/sql不會提示錯誤,主要原因是mybayis無法進行轉換
各屬性標簽:
Id:唯一標識,此id用於sql映射元素 resultMap屬性的引用
Type:表示該resultMap的映射結果類型
result子節點:用於標識一些簡單屬性
返回類型resultType 與 resultMap?
這里的resultType 與 resultMap跟上面所說的那個不一樣,,這里探討的是Sql映射標簽 返回類型
1. resultType:
直接表示返回類型,包括基礎數據類型和復雜數據類型
2. resultMap:
對外部resultMap定義的引用,對應外部resultMap的id,表示返回的結果集映射到哪一個resultMap上。應用場景:數據庫字段信息與對象屬性不一致或者需要做復雜的聯合查詢以便自由控制映射結果
3. resultType和resultMap的關聯
無論是resultType還是resultMap,其實查詢出來的每個字段值都放在一個對應的Map里面,其鍵是字段名,值是其對應的值
然后呢,當select元素提供的返回值類型是resultType的時候,MuBatis會將Map里面的鍵值對取出賦給resultType所指定的對象對應的屬性(即調用對應的對象里的屬性的setter方法進行填充)
··所以呢,其實每個查詢的返回類型都是resultMap
1)只是當我們提供的返回值類型屬性是resultType 的時候,會自動把對應的賦值resultType 所指定對象的屬性
2)我們提供的返回類型是resultMap的時候,因為Map不能很好的表示領域模型,我們就需要通過進一步的定義把它轉化為對應的實體對象
開發經驗:
當返回類型是resultMap時,主要用在進行復雜聯合查詢上
進行簡單查詢時,使用resultType 足以
注意:在select元素中,resultType 和resultMap本質上是一樣的,都是Map數據結構
明確一點。。。兩種絕對不能同時存在,只能二者選其一使用
4. resultMap的自動映射級別
resultMap自動映射級別默認的是PARTIAL,為自動裝配,他會自動裝配所有查詢出來並且實體類里擁有的字段
如果想要選擇部分字段進行映射,希望沒有映射的字段是不能在后台查詢並輸出的,則需要在核心配置文件settings中設置resultMap的自動映射級別(autoMappingBehavior)為NONE,即禁止自動匹配
<settings> <setting name="autoMappingBehavior" value="NONE"/> </settings>
注意點:
在MyBatis中,使用resultMap能夠進行自動映射匹配的前提是字段名和屬性名需要一致,在默認映射級別(PARTIAL)情況下:
1)若一致,即使沒有做屬性名和字段名的匹配映射,也可以在后台獲取到未匹配過的屬性值
2)若不一致,且在resultMap里沒有做映射,那么就無法在后台獲取並輸出
2. Select
屬性:
1)Id
命名空間中唯一的標識符,可以被用來引用這條語句
由於我們常用的映射方法是基於Mapper接口,所有id值需跟對應的接口方法名一致
2)ParameterType:
標識查詢語句傳入參數的類型的完全限定名或別名。
支持繼承數據類型和復雜數據類型
MyBatis有內建的類型別名,,上面有!!!
除了內建的類型別名外,還可以為自定義的類設置別名。。。在核心配置文件的typeAliases元素中設置
在映射文件中可直接使用別名,以減少配置文件的代碼量
注:
1. 基本情況下只允許傳入一個參數,如果要傳入多個參數,,就要對多個參數進行封裝后續還可以使用@Param注解實現多參數入參
2. Mybatis傳入參數類型可以是Java繼承數據類型,但是只適用於一個參數的情況,通過#{參數名}即可獲取傳入的值。若是多參數入參,需要復雜數據類型來支持,包括Java實體類,map,通過#{屬性名}或者#{Map的key}來獲取傳入的參數值
#{值}:寫法是OGNL表達式
如果是實體類對象和基本數據類型多參數傳參
取值時:實體類對象.屬性名
3)ResultType:
查詢語句返回結果類型的完全限定名或別名,命名與ParameterType大體一致
3. Insert,Update,Delete
屬性:
1)id:
與select元素的id一樣,是命名空間中唯一的標識符,可以唄用來引用這條語句
2)ParameterType:
與select元素其屬性一樣,是傳入參數的類型的完全限定名或別名
注意點:
對於增刪改(insert,update,delete)這類數據庫更新操作
1)該類型的操作本身默認返回執行SQL影響的行數,所以DAO層的接口方法的返回值一般設置為int類型。最好不要返回boolean類型
2)Insert,update,delete元素中均沒有resultType屬性,只有查詢操作需要對返回結果類型(resultType/resultMap)進行相應的指定
3)在測試類中,當sqlSession執行完sql操作之后,需要進行commit,完成數據的插入操作。若在執行的過程中拋了異常,那么就必須在catch中進行回滾,以此來保證數據的一致性,同時設置count為0
六.使用@param注解實現多參數入參
int del(@Param("uid")Integer id,@Param("xxx")String x);
解析:
使用注解@Param來傳入多個參數,並且注解內可以將變量名重命名為其他名字,,只會影響SQL映射文件的使用,不會影響java類中的真實使用。。在映射文件中使用#{注解內的值}來獲取其傳入的值,,,
還有啊,使用注解,裝配的入參,參數類型必須是引用類型(復雜類型或String,或者基本數據類型的包裝類,對象等)使用int等值值類型,會報錯
如果是實體類對象和基本數據類型多參數傳參
取值時:
實體類:#{注解內的值.屬性名}
基本:#{名}
擴展:
使用多參數入參,必須使用@Param注解方式,若不使用,則會報錯,報錯信息類似於Pararmeter‘參數名’ not found。
Reason:
深入MyBatis源碼,我們可以發現,MyBatis的參數類型為Map
1)若使用@Param注解參數,那么就會記錄指定的參數名為key
2)若參數前沒有加@Param,那么就會使用“param”+它的序號座位Map的key。
故而進行多參數入參時,若沒有使用@Param指定參數key,那么在映射的SQL語句中獲取不到#{參數名},從而報錯
經驗:
相信大家學了@Param注解入參就會有疑惑,既然有高級貨,為什么不早拿出來???
其實呢,普通入參方式也不是一無是處,下面我們來談論一下《在MyBatis中參數入參,何時需要封裝成對象入參,何時又需要使用多參數入參???》
來,let’s go :
1)一般情況下呢,超過4個參數最好封裝成對象入參(特別是在常規的增加和修改操作時,字段較多,封裝成對象比較方便,也省的一個個記參數名了,也不是)
2)對於參數固定的業務方法呢,最好使用多參數入參,原因是這種方法比較靈活,代碼的可讀性高,可以清晰地看出接口方法中所需的參數是什么。並且對於固定的接口方法,參數一般是固定的,所以直接多參數入參即可,無需封裝對象(比如修改個人密碼,根據用戶id刪除用戶,根據用戶id查看用戶明細,都可以采用這種方式)
需要注意的是:
當參數為基礎數據類型時,不管是多參數入參還是單獨一個參數,都需要使用@Param注解來進行參數的傳遞
還有一定記住一點。。使用多參數入參時。在SQL映射文件中使用OGNL表達式獲取傳開的值的時候 其key值是@Param()括號中的值
七.ResultMap實現高級結果映射
我們先回顧一下前面提到的resultMap的基本配置
基本配置:
1> 屬性
1)id:resultMap的唯一標識
2)Type:標識該resultMap的映射結果類型(通常時Java實體類)
2> 字節點
1)id:一般對應數據庫中該行的主鍵id,設置此項可以提升MyBatis性能
2)Result:映射到JavaBean的某個“簡單類型”屬性,如:基礎數據類型,包裝類等
子節點id和result均可實現最基本的結果集映射,將列映射到簡單數據類型的屬性。
這兩者唯一不同的是:在比較對象實例時id將作為結果集的標識屬性。有助於提高整體性能,特別是應用緩存和潛逃結果映射的時候
高級結果映射,兩個配置項:
1> Association
映射到JavaBean的某個“復雜類型”屬性,比如JavaBean類,即JavaBean內部嵌套一個復雜數據類型(JavaBean)屬性,這種情況就屬於復雜類型的關聯。
需要注意的是:
Association僅處理一對一的關聯關系(比如,每個用戶都會有一個角色,兩表關系一對一)
<resultMap type="com.metro.entity.User" id="user"> <id column="ID" jdbcType="INTEGER" property="id" /> <result column="NAME" jdbcType="VARCHAR" property="name"/> <result column="PWD" jdbcType="VARCHAR" property="pwd"/> <association property="role" javaType="Roel"> <id property="rid" column="RID"/> <result property="rname" column="RNAME"/> </association> </resultMap> <select id="listAll" resultMap="user" parameterType="Integer"> SELECT u.* r.id AS RID,r.name AS RNAME FROM user u,role r WHERE u.rid=r.id AND u.id=#{uid} </select>
分析其屬性:
Association元素:
1> javaType:
完整Java類名或者別名。
1)若映射到一個JavaBean,則MyBatis通常會自行檢測到其類型;
2)若映射到一個HashMap,則應該明確指定JavaType,來確保所需行為。此處為Role
2> Property:
映射數據庫列的實體對象的屬性。此處為在User里定義的屬性role
其子節點元素:
1> Id:
不多說,看下面
2> Result:
不多說,看下面
上兩者共同屬性:
1)property:
映射數據庫列的實體對象的屬性,上面的例子為Role的屬性
2)colunm:
數據庫對象的列名或別名
注意:
Id子元素在嵌套結果映射中扮演了一個非常重要的角色,應該指定一個或者多個屬性來唯一標識這個結果集。實際上,即使沒有值得id,MyBatis也會工作,但是會導致嚴重的性能開銷,所以最好選擇盡量少的屬性來唯一標識結果,主鍵或者聯合主鍵均可
最后:
了解了關於association的基本用法以及使用場景,一些復用大佬,就會想到這個東東可不可以復用呢,,
答案是肯定的,association提供了另一個屬性:resultMap。(相信說到這里大家已經指定怎么做了,每次就是跟select等映射標簽一樣,,搞唄,resultMap=“某resultMap元素id值”)通過這個屬性可以擴展一個resultMap來進行聯合映射,這樣就可以使其結果映射重復使用。當然,若不需要復用,也可安裝之前的寫法,直接嵌套這個聯合結果映射,根據具體業務而定
<resultMap type="com.metro.entity.User" id="user"> <id column="ID" jdbcType="INTEGER" property="id" /> <result column="NAME" jdbcType="VARCHAR" property="name"/> <result column="PWD" jdbcType="VARCHAR" property="pwd"/> <association property="role" javaType="Roel" resultMap="role"/> </resultMap> <resultMap id="role" type="Role"> <id property="rid" column="RID"/> <result property="rname" column="RNAME"/> </resultMap>
引用外部resultMap的好處:可以達到復用的效果,並且整體的結構較為清晰,特別適合association的結果映射比較多的情況
返回對象
Association可以處理一對一的關聯關系,那么對於一對多的關聯關系的處理,它就心有余而力不足了,,,,,怎么辦呢?嗯,用collection元素
2> Collection
作用和association元素的作用差不多一樣,事實上,它們非常類似,也是映射到JavaBean的某個“復雜類型”屬性,只不過這個屬性是一個集合列表,即JavaBean內部嵌套一個復雜數據類型(集合)屬性。和使用association元素一樣,我們使用嵌套查詢,或者從連接中嵌套結果集
<resultMap id="role" type="Role"> <id property="rid" column="RID"/> <result property="rname" column="RNAME"/> <collection property="juris" ofType="Juris" resultMap="ju"/> </resultMap> <resultMap id="ju" type="Juris"> <id property="jid" column="JID"/> <result property="jname" column="JNAME"/> </resultMap>
與association用法基本一致,就是多出來了一個ofType屬性
ofType屬性呢:代表的是Java類名或者別名,即集合所包含的類型
返回集合
八.ResultMap自動映射級別和MyBatis緩存
(一)resultMap自動映射級別
resultMap自動映射的三個匹配級別:
1> NONE:
禁止自動匹配
2> PARTIAL:
默認自動映射級別,自動匹配所以屬性,有內部嵌套的(association、collection)的除外
3> FULL:
自動匹配所有,所有所有!!!
詳解:
用法:
在核心配置中配置:
<settings> <setting name="autoMappingBehavior" value="匹配級別"/> </settings>
NONE禁止自動匹配不用多說,就是不自動匹配嘛,手動映射啥就匹配啥並且賦值,否則沒值,並且上面也舉過例子,所有不再做過多闡述
主要講解PARTIAL與FULL的區別
可以私下里做個例子,發現沒有設置autoMappingBehavior的時候,也就是默認情況下(PARTIAL),若是普通數據類型的屬性,會自動匹配所有,,,,,但是呢若是內部嵌套(association或collection)(集合或實體對象),那么輸出結果就是null。也就是說它不會自動匹配,除非手工設置autoMappingBehavior的value為FULL(自動匹配所有)
修改后你會發現autoMappingBehavior的value為FULL(自動匹配所有)之后,未作映射的字段(任何類型)均有值輸出
(二)MyBatis緩存
如大多數持久化框架一樣,MyBatis提供了一級緩存和二級緩存的支持
一級緩存緩存的是SQL語句,二級緩存緩存的是結果對象
1. 一級緩存
一級緩存是基於PerpetualCache(MyBatis自帶)的HashMap本地緩存,作用范圍為session域內,當session flush或者close之后,該session中所有的cache就會清空
配置:
因為MyBatis默認就是一級緩存,所以刪除所有關於緩存的配置,就是一級緩存
2. 二級緩存
二級緩存就是global caching,它超出session范圍之外,可以被所有的SqlSession共享,開啟它只需要在MyBatis的核心文件settings中設置即可
三種配置方法:
為cache賦值:true為二級緩存。false為一級緩存
1)MyBatis的全局cache配置
在核心文件配置:
<settings> <setting name="cacheEnabled" value="false"/> </settings>
2)針對mapper的namespace
1)在mapper文件(也就是SQL映射文件)中設置緩存,默認情況下是沒有開啟緩存
2)需要注意的是,global caching發作用域是針對mapper的namespace而言的,即只有namespace內的查詢才能共享這個cache
<mapper namespace="SubwayInfo"> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 。。。。 </mapper>
代表創建了一個LRU緩存,並每隔60秒刷新,最大存儲512個對象,而且返回的對象被認為是只讀的。
evicition收回策略,默認是LRU
(1)LRU最近最少使用策略,一處做長時間不被使用的對象。
(2)FIFO先進先出策略,按對象進入緩存的順序來移除它們。
(3)SOFT軟引用策略,移除基於垃圾回收器狀態和軟引用規則的對象。
(4)WEAK弱引用策略,更積極地移除基於垃圾收集器狀態和弱引用規則的對象。
3)針對個別查詢進行調整
配置這個的前提是必須配置好上面2)針對mapper的namespace
如果需要對個別查詢進行調整,可以單獨設置cache:
<select id="listAll" resultMap="subwayInfo" useCache="true"> 。。。 </select>
詳解一二級緩存的區別:
一級緩存基於sqlSession默認開啟,在操作數據庫時需要構造SqlSession對象,在對象中有一個HashMap用於存儲緩存數據。不同的SqlSession之間的緩存數據區域是互相不影響的。
一級緩存的作用域是SqlSession范圍的,當在同一個sqlSession中執行兩次相同的sql語句時,第一次執行完畢會將數據庫中查詢的數據寫到緩存(內存),
第二次查詢時會從緩存中獲取數據,不再去底層數據庫查詢,從而提高查詢效率。
需要注意的是,如果SqlSession執行了DML操作(增刪改),並且提交到數據庫,MyBatis則會清空SqlSession中的一級緩存,這樣做的目的是為了保證緩存中存儲的是最新的信息,避免出現臟讀現象。
當一個SqlSession結束后該SqlSession中的一級緩存也就不存在了。
關閉一級緩存后,再次訪問,需要再次獲取一級緩存,然后才能查找數據,否則會拋出異常。
二級緩存是mapper級別的緩存。使用二級緩存時,多個SqlSession使用同一個Mapper的sql語句去操作數據庫,得到的數據會存在二級緩存區域,它同樣是使用HashMap進行數據存儲。相比一級緩存SqlSession,二級緩存的范圍更大,多個Sqlsession可以共用二級緩存,二級緩存是跨SqlSession的。
二級緩存的作用域是mapper的同一個namespace。不同的sqlSession兩次執行相同的namespace下的sql語句,且向sql中傳遞的參數也相同,即最終執行相同的sql語句,則第一次執行完畢會將數據庫中查詢的數據寫到緩存,第二次查詢會從緩存中獲取數據,不再去底層數據庫查詢,從而提高效率。
最后:
對於MyBatis緩存的內容僅做了解即可,因為面對一定的數據量,內置的Cache方式就派不上用場了
並且對查詢結果集做緩存並不是MyBatis框架擅長的,它專心做的應該是SQL映射,所有我們一般采用 OSCache、Memcached等專門的緩存服務器來做更為合理
九.動態SQL咯
很簡單。。也就是標簽元素,,沒什么技術含量
記住拿過來用就好、
各標簽元素
為了支持用戶多樣化操作
1. If
<if test="subwayName!=null and "".equals(subwayName.trim())">AND SUBWAYNAME=#{subwayName}</if>
"" 代表空字符串 某些原因,“”不可用
test內 的返回結果為true走標簽內部結構
否則不進,注意,這里面沒有else標簽,要想實現if-else結構標簽,后面有新引入的標簽,這里不做詳細講解,請繼續去閱讀
2. Where
可以智能 的處理where和and所處的位置
因為我們要動態拼接,where語法:where xxx=x and yyy=y
有時呢,我們拼接sql的時候where子句不好拼接,因為我們確定不了到底哪里是第一個條件的開頭,where到底拼接到哪個語句前面,,所以就誕生了這個where標簽:
<select id="listAll" resultMap="subwayInfo" > SELECT id,<include refid="co"/> FROM subwayInfo <where> <if test="subwayName!=null and "".equals(subwayName.trim())">AND SUBWAYNAME=#{subwayName}</if> <if test="startStation!=null and "".equals(startStation.trim())">AND STARTSTATION=#{startStation},</if> </where> </select>
在這里我們會發現sql因為有兩個if條件不清楚到底會符合一條還是兩條,或者說都不符合,where將要放的位置會異常尷尬
然后呢,可有看到我們這個例子的sql我並沒有寫where 為什么呢?因為無論執行的怎樣我們的where都會為什么自動在最前面拼上where 子句,,如果呢,后面標簽內都不符合那么where 不會拼接
做的很牛逼!!!
3. Choose
這里,,,choose,就是上面所說的可以實現if-else結構的標簽,,沒有久等吧,嘿嘿嘿
Choose標簽呢,他又有兩個子標簽來配合他實現結構功能分別是:
When
If語句或者else if 語句
Otherwise
Else語句
<choose> <when test="">xx</when> <when test="">yy</when> <otherwise>zz</otherwise> </choose>
比如上面的語句
最后只會出現一個值, xx或者yy或者zz
第一個when就充當了if()
第二個when就充當了else if()
就算后面更多when也充當的是else if()語句 (test為true輸出,但when只會執行一道,靠前者優先)
有人疑惑了,那最后的else呢?
Ok ,,,當然是之前沒有出場的otherwise標簽了,(所以條件不滿足的時候走)
學過選擇結構,不傻 的相信都 一點就懂,不多說
4. Set
Set元素主要用於更新操作,它的主要功能和where元素差不多,主要在包含的語句前輸出宇哥set,若包含的語句是以逗號結束的,會自動把改逗號忽略掉,再配合id元素就可以動態地更新需要修改的字段;而不需修改的字段,則可以不再更新(因為有的時候在update操作中使用多個if或者別的選擇標簽,若一部分沒有執行,則導致在語句末尾殘留多余的逗號,解決此問題)
Tip:
<update id="up" parameterType="com.metro.entity.SubwayInfo"> UPDATE `subwayInfo` <set> <if test="subwayName!=null and "".equals(subwayName.trim())"> SUBWAYNAME=#{subwayName}, </if> <if test="startStation!=null and "".equals(startStation.trim())"> STARTSTATION=#{startStation}, </if> </set> WHERE id=#{id} </update>
5. Trim
更為靈活的元素,,,可以替代之前的
Trim元素也會自動識別其標簽內是否有返回值,若有返回值,會在自己包含的內容前加上某些前綴(prefix),也可在其后加上某些后綴(suffix),也可把包含內容的首部某些內存覆蓋(忽略)(prefixOverrides),或者把尾部的某些內容覆蓋(suffixOverrides),無可厚非的強大,可以利用trim替代where或者set元素,實現相同效果
小Tip:
<trim prefix="where" prefixOverrides="and | or"> <if Test=””> and xx = x </if> 。。。 </trim>
不會報錯,,前面的and會自動消除,,賊智能,,后綴處理同理
再解釋一個各屬性:
1> Prefix:
前綴,作用是通過自動識別是否有返回值后,在trim包含的內容上加上前綴,一般用於where
2> Suffix:
后綴,作用是在trim包含的內容上加上后綴
3> prefixOverrides:
對於trim包含內容的首部進行指定內容(如where子句中的and和or)的忽略。
4> suffixOverrides:
對於trim包含內容的首尾部進行指定內容的忽略。
6. Foreach
當MyBatis入參為數組類型的時候,就需要使用foeach來進行迭代取值了
Foreach呢,主要用於構建in條件中,他可以在sql語句中迭代一個集合,,主要屬性有:
1)Item:
表示集合中每個元素進行迭代時的別名
2)Index:
指定一個名稱,用於表示在迭代過程中,每次迭代到的位置
3)Close:
表示該語句以什么結束(這里以in條件語句舉例,,所以用“)”結束,具體根據需求而定)
4)Open:
表示該語句以什么開始(這里以in條件語句舉例,,所以用“(”開始,具體根據需求而定)
5)Separator
表示在每次進行迭代之間以什么符號作為分隔符(這里以in條件語句舉例,,,所以用“,”分隔)
6)Collection:
最關鍵並最容易出錯的屬性,需要格外注意,改屬性必須指定 不同情況下,該屬性的值是不一樣的。注意有三種情況:
a. 若入參為單參數且參數類型是一個List的時候,collection的屬性值為list
b. 若從入參為單參數且參數類型是一個數組的時候,collection屬性值為array
c. 若傳入參數為多參數,就需要把它們封裝為一個Map進行處理
最后來個關於collection三種情況的例子吧:
1.單參數List的類型:
<select id="dynamicForeachTest" parameterType="java.util.List" resultType="Blog"> select * from t_blog where id in <foreach collection="list" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
上述collection的值為list,對應的Mapper是這樣的
public List dynamicForeachTest(List ids);
測試代碼:
@Test public void dynamicForeachTest() { SqlSession session = Util.getSqlSessionFactory().openSession(); BlogMapper blogMapper = session.getMapper(BlogMapper.class); List ids = new ArrayList(); ids.add(1); ids.add(3); ids.add(6); List blogs = blogMapper.dynamicForeachTest(ids); for (Blog blog : blogs) System.out.println(blog); session.close(); }
2.單參數array數組的類型:
<select id="dynamicForeach2Test" parameterType="java.util.ArrayList" resultType="Blog"> select * from t_blog where id in <foreach collection="array" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
上述collection為array,對應的Mapper代碼:
public List dynamicForeach2Test(int[] ids);
對應的測試代碼:
@Test public void dynamicForeach2Test() { SqlSession session = Util.getSqlSessionFactory().openSession(); BlogMapper blogMapper = session.getMapper(BlogMapper.class); int[] ids = new int[] {1,3,6,9}; List blogs = blogMapper.dynamicForeach2Test(ids); for (Blog blog : blogs) System.out.println(blog); session.close(); }
3.自己把參數封裝成Map的類型
<select id="dynamicForeach3Test" parameterType="java.util.HashMap" resultType="Blog"> select * from t_blog where title like "%"#{title}"%" and id in <foreach collection="ids" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
上述collection的值為ids,是傳入的參數Map的key,對應的Mapper代碼:
public List dynamicForeach3Test(Map params);
對應測試代碼:
@Test public void dynamicForeach3Test() { SqlSession session = Util.getSqlSessionFactory().openSession(); BlogMapper blogMapper = session.getMapper(BlogMapper.class); final List ids = new ArrayList(); ids.add(1); ids.add(2); ids.add(3); ids.add(6); ids.add(7); ids.add(9); Map params = new HashMap(); params.put("ids", ids); params.put("title", "中國"); List blogs = blogMapper.dynamicForeach3Test(params); for (Blog blog : blogs) System.out.println(blog); session.close(); }
7. Include
最后提一句這個標簽。。。怕忘了,,用於引用外部sql標簽
未完,待續。。。