mybatis框架執行過程:
1、配置mybatis的配置文件,SqlMapConfig.xml(名稱不固定)
2、通過配置文件,加載mybatis運行環境,創建SqlSessionFactory會話工廠
SqlSessionFactory在實際使用時按單例方式。
3、通過SqlSessionFactory創建SqlSession
SqlSession是一個面向用戶接口(提供操作數據庫方法),實現對象是線程不安全的,建議sqlSession應用場合在方法體內。
4、調用sqlSession的方法去操作數據。
如果需要提交事務,需要執行SqlSession的commit()方法。
5、釋放資源,關閉SqlSession
mapper代理開發方法(建議使用)
只需要程序員編寫mapper接口(就是dao接口)
程序員在編寫mapper.xml(映射文件)和mapper.java需要遵循一個開發規范:
1、mapper.xml中namespace就是mapper.java的類全路徑。
2、mapper.xml中statement的id和mapper.java中方法名一致。
3、mapper.xml中statement的parameterType指定輸入參數的類型和mapper.java的方法輸入 參數類型一致。
4、mapper.xml中statement的resultType指定輸出結果的類型和mapper.java的方法返回值類型一致。
本文內容:
對訂單商品數據模型進行分析。
高級映射:(了解)
實現一對一查詢、一對多、多對多查詢。
延遲加載
查詢緩存
一級緩存
二級緩存(了解mybatis二級緩存使用場景)
mybatis和spirng整合(掌握)
逆向工程(會用)
訂單商品數據模型
數據模型分析思路
1、每張表記錄的數據內容
分模塊對每張表記錄的內容進行熟悉,相當于你學習系統需求(功能)的過程。
2、每張表重要的字段設置
非空字段、外鍵字段
3、數據庫級別表與表之間的關系
外鍵關系
4、表與表之間的業務關系
在分析表與表之間的業務關系時一定要建立在某個業務意義基礎上去分析。
數據模型分析
用戶表user:
記錄了購買商品的用戶信息
訂單表:orders
記錄了用戶所創建的訂單(購買商品的訂單)
訂單明細表:orderdetail:
記錄了訂單的詳細信息即購買商品的信息
商品表:items
記錄了商品信息
表與表之間的業務關系:
在分析表與表之間的業務關系時需要建立在某個業務意義基礎上去分析。
先分析數據級別之間有關系的表之間的業務關系:
usre和orders:
user—->orders : 一個用戶可以創建多個訂單,一對多
orders—>user : 一個訂單只由一個用戶創建,一對一
orders和orderdetail:
orders –> orderdetail:一個訂單可以包括多個訂單明細,因為一個訂單可以購買多個商品,每個商品的購買信息在orderdetail記錄,一對多關系
orderdetail–> orders:一個訂單明細只能包括在一個訂單中,一對一
orderdetail和itesm:
orderdetail—> itesms:一個訂單明細只對應一個商品信息,一對一
items–> orderdetail:一個商品可以包括在多個訂單明細 ,一對多
再分析數據庫級別沒有關系的表之間是否有業務關系:
orders和items:
orders和items之間可以通過orderdetail表建立關系。
user 和 items : 通過其他表構成了多對多關系
一對一查詢
需求 : 查詢訂單信息,關聯查詢創建訂單的用戶信息
使用resultType方式查詢
sql語句使用考慮
確定查詢的主表:訂單表
確定查詢的關聯表:用戶表
關聯查詢使用內鏈接,還是外鏈接?
由于orders表中有一個外鍵(user_id),通過外鍵關聯查詢用戶表只能查詢出一條記錄,可以使用內鏈接。
1
2
3
4
5
6
7
8
9
|
SELECT orders.*, USER.username, USER.sex, USER.address FROM orders, USER WHERE orders.user_id = user.id |
創建pojo(OrdersCustom.java)
將上邊sql查詢的結果映射到pojo中,pojo中必須包括所有查詢列名。
原始的Orders.java不能映射全部字段,需要新創建的pojo。
創建 一個pojo繼承包括查詢字段較多的po類。
OrdersMapperCustom.xml
OrdersMapperCustom.java
編寫測試類
選擇OrdersMapperCustom.java文件右鍵–>選擇New–>Others–> Junit Test Case–>選擇要測試的函數
在OrdersMapperCustomTest.java中編寫如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class OrdersMapperCustomTest { private SqlSessionFactory sqlSessionFactory; // 此方法是在執行testFindUserById之前執行 @Before public void setUp() throws Exception { // 創建sqlSessionFactory // mybatis配置文件 String resource = "SqlMapConfig.xml" ; // 得到配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); // 創建會話工廠,傳入mybatis的配置文件信息 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindOrdersUser() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 創建代理對象 OrdersMapperCustom ordersMapperCustom = sqlSession .getMapper(OrdersMapperCustom. class ); // 調用maper的方法 List<OrdersCustom> list = ordersMapperCustom.findOrdersUser(); System.out.println(list); sqlSession.close(); } |
使用resultMap方式查詢
sql語句 : 同resultType實現的sql
使用resultMap映射的思路
使用resultMap將查詢結果中的訂單信息映射到Orders對象中,在orders類中添加User屬性,將關聯查詢出來的用戶信息映射到orders對象中的user屬性中。
Orders類中添加user屬性
OrdersMapperCustom.xml
定義resultMap
tyep : 表示將整個查詢的結果映射到某個類中 eg:cn.itcast.mybatis.po.Orders
id:表示數據庫表中查詢列的唯一標識,訂單信息的中的唯一標識,如果有多個列組成唯一標識,配置多個id
column:數據庫表中訂單信息的唯一標識列
property:訂單信息的唯一標識列所映射到Orders中哪個屬性
association:用于映射關聯查詢單個對象的信息
property:要將關聯查詢的用戶信息映射到Orders中哪個屬性
javaType:映射到user的哪個屬性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<!-- 訂單查詢關聯用戶的resultMap 將整個查詢的結果映射到cn.itcast.mybatis.po.Orders中 --> <resultMap type= "cn.itcast.mybatis.po.Orders" id= "OrdersUserResultMap" > <!-- 配置映射的訂單信息 --> <!-- id:指定查詢列中的唯 一標識,訂單信息的中的唯 一標識,如果有多個列組成唯一標識,配置多個id column:訂單信息的唯 一標識 列 property:訂單信息的唯 一標識 列所映射到Orders中哪個屬性 --> <id column= "id" property= "id" /> <result column= "user_id" property= "userId" /> <result column= "number" property= "number" /> <result column= "createtime" property= "createtime" /> <result column= "note" property=note/> <!-- 配置映射的關聯的用戶信息 --> <!-- association:用于映射關聯查詢單個對象的信息 property:要將關聯查詢的用戶信息映射到Orders中哪個屬性 --> <association property= "user" javaType= "cn.itcast.mybatis.po.User" > <!-- id:關聯查詢用戶的唯一標識 column:指定唯 一標識用戶信息的列 javaType:映射到user的哪個屬性 --> <id column= "user_id" property= "id" /> <result column= "username" property= "username" /> <result column= "sex" property= "sex" /> <result column= "address" property= "address" /> </association> </resultMap> |
statement定義
OrdersMapperCustom.java
測試代碼
1
2
3
4
5
6
7
8
9
10
11
|
@Test public void testFindOrdersUserResultMap() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 創建代理對象 OrdersMapperCustom ordersMapperCustom = sqlSession .getMapper(OrdersMapperCustom. class ); // 調用maper的方法 List<Orders> list = ordersMapperCustom.findOrdersUserResultMap(); System.out.println(list); sqlSession.close(); } |
resultType和resultMap實現一對一查詢小結
resultType:使用resultType實現較為簡單,如果pojo中沒有包括查詢出來的列名,需要增加列名對應的屬性,即可完成映射。
如果沒有查詢結果的特殊要求建議使用resultType。
resultMap:需要單獨定義resultMap,實現有點麻煩,如果對查詢結果有特殊的要求,使用resultMap可以完成將關聯查詢映射pojo的屬性中。
resultMap可以實現延遲加載,resultType無法實現延遲加載。
一對多查詢
需求 : 查詢訂單及訂單明細的信息。
sql語句
確定主查詢表:訂單表
確定關聯查詢表:訂單明細表
在一對一查詢基礎上添加訂單明細表關聯即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
SELECT orders.*, USER.username, USER.sex, USER.address, orderdetail.id orderdetail_id, orderdetail.items_id, orderdetail.items_num, orderdetail.orders_id FROM orders, USER, orderdetail WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id |
分析 : 使用resultType將上邊的 查詢結果映射到pojo中,訂單信息的就是重復。
要求:對orders映射不能出現重復記錄。
在orders.java類中添加List<orderDetail> orderDetails屬性。
最終會將訂單信息映射到orders中,訂單所對應的訂單明細映射到orders中的orderDetails屬性中。
映射成的orders記錄數為兩條(orders信息不重復)
每個orders中的orderDetails屬性存儲了該訂單所對應的訂單明細。
在Orders.java中添加list訂單明細屬性
OrdersMapperCustom.xml
resultMap定義
使用extends繼承,不用在中配置訂單信息和用戶信息的映射
collection:對關聯查詢到多條記錄映射到集合對象中
property:將關聯查詢到多條記錄映射到cn.itcast.mybatis.po.Orders哪個屬性
ofType:指定映射到list集合屬性中pojo的類型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!-- 訂單及訂單明細的resultMap 使用 extends 繼承,不用在中配置訂單信息和用戶信息的映射 --> <resultMap type= "cn.itcast.mybatis.po.Orders" id= "OrdersAndOrderDetailResultMap" extends = "OrdersUserResultMap" > <!-- 訂單信息 --> <!-- 用戶信息 --> <!-- 使用 extends 繼承,不用在中配置訂單信息和用戶信息的映射 --> <!-- 訂單明細信息 一個訂單關聯查詢出了多條明細,要使用collection進行映射 collection:對關聯查詢到多條記錄映射到集合對象中 property:將關聯查詢到多條記錄映射到cn.itcast.mybatis.po.Orders哪個屬性 ofType:指定映射到list集合屬性中pojo的類型 --> <collection property= "orderdetails" ofType= "cn.itcast.mybatis.po.Orderdetail" > <!-- id:訂單明細唯 一標識 property:要將訂單明細的唯一標識映射到cn.itcast.mybatis.po.Orderdetail的哪個屬性 --> <id column= "orderdetail_id" property= "id" /> <result column= "items_id" property= "itemsId" /> <result column= "items_num" property= "itemsNum" /> <result column= "orders_id" property= "ordersId" /> </collection> </resultMap> |
OrdersMapperCustom.java
測試代碼:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Test public void testFindOrdersAndOrderDetailResultMap() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 創建代理對象 OrdersMapperCustom ordersMapperCustom = sqlSession .getMapper(OrdersMapperCustom. class ); // 調用maper的方法 List<Orders> list = ordersMapperCustom .findOrdersAndOrderDetailResultMap(); System.out.println(list); sqlSession.close(); } |
小結
mybatis使用resultMap的collection對關聯查詢的多條記錄映射到一個list集合屬性中。
使用resultType實現:
將訂單明細映射到orders中的orderdetails中,需要自己處理,使用雙重循環遍歷,去掉重復記錄,將訂單明細放在orderdetails中。
多對多查詢
需求 : 查詢用戶及用戶購買商品信息。
sql語句
查詢主表是:用戶表
關聯表:由于用戶和商品沒有直接關聯,通過訂單和訂單明細進行關聯,所以關聯表:orders、orderdetail、items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
SELECT orders.*, USER.username, USER.sex, USER.address, orderdetail.id orderdetail_id, orderdetail.items_id, orderdetail.items_num, orderdetail.orders_id, items.name items_name, items.detail items_detail, items.price items_price FROM orders, USER, orderdetail, items WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id |
映射思路
將用戶信息映射到user中。
在user類中添加訂單列表屬性List<Orders> orderslist,將用戶創建的訂單映射到orderslist
在Orders中添加訂單明細列表屬性List<OrderDetail>orderdetials,將訂單的明細映射到orderdetials
在OrderDetail中添加Items屬性,將訂單明細所對應的商品映射到Items
OrdersMapperCustom.xml
resultMap定義
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<!-- 查詢用戶及購買的商品 --> <resultMap type= "cn.itcast.mybatis.po.User" id= "UserAndItemsResultMap" > <!-- 用戶信息 --> <id column= "user_id" property= "id" /> <result column= "username" property= "username" /> <result column= "sex" property= "sex" /> <result column= "address" property= "address" /> <!-- 訂單信息 一個用戶對應多個訂單,使用collection映射--> <collection property= "ordersList" ofType= "cn.itcast.mybatis.po.Orders" > <id column= "id" property= "id" /> <result column= "user_id" property= "userId" /> <result column= "number" property= "number" /> <result column= "createtime" property= "createtime" /> <result column= "note" property= "note" /> <!-- 訂單明細 一個訂單包括 多個明細--> <collection property= "orderdetails" ofType= "cn.itcast.mybatis.po.Orderdetail" > <id column= "orderdetail_id" property= "id" /> <result column= "items_id" property= "itemsId" /> <result column= "items_num" property= "itemsNum" /> <result column= "orders_id" property= "ordersId" /> <!-- 商品信息一個訂單明細對應一個商品--> <association property= "items" javaType= "cn.itcast.mybatis.po.Items" > <id column= "items_id" property= "id" /> <result column= "items_name" property= "name" /> <result column= "items_detail" property= "detail" /> <result column= "items_price" property= "price" /> </association> </collection> </collection> </resultMap> |
OrdersMapperCustom.java
測試代碼:
1
2
3
4
5
6
7
8
9
10
11
|
@Test public void testFindUserAndItemsResultMap() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 創建代理對象 OrdersMapperCustom ordersMapperCustom = sqlSession .getMapper(OrdersMapperCustom. class ); // 調用maper的方法 List<User> list = ordersMapperCustom.findUserAndItemsResultMap(); System.out.println(list); sqlSession.close(); } |
多對多查詢總結
將查詢用戶購買的商品信息明細清單,(用戶名、用戶地址、購買商品名稱、購買商品時間、購買商品數量)
針對上邊的需求就使用resultType將查詢到的記錄映射到一個擴展的pojo中,很簡單實現明細清單的功能。
一對多是多對多的特例,如下需求:
查詢用戶購買的商品信息,用戶和商品的關系是多對多關系。
需求1:
查詢字段:用戶賬號、用戶名稱、用戶性別、商品名稱、商品價格(最常見)
企業開發中常見明細列表,用戶購買商品明細列表,
使用resultType將上邊查詢列映射到pojo輸出。
需求2:
查詢字段:用戶賬號、用戶名稱、購買商品數量、商品明細(鼠標移上顯示明細)
使用resultMap將用戶購買的商品明細列表映射到user對象中。
總結:
使用resultMap是針對那些對查詢結果映射有特殊要求的功能,比如特殊要求映射成list中包括多個list。
resultType與resultMap的總結
resultType:
作用:
將查詢結果按照sql列名pojo屬性名一致性映射到pojo中。
場合:
常見一些明細記錄的展示,比如用戶購買商品明細,將關聯查詢信息全部展示在頁面時,此時可直接使用resultType將每一條記錄映
射到pojo中,在前端頁面遍歷list(list中是pojo)即可。
resultMap:
使用association和collection完成一對一和一對多高級映射(對結果有特殊的映射要求)。
association:
作用:
將關聯查詢信息映射到一個pojo對象中。
場合:
為了方便查詢關聯信息可以使用association將關聯訂單信息映射為用戶對象的pojo屬性中,比如:查詢訂單及關聯用戶信息。
使用resultType無法將查詢結果映射到pojo對象的pojo屬性中,根據對結果集查詢遍歷的需要選擇使用resultType還是resultMap。
collection:
作用:
將關聯查詢信息映射到一個list集合中。
場合:
為了方便查詢遍歷關聯信息可以使用collection將關聯信息映射到list集合中,比如:查詢用戶權限范圍模塊及模塊下的菜單,可使用collection將模塊映射到模塊list中,將菜單列表映射到模塊對象的菜單list屬性中,這樣的作的目的也是方便對查詢結果集進行遍歷查詢。
如果使用resultType無法將查詢結果映射到list集合中。
延遲加載
resultMap可以實現高級映射(使用association、collection實現一對一及一對多映射),association、collection具備延遲加載功能。
需求:
如果查詢訂單并且關聯查詢用戶信息。如果先查詢訂單信息即可滿足要求,當我們需要查詢用戶信息時再查詢用戶信息。把對用戶信息的按需去查詢就是延遲加載。
延遲加載:先從單表查詢、需要時再從關聯表去關聯查詢,大大提高數據庫性能,因為查詢單表要比關聯查詢多張表速度要快。
使用association實現延遲加載
需求 : 查詢訂單并且關聯查詢用戶信息
OrdresMapperCustom.xml
需要定義兩個mapper的方法對應的statement。
1、只查詢訂單信息
1
|
SELECT * FROM orders |
在查詢訂單的statement中使用association去延遲加載(執行)下邊的satatement(關聯查詢用戶信息)
2、關聯查詢用戶信息
通過上邊查詢到的訂單信息中user_id去關聯查詢用戶信息
使用UserMapper.xml中的findUserById
上邊先去執行findOrdersUserLazyLoading,當需要去查詢用戶的時候再去執行findUserById,通過resultMap的定義將延遲加載執行配置起來。
延遲加載resultMap
使用association中的select指定延遲加載去執行的statement的id。
1
2
3
4
5
6
7
8
9
|
<!-- 延遲加載的resultMap --> <resultMap type= "cn.itcast.mybatis.po.Orders" id= "OrdersUserLazyLoadingResultMap" > <!--對訂單信息進行映射配置 --> <id column= "id" property= "id" /> <result column= "user_id" property= "userId" /> <result column= "number" property= "number" /> <result column= "createtime" property= "createtime" /> <result column= "note" property= "note" /> <!-- 實現對用戶信息進行延遲加載 |
select:指定延遲加載需要執行的statement的id(是根據user_id查詢用戶信息的statement)
要使用userMapper.xml中findUserById完成根據用戶id(user_id)用戶信息的查詢,如果findUserById不在本mapper中需要前邊加namespace
column:訂單信息中關聯用戶信息查詢的列,是user_id
關聯查詢的sql理解為:
1
2
3
4
5
6
7
8
9
10
|
SELECT orders.*, (SELECT username FROM USER WHERE orders.user_id = user.id)username, (SELECT sex FROM USER WHERE orders.user_id = user.id)sex FROM orders --> <association property= "user" javaType= "cn.itcast.mybatis.po.User" select= "cn.itcast.mybatis.mapper.UserMapper.findUserById" column= "user_id" > <!-- 實現對用戶信息進行延遲加載 --> </association> </resultMap> |
OrderesMapperCustom.java
測試思路:
1、執行上邊mapper方法(findOrdersUserLazyLoading),內部去調用cn.itcast.mybatis.mapper.OrdersMapperCustom中的findOrdersUserLazyLoading只查詢orders信息(單表)。
2、在程序中去遍歷上一步驟查詢出的List<Orders>,當我們調用Orders中的getUser方法時,開始進行延遲加載。
3、延遲加載,去調用UserMapper.xml中findUserbyId這個方法獲取用戶信息。
延遲加載配置
mybatis默認沒有開啟延遲加載,需要在SqlMapConfig.xml中setting配置。
在mybatis核心配置文件中配置:
設置項 描述 允許值 默認值
lazyLoadingEnabled 全局性設置懶加載。如果設為‘false',則所有相關聯的都會被初始化加載。 true or false false
aggressiveLazyLoading 當設置為‘true'的時候,懶加載的對象可能被任何懶屬性全部加載。否則,每個屬性都按需加載。 true or false true
在SqlMapConfig.xml中配置:
測試代碼
延遲加載思考
不使用mybatis提供的association及collection中的延遲加載功能,如何實現延遲加載?
實現方法如下:
定義兩個mapper方法:
1、查詢訂單列表
2、根據用戶id查詢用戶信息
實現思路:
先去查詢第一個mapper方法,獲取訂單信息列表
在程序中(service),按需去調用第二個mapper方法去查詢用戶信息。
總之:使用延遲加載方法,先去查詢簡單的sql(最好單表,也可以關聯查詢),再去按需要加載關聯查詢的其它信息。
查詢緩存
mybatis提供查詢緩存,用于減輕數據壓力,提高數據庫性能。
mybaits提供一級緩存,和二級緩存。
一級緩存是SqlSession級別的緩存。在操作數據庫時需要構造 sqlSession對象,在對象中有一個數據結構(HashMap)用于存儲緩存數據。不同的sqlSession之間的緩存數據區域(HashMap)是互相不影響的。
二級緩存是mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。
為什么要用緩存?
如果緩存中有數據就不用從數據庫中獲取,大大提高系統性能。
一級緩存
一級緩存工作原理
第一次發起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,如果沒有,從數據庫查詢用戶信息。
得到用戶信息,將用戶信息存儲到一級緩存中。
如果sqlSession去執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。
第二次發起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。
一級緩存測試
mybatis默認支持一級緩存,不需要在配置文件去配置。
按照上邊一級緩存原理步驟去測試。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//OrdersMapperCusntomTest.java @Test public void testCache1() throws Exception{ SqlSession sqlSession = sqlSessionFactory.openSession(); //創建代理對象 UserMapper userMapper = sqlSession.getMapper(UserMapper. class ); //下邊查詢使用一個SqlSession //第一次發起請求,查詢id為1的用戶 User user1 = userMapper.findUserById( 1 ); System.out.println(user1); // 如果sqlSession去執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。 //更新user1的信息 user1.setUsername( "測試用戶22" ); userMapper.updateUser(user1); //執行commit操作去清空緩存 sqlSession.commit(); //第二次發起請求,查詢id為1的用戶 User user2 = userMapper.findUserById( 1 ); System.out.println(user2); sqlSession.close(); } |
一級緩存應用
正式開發,是將mybatis和spring進行整合開發,事務控制在service中。
一個service方法中包括 很多mapper方法調用。
1
2
3
4
5
6
|
service{ //開始執行時,開啟事務,創建SqlSession對象 //第一次調用mapper的方法findUserById(1) //第二次調用mapper的方法findUserById(1),從一級緩存中取數據 //方法結束,sqlSession關閉 } |
如果是執行兩次service調用查詢相同 的用戶信息,不走一級緩存,因為session方法結束,sqlSession就關閉,一級緩存就清空。
二級緩存
原理
首先開啟mybatis的二級緩存。
sqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息會將查詢數據存儲到二級緩存中。
如果SqlSession3去執行相同 mapper下sql,執行commit提交,清空該 mapper下的二級緩存區域的數據。
sqlSession2去查詢用戶id為1的用戶信息,去緩存中找是否存在數據,如果存在直接從緩存中取出數據。
二級緩存與一級緩存區別,二級緩存的范圍更大,多個sqlSession可以共享一個UserMapper的二級緩存區域。
UserMapper有一個二級緩存區域(按namespace分) ,其它mapper也有自己的二級緩存區域(按namespace分)。
每一個namespace的mapper都有一個二緩存區域,兩個mapper的namespace如果相同,這兩個mapper執行sql查詢到數據將存在相同的二級緩存區域中。
開啟二級緩存
mybaits的二級緩存是mapper范圍級別,除了在SqlMapConfig.xml設置二級緩存的總開關,還要在具體的mapper.xml中開啟二級緩存。
在核心配置文件SqlMapConfig.xml中加入
1
|
<setting name= "cacheEnabled" value= "true" /> |
描述 允許值 默認值
cacheEnabled 對在此配置文件下的所有cache 進行全局性開/關設置。 true or false
在UserMapper.xml中開啟二級緩存,UserMapper.xml下的sql執行完成會存儲到它的緩存區域(HashMap)。
調用pojo類實現序列化接口
為了將緩存數據取出執行反序列化操作,因為二級緩存數據存儲介質多種多樣,不一樣在內存。
二級緩存測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@Test public void testCache2() throws Exception { SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); SqlSession sqlSession3 = sqlSessionFactory.openSession(); // 創建代理對象 UserMapper userMapper1 = sqlSession1.getMapper(UserMapper. class ); // 第一次發起請求,查詢id為1的用戶 User user1 = userMapper1.findUserById( 1 ); System.out.println(user1); //這里執行關閉操作,將sqlsession中的數據寫到二級緩存區域 sqlSession1.close(); //使用sqlSession3執行commit()操作 UserMapper userMapper3 = sqlSession3.getMapper(UserMapper. class ); User user = userMapper3.findUserById( 1 ); user.setUsername( "張明明" ); userMapper3.updateUser(user); //執行提交,清空UserMapper下邊的二級緩存 sqlSession3.commit(); sqlSession3.close(); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper. class ); // 第二次發起請求,查詢id為1的用戶 User user2 = userMapper2.findUserById( 1 ); System.out.println(user2); sqlSession2.close(); } |
useCache配置
在statement中設置useCache=false可以禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認情況是true,即該sql使用二級緩存。
1
|
< select id= "findOrderListResultMap" resultMap= "ordersUserMap" useCache= "false" > |
總結:針對每次查詢都需要最新的數據sql,要設置成useCache=false,禁用二級緩存。
刷新緩存
就是清空緩存
在mapper的同一個namespace中,如果有其它insert、update、delete操作數據后需要刷新緩存,如果不執行刷新緩存會出現臟讀。
設置statement配置中的flushCache=”true” 屬性,默認情況下為true即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數據庫表中的查詢數據會出現臟讀。
1
|
< insert id= "insertUser" parameterType= "cn.itcast.mybatis.po.User" flushCache= "true" > |
總結:一般下執行完commit操作都需要刷新緩存,flushCache=true表示刷新緩存,這樣可以避免數據庫臟讀。
mybatis整合ehcache
ehcache是一個分布式緩存框架。
分布緩存
我們系統為了提高系統并發,性能、一般對系統進行分布式部署(集群部署方式)
不使用分布緩存,緩存的數據在各各服務單獨存儲,不方便系統 開發。所以要使用分布式緩存對緩存數據進行集中管理。
mybatis無法實現分布式緩存,需要和其它分布式緩存框架進行整合。
整合ehcache方法(掌握)
mybatis提供了一個cache接口,如果要實現自己的緩存邏輯,實現cache接口開發即可。
mybatis和ehcache整合,mybatis和ehcache整合包中提供了一個cache接口的實現類。
mybatis默認實現cache類是:
加入ehcache包
整合ehcache
配置mapper中cache中的type為ehcache對cache接口的實現類型。
加入ehcache的配置文件(在classpath下配置ehcache.xml)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<ehcache xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation= "../config/ehcache.xsd" > <diskStore path= "F:\develop\ehcache" /> <defaultCache maxElementsInMemory= "1000" maxElementsOnDisk= "10000000" eternal= "false" overflowToDisk= "false" timeToIdleSeconds= "120" timeToLiveSeconds= "120" diskExpiryThreadIntervalSeconds= "120" memoryStoreEvictionPolicy= "LRU" > </defaultCache> </ehcache> |
屬性說明:
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二級緩存技術降低數據庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。
實現方法如下:通過設置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,比如設置為30分鐘、60分鐘、24小時等,根據需求而定。
二級緩存局限性
mybatis二級緩存對細粒度的數據級別的緩存實現不好,比如如下需求:對商品信息進行緩存,由于商品信息查詢訪問量大,但是要求用戶每次都能查詢最新的商品信息,此時如果使用mybatis的二級緩存就無法實現當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因為mybaits的二級緩存區域以mapper為單位劃分,當一個商品信息變化會將所有商品信息的緩存數據全部清空。解決此類問題需要在業務層根據需求對數據有針對性緩存。
原文鏈接:http://blog.csdn.net/lutianfeiml/article/details/51781318