MyBatis高級映射和查詢緩存
mybatis框架執(zhí)行過程:
1、配置mybatis的配置文件,SqlMapConfig.xml(名稱不固定)
2、通過配置文件,加載mybatis運行環(huán)境,創(chuàng)建SqlSessionFactory會話工廠
SqlSessionFactory在實際使用時按單例方式。
3、通過SqlSessionFactory創(chuàng)建SqlSession
SqlSession是一個面向用戶接口(提供操作數(shù)據(jù)庫方法),實現(xiàn)對象是線程不安全的,建議sqlSession應用場合在方法體內(nèi)。
4、調(diào)用sqlSession的方法去操作數(shù)據(jù)。
如果需要提交事務(wù),需要執(zhí)行SqlSession的commit()方法。
5、釋放資源,關(guān)閉SqlSession
mapper代理開發(fā)方法(建議使用)
只需要程序員編寫mapper接口(就是dao接口)
程序員在編寫mapper.xml(映射文件)和mapper.java需要遵循一個開發(fā)規(guī)范:
1、mapper.xml中namespace就是mapper.java的類全路徑。
2、mapper.xml中statement的id和mapper.java中方法名一致。
3、mapper.xml中statement的parameterType指定輸入?yún)?shù)的類型和mapper.java的方法輸入 參數(shù)類型一致。
4、mapper.xml中statement的resultType指定輸出結(jié)果的類型和mapper.java的方法返回值類型一致。
本文內(nèi)容:
對訂單商品數(shù)據(jù)模型進行分析。
高級映射:(了解)
實現(xiàn)一對一查詢、一對多、多對多查詢。
延遲加載
查詢緩存
一級緩存
二級緩存(了解mybatis二級緩存使用場景)
mybatis和spirng整合(掌握)
逆向工程(會用)
訂單商品數(shù)據(jù)模型
數(shù)據(jù)模型分析思路
1、每張表記錄的數(shù)據(jù)內(nèi)容
分模塊對每張表記錄的內(nèi)容進行熟悉,相當于你學習系統(tǒng)需求(功能)的過程。
2、每張表重要的字段設(shè)置
非空字段、外鍵字段
3、數(shù)據(jù)庫級別表與表之間的關(guān)系
外鍵關(guān)系
4、表與表之間的業(yè)務(wù)關(guān)系
在分析表與表之間的業(yè)務(wù)關(guān)系時一定要建立在某個業(yè)務(wù)意義基礎(chǔ)上去分析。
數(shù)據(jù)模型分析
用戶表user:
記錄了購買商品的用戶信息
訂單表:orders
記錄了用戶所創(chuàng)建的訂單(購買商品的訂單)
訂單明細表:orderdetail:
記錄了訂單的詳細信息即購買商品的信息
商品表:items
記錄了商品信息
表與表之間的業(yè)務(wù)關(guān)系:
在分析表與表之間的業(yè)務(wù)關(guān)系時需要建立在某個業(yè)務(wù)意義基礎(chǔ)上去分析。
先分析數(shù)據(jù)級別之間有關(guān)系的表之間的業(yè)務(wù)關(guān)系:
usre和orders:
user—->orders : 一個用戶可以創(chuàng)建多個訂單,一對多
orders—>user : 一個訂單只由一個用戶創(chuàng)建,一對一
orders和orderdetail:
orders –> orderdetail:一個訂單可以包括多個訂單明細,因為一個訂單可以購買多個商品,每個商品的購買信息在orderdetail記錄,一對多關(guān)系
orderdetail–> orders:一個訂單明細只能包括在一個訂單中,一對一
orderdetail和itesm:
orderdetail—> itesms:一個訂單明細只對應一個商品信息,一對一
items–> orderdetail:一個商品可以包括在多個訂單明細 ,一對多
再分析數(shù)據(jù)庫級別沒有關(guān)系的表之間是否有業(yè)務(wù)關(guān)系:
orders和items:
orders和items之間可以通過orderdetail表建立關(guān)系。
user 和 items : 通過其他表構(gòu)成了多對多關(guān)系
一對一查詢
需求 : 查詢訂單信息,關(guān)聯(lián)查詢創(chuàng)建訂單的用戶信息
使用resultType方式查詢
sql語句使用考慮
確定查詢的主表:訂單表
確定查詢的關(guān)聯(lián)表:用戶表
關(guān)聯(lián)查詢使用內(nèi)鏈接,還是外鏈接?
由于orders表中有一個外鍵(user_id),通過外鍵關(guān)聯(lián)查詢用戶表只能查詢出一條記錄,可以使用內(nèi)鏈接。
SELECT orders.*, USER.username, USER.sex, USER.address FROM orders, USER WHERE orders.user_id = user.id
創(chuàng)建pojo(OrdersCustom.java)
將上邊sql查詢的結(jié)果映射到pojo中,pojo中必須包括所有查詢列名。
原始的Orders.java不能映射全部字段,需要新創(chuàng)建的pojo。
創(chuàng)建 一個pojo繼承包括查詢字段較多的po類。
OrdersMapperCustom.xml
OrdersMapperCustom.java
編寫測試類
選擇OrdersMapperCustom.java文件右鍵–>選擇New–>Others–> Junit Test Case–>選擇要測試的函數(shù)
在OrdersMapperCustomTest.java中編寫如下代碼:
public class OrdersMapperCustomTest { private SqlSessionFactory sqlSessionFactory; // 此方法是在執(zhí)行testFindUserById之前執(zhí)行 @Before public void setUp() throws Exception { // 創(chuàng)建sqlSessionFactory // mybatis配置文件 String resource = "SqlMapConfig.xml"; // 得到配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); // 創(chuàng)建會話工廠,傳入mybatis的配置文件信息 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindOrdersUser() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 創(chuàng)建代理對象 OrdersMapperCustom ordersMapperCustom = sqlSession .getMapper(OrdersMapperCustom.class); // 調(diào)用maper的方法 List<OrdersCustom> list = ordersMapperCustom.findOrdersUser(); System.out.println(list); sqlSession.close(); }
使用resultMap方式查詢
sql語句 : 同resultType實現(xiàn)的sql
使用resultMap映射的思路
使用resultMap將查詢結(jié)果中的訂單信息映射到Orders對象中,在orders類中添加User屬性,將關(guān)聯(lián)查詢出來的用戶信息映射到orders對象中的user屬性中。
Orders類中添加user屬性
OrdersMapperCustom.xml
定義resultMap
tyep : 表示將整個查詢的結(jié)果映射到某個類中 eg:cn.itcast.mybatis.po.Orders
id:表示數(shù)據(jù)庫表中查詢列的唯一標識,訂單信息的中的唯一標識,如果有多個列組成唯一標識,配置多個id
column:數(shù)據(jù)庫表中訂單信息的唯一標識列
property:訂單信息的唯一標識列所映射到Orders中哪個屬性
association:用于映射關(guān)聯(lián)查詢單個對象的信息
property:要將關(guān)聯(lián)查詢的用戶信息映射到Orders中哪個屬性
javaType:映射到user的哪個屬性
<!-- 訂單查詢關(guān)聯(lián)用戶的resultMap 將整個查詢的結(jié)果映射到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/> <!-- 配置映射的關(guān)聯(lián)的用戶信息 --> <!-- association:用于映射關(guān)聯(lián)查詢單個對象的信息 property:要將關(guān)聯(lián)查詢的用戶信息映射到Orders中哪個屬性 --> <association property="user" javaType="cn.itcast.mybatis.po.User"> <!-- id:關(guān)聯(lián)查詢用戶的唯一標識 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
測試代碼
@Test public void testFindOrdersUserResultMap() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 創(chuàng)建代理對象 OrdersMapperCustom ordersMapperCustom = sqlSession .getMapper(OrdersMapperCustom.class); // 調(diào)用maper的方法 List<Orders> list = ordersMapperCustom.findOrdersUserResultMap(); System.out.println(list); sqlSession.close(); }
resultType和resultMap實現(xiàn)一對一查詢小結(jié)
resultType:使用resultType實現(xiàn)較為簡單,如果pojo中沒有包括查詢出來的列名,需要增加列名對應的屬性,即可完成映射。
如果沒有查詢結(jié)果的特殊要求建議使用resultType。
resultMap:需要單獨定義resultMap,實現(xiàn)有點麻煩,如果對查詢結(jié)果有特殊的要求,使用resultMap可以完成將關(guān)聯(lián)查詢映射pojo的屬性中。
resultMap可以實現(xiàn)延遲加載,resultType無法實現(xiàn)延遲加載。
一對多查詢
需求 : 查詢訂單及訂單明細的信息。
sql語句
確定主查詢表:訂單表
確定關(guān)聯(lián)查詢表:訂單明細表
在一對一查詢基礎(chǔ)上添加訂單明細表關(guān)聯(lián)即可。
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將上邊的 查詢結(jié)果映射到pojo中,訂單信息的就是重復。
要求:對orders映射不能出現(xiàn)重復記錄。
在orders.java類中添加List<orderDetail> orderDetails屬性。
最終會將訂單信息映射到orders中,訂單所對應的訂單明細映射到orders中的orderDetails屬性中。
映射成的orders記錄數(shù)為兩條(orders信息不重復)
每個orders中的orderDetails屬性存儲了該訂單所對應的訂單明細。
在Orders.java中添加list訂單明細屬性
OrdersMapperCustom.xml
resultMap定義
使用extends繼承,不用在中配置訂單信息和用戶信息的映射
collection:對關(guān)聯(lián)查詢到多條記錄映射到集合對象中
property:將關(guān)聯(lián)查詢到多條記錄映射到cn.itcast.mybatis.po.Orders哪個屬性
ofType:指定映射到list集合屬性中pojo的類型
<!-- 訂單及訂單明細的resultMap 使用extends繼承,不用在中配置訂單信息和用戶信息的映射 --> <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersAndOrderDetailResultMap" extends="OrdersUserResultMap"> <!-- 訂單信息 --> <!-- 用戶信息 --> <!-- 使用extends繼承,不用在中配置訂單信息和用戶信息的映射 --> <!-- 訂單明細信息 一個訂單關(guān)聯(lián)查詢出了多條明細,要使用collection進行映射 collection:對關(guān)聯(lián)查詢到多條記錄映射到集合對象中 property:將關(guān)聯(lián)查詢到多條記錄映射到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
測試代碼:
@Test public void testFindOrdersAndOrderDetailResultMap() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 創(chuàng)建代理對象 OrdersMapperCustom ordersMapperCustom = sqlSession .getMapper(OrdersMapperCustom.class); // 調(diào)用maper的方法 List<Orders> list = ordersMapperCustom .findOrdersAndOrderDetailResultMap(); System.out.println(list); sqlSession.close(); }
小結(jié)
mybatis使用resultMap的collection對關(guān)聯(lián)查詢的多條記錄映射到一個list集合屬性中。
使用resultType實現(xiàn):
將訂單明細映射到orders中的orderdetails中,需要自己處理,使用雙重循環(huán)遍歷,去掉重復記錄,將訂單明細放在orderdetails中。
多對多查詢
需求 : 查詢用戶及用戶購買商品信息。
sql語句
查詢主表是:用戶表
關(guān)聯(lián)表:由于用戶和商品沒有直接關(guān)聯(lián),通過訂單和訂單明細進行關(guān)聯(lián),所以關(guān)聯(lián)表:orders、orderdetail、items
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,將用戶創(chuàng)建的訂單映射到orderslist
在Orders中添加訂單明細列表屬性List<OrderDetail>orderdetials,將訂單的明細映射到orderdetials
在OrderDetail中添加Items屬性,將訂單明細所對應的商品映射到Items
OrdersMapperCustom.xml
resultMap定義
<!-- 查詢用戶及購買的商品 --> <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
測試代碼:
@Test public void testFindUserAndItemsResultMap() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // 創(chuàng)建代理對象 OrdersMapperCustom ordersMapperCustom = sqlSession .getMapper(OrdersMapperCustom.class); // 調(diào)用maper的方法 List<User> list = ordersMapperCustom.findUserAndItemsResultMap(); System.out.println(list); sqlSession.close(); }
多對多查詢總結(jié)
將查詢用戶購買的商品信息明細清單,(用戶名、用戶地址、購買商品名稱、購買商品時間、購買商品數(shù)量)
針對上邊的需求就使用resultType將查詢到的記錄映射到一個擴展的pojo中,很簡單實現(xiàn)明細清單的功能。
一對多是多對多的特例,如下需求:
查詢用戶購買的商品信息,用戶和商品的關(guān)系是多對多關(guān)系。
需求1:
查詢字段:用戶賬號、用戶名稱、用戶性別、商品名稱、商品價格(最常見)
企業(yè)開發(fā)中常見明細列表,用戶購買商品明細列表,
使用resultType將上邊查詢列映射到pojo輸出。
需求2:
查詢字段:用戶賬號、用戶名稱、購買商品數(shù)量、商品明細(鼠標移上顯示明細)
使用resultMap將用戶購買的商品明細列表映射到user對象中。
總結(jié):
使用resultMap是針對那些對查詢結(jié)果映射有特殊要求的功能,比如特殊要求映射成list中包括多個list。
resultType與resultMap的總結(jié)
resultType:
作用:
將查詢結(jié)果按照sql列名pojo屬性名一致性映射到pojo中。
場合:
常見一些明細記錄的展示,比如用戶購買商品明細,將關(guān)聯(lián)查詢信息全部展示在頁面時,此時可直接使用resultType將每一條記錄映
射到pojo中,在前端頁面遍歷list(list中是pojo)即可。
resultMap:
使用association和collection完成一對一和一對多高級映射(對結(jié)果有特殊的映射要求)。
association:
作用:
將關(guān)聯(lián)查詢信息映射到一個pojo對象中。
場合:
為了方便查詢關(guān)聯(lián)信息可以使用association將關(guān)聯(lián)訂單信息映射為用戶對象的pojo屬性中,比如:查詢訂單及關(guān)聯(lián)用戶信息。
使用resultType無法將查詢結(jié)果映射到pojo對象的pojo屬性中,根據(jù)對結(jié)果集查詢遍歷的需要選擇使用resultType還是resultMap。
collection:
作用:
將關(guān)聯(lián)查詢信息映射到一個list集合中。
場合:
為了方便查詢遍歷關(guān)聯(lián)信息可以使用collection將關(guān)聯(lián)信息映射到list集合中,比如:查詢用戶權(quán)限范圍模塊及模塊下的菜單,可使用collection將模塊映射到模塊list中,將菜單列表映射到模塊對象的菜單list屬性中,這樣的作的目的也是方便對查詢結(jié)果集進行遍歷查詢。
如果使用resultType無法將查詢結(jié)果映射到list集合中。
延遲加載
resultMap可以實現(xiàn)高級映射(使用association、collection實現(xiàn)一對一及一對多映射),association、collection具備延遲加載功能。
需求:
如果查詢訂單并且關(guān)聯(lián)查詢用戶信息。如果先查詢訂單信息即可滿足要求,當我們需要查詢用戶信息時再查詢用戶信息。把對用戶信息的按需去查詢就是延遲加載。
延遲加載:先從單表查詢、需要時再從關(guān)聯(lián)表去關(guān)聯(lián)查詢,大大提高數(shù)據(jù)庫性能,因為查詢單表要比關(guān)聯(lián)查詢多張表速度要快。
使用association實現(xiàn)延遲加載
需求 : 查詢訂單并且關(guān)聯(lián)查詢用戶信息
OrdresMapperCustom.xml
需要定義兩個mapper的方法對應的statement。
1、只查詢訂單信息
SELECT * FROM orders
在查詢訂單的statement中使用association去延遲加載(執(zhí)行)下邊的satatement(關(guān)聯(lián)查詢用戶信息)
2、關(guān)聯(lián)查詢用戶信息
通過上邊查詢到的訂單信息中user_id去關(guān)聯(lián)查詢用戶信息
使用UserMapper.xml中的findUserById
上邊先去執(zhí)行findOrdersUserLazyLoading,當需要去查詢用戶的時候再去執(zhí)行findUserById,通過resultMap的定義將延遲加載執(zhí)行配置起來。
延遲加載resultMap
使用association中的select指定延遲加載去執(zhí)行的statement的id。
<!-- 延遲加載的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"/> <!-- 實現(xiàn)對用戶信息進行延遲加載
select:指定延遲加載需要執(zhí)行的statement的id(是根據(jù)user_id查詢用戶信息的statement)
要使用userMapper.xml中findUserById完成根據(jù)用戶id(user_id)用戶信息的查詢,如果findUserById不在本mapper中需要前邊加namespace
column:訂單信息中關(guān)聯(lián)用戶信息查詢的列,是user_id
關(guān)聯(lián)查詢的sql理解為:
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"> <!-- 實現(xiàn)對用戶信息進行延遲加載 --> </association> </resultMap>
OrderesMapperCustom.java
測試思路:
1、執(zhí)行上邊mapper方法(findOrdersUserLazyLoading),內(nèi)部去調(diào)用cn.itcast.mybatis.mapper.OrdersMapperCustom中的findOrdersUserLazyLoading只查詢orders信息(單表)。
2、在程序中去遍歷上一步驟查詢出的List<Orders>,當我們調(diào)用Orders中的getUser方法時,開始進行延遲加載。
3、延遲加載,去調(diào)用UserMapper.xml中findUserbyId這個方法獲取用戶信息。
延遲加載配置
mybatis默認沒有開啟延遲加載,需要在SqlMapConfig.xml中setting配置。
在mybatis核心配置文件中配置:
設(shè)置項 描述 允許值 默認值
lazyLoadingEnabled 全局性設(shè)置懶加載。如果設(shè)為‘false',則所有相關(guān)聯(lián)的都會被初始化加載。 true or false false
aggressiveLazyLoading 當設(shè)置為‘true'的時候,懶加載的對象可能被任何懶屬性全部加載。否則,每個屬性都按需加載。 true or false true
在SqlMapConfig.xml中配置:
測試代碼
延遲加載思考
不使用mybatis提供的association及collection中的延遲加載功能,如何實現(xiàn)延遲加載?
實現(xiàn)方法如下:
定義兩個mapper方法:
1、查詢訂單列表
2、根據(jù)用戶id查詢用戶信息
實現(xiàn)思路:
先去查詢第一個mapper方法,獲取訂單信息列表
在程序中(service),按需去調(diào)用第二個mapper方法去查詢用戶信息。
總之:使用延遲加載方法,先去查詢簡單的sql(最好單表,也可以關(guān)聯(lián)查詢),再去按需要加載關(guān)聯(lián)查詢的其它信息。
查詢緩存
mybatis提供查詢緩存,用于減輕數(shù)據(jù)壓力,提高數(shù)據(jù)庫性能。
mybaits提供一級緩存,和二級緩存。
一級緩存是SqlSession級別的緩存。在操作數(shù)據(jù)庫時需要構(gòu)造 sqlSession對象,在對象中有一個數(shù)據(jù)結(jié)構(gòu)(HashMap)用于存儲緩存數(shù)據(jù)。不同的sqlSession之間的緩存數(shù)據(jù)區(qū)域(HashMap)是互相不影響的。
二級緩存是mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。
為什么要用緩存?
如果緩存中有數(shù)據(jù)就不用從數(shù)據(jù)庫中獲取,大大提高系統(tǒng)性能。
一級緩存
一級緩存工作原理
第一次發(fā)起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,如果沒有,從數(shù)據(jù)庫查詢用戶信息。
得到用戶信息,將用戶信息存儲到一級緩存中。
如果sqlSession去執(zhí)行commit操作(執(zhí)行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。
第二次發(fā)起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。
一級緩存測試
mybatis默認支持一級緩存,不需要在配置文件去配置。
按照上邊一級緩存原理步驟去測試。
//OrdersMapperCusntomTest.java @Test public void testCache1() throws Exception{ SqlSession sqlSession = sqlSessionFactory.openSession();//創(chuàng)建代理對象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //下邊查詢使用一個SqlSession //第一次發(fā)起請求,查詢id為1的用戶 User user1 = userMapper.findUserById(1); System.out.println(user1); // 如果sqlSession去執(zhí)行commit操作(執(zhí)行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。 //更新user1的信息 user1.setUsername("測試用戶22"); userMapper.updateUser(user1); //執(zhí)行commit操作去清空緩存 sqlSession.commit(); //第二次發(fā)起請求,查詢id為1的用戶 User user2 = userMapper.findUserById(1); System.out.println(user2); sqlSession.close(); }
一級緩存應用
正式開發(fā),是將mybatis和spring進行整合開發(fā),事務(wù)控制在service中。
一個service方法中包括 很多mapper方法調(diào)用。
service{ //開始執(zhí)行時,開啟事務(wù),創(chuàng)建SqlSession對象 //第一次調(diào)用mapper的方法findUserById(1) //第二次調(diào)用mapper的方法findUserById(1),從一級緩存中取數(shù)據(jù) //方法結(jié)束,sqlSession關(guān)閉 }
如果是執(zhí)行兩次service調(diào)用查詢相同 的用戶信息,不走一級緩存,因為session方法結(jié)束,sqlSession就關(guān)閉,一級緩存就清空。
二級緩存
原理
首先開啟mybatis的二級緩存。
sqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息會將查詢數(shù)據(jù)存儲到二級緩存中。
如果SqlSession3去執(zhí)行相同 mapper下sql,執(zhí)行commit提交,清空該 mapper下的二級緩存區(qū)域的數(shù)據(jù)。
sqlSession2去查詢用戶id為1的用戶信息,去緩存中找是否存在數(shù)據(jù),如果存在直接從緩存中取出數(shù)據(jù)。
二級緩存與一級緩存區(qū)別,二級緩存的范圍更大,多個sqlSession可以共享一個UserMapper的二級緩存區(qū)域。
UserMapper有一個二級緩存區(qū)域(按namespace分) ,其它mapper也有自己的二級緩存區(qū)域(按namespace分)。
每一個namespace的mapper都有一個二緩存區(qū)域,兩個mapper的namespace如果相同,這兩個mapper執(zhí)行sql查詢到數(shù)據(jù)將存在相同的二級緩存區(qū)域中。
開啟二級緩存
mybaits的二級緩存是mapper范圍級別,除了在SqlMapConfig.xml設(shè)置二級緩存的總開關(guān),還要在具體的mapper.xml中開啟二級緩存。
在核心配置文件SqlMapConfig.xml中加入
<setting name="cacheEnabled" value="true"/>
描述 允許值 默認值
cacheEnabled 對在此配置文件下的所有cache 進行全局性開/關(guān)設(shè)置。 true or false
在UserMapper.xml中開啟二級緩存,UserMapper.xml下的sql執(zhí)行完成會存儲到它的緩存區(qū)域(HashMap)。
調(diào)用pojo類實現(xiàn)序列化接口
為了將緩存數(shù)據(jù)取出執(zhí)行反序列化操作,因為二級緩存數(shù)據(jù)存儲介質(zhì)多種多樣,不一樣在內(nèi)存。
二級緩存測試
@Test public void testCache2() throws Exception { SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); SqlSession sqlSession3 = sqlSessionFactory.openSession(); // 創(chuàng)建代理對象 UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); // 第一次發(fā)起請求,查詢id為1的用戶 User user1 = userMapper1.findUserById(1); System.out.println(user1); //這里執(zhí)行關(guān)閉操作,將sqlsession中的數(shù)據(jù)寫到二級緩存區(qū)域 sqlSession1.close(); //使用sqlSession3執(zhí)行commit()操作 UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class); User user = userMapper3.findUserById(1); user.setUsername("張明明"); userMapper3.updateUser(user); //執(zhí)行提交,清空UserMapper下邊的二級緩存 sqlSession3.commit(); sqlSession3.close(); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); // 第二次發(fā)起請求,查詢id為1的用戶 User user2 = userMapper2.findUserById(1); System.out.println(user2); sqlSession2.close(); }
useCache配置
在statement中設(shè)置useCache=false可以禁用當前select語句的二級緩存,即每次查詢都會發(fā)出sql去查詢,默認情況是true,即該sql使用二級緩存。
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
總結(jié):針對每次查詢都需要最新的數(shù)據(jù)sql,要設(shè)置成useCache=false,禁用二級緩存。
刷新緩存
就是清空緩存
在mapper的同一個namespace中,如果有其它insert、update、delete操作數(shù)據(jù)后需要刷新緩存,如果不執(zhí)行刷新緩存會出現(xiàn)臟讀。
設(shè)置statement配置中的flushCache=”true” 屬性,默認情況下為true即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數(shù)據(jù)庫表中的查詢數(shù)據(jù)會出現(xiàn)臟讀。
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
總結(jié):一般下執(zhí)行完commit操作都需要刷新緩存,flushCache=true表示刷新緩存,這樣可以避免數(shù)據(jù)庫臟讀。
mybatis整合ehcache
ehcache是一個分布式緩存框架。
分布緩存
我們系統(tǒng)為了提高系統(tǒng)并發(fā),性能、一般對系統(tǒng)進行分布式部署(集群部署方式)
不使用分布緩存,緩存的數(shù)據(jù)在各各服務(wù)單獨存儲,不方便系統(tǒng) 開發(fā)。所以要使用分布式緩存對緩存數(shù)據(jù)進行集中管理。
mybatis無法實現(xiàn)分布式緩存,需要和其它分布式緩存框架進行整合。
整合ehcache方法(掌握)
mybatis提供了一個cache接口,如果要實現(xiàn)自己的緩存邏輯,實現(xiàn)cache接口開發(fā)即可。
mybatis和ehcache整合,mybatis和ehcache整合包中提供了一個cache接口的實現(xiàn)類。
mybatis默認實現(xiàn)cache類是:
加入ehcache包
整合ehcache
配置mapper中cache中的type為ehcache對cache接口的實現(xiàn)類型。
加入ehcache的配置文件(在classpath下配置ehcache.xml)
<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:指定數(shù)據(jù)在磁盤中的存儲位置。
defaultCache:當借助CacheManager.add(“demoCache”)創(chuàng)建Cache時,EhCache便會采用<defalutCache/>指定的的管理策略
以下屬性是必須的:
maxElementsInMemory - 在內(nèi)存中緩存的element的最大數(shù)目
maxElementsOnDisk - 在磁盤上緩存的element的最大數(shù)目,若是0表示無窮大
eternal - 設(shè)定緩存的elements是否永遠不過期。如果為true,則緩存的數(shù)據(jù)始終有效,如果為false那么還要根據(jù)timeToIdleSeconds,timeToLiveSeconds判斷
overflowToDisk - 設(shè)定當內(nèi)存緩存溢出的時候是否將過期的element緩存到磁盤上
以下屬性是可選的:
timeToIdleSeconds - 當緩存在EhCache中的數(shù)據(jù)前后兩次訪問的時間超過timeToIdleSeconds的屬性取值時,這些數(shù)據(jù)便會刪除,默認值是0,也就是可閑置時間無窮大
timeToLiveSeconds - 緩存element的有效生命期,默認是0.,也就是element存活時間無窮大
diskSpoolBufferSizeMB 這個參數(shù)設(shè)置DiskStore(磁盤緩存)的緩存區(qū)大小.默認是30MB.每個Cache都應該有自己的一個緩沖區(qū).
diskPersistent - 在VM重啟的時候是否啟用磁盤保存EhCache中的數(shù)據(jù),默認是false。
diskExpiryThreadIntervalSeconds - 磁盤緩存的清理線程運行間隔,默認是120秒。每個120s,相應的線程會進行一次EhCache中數(shù)據(jù)的清理工作
memoryStoreEvictionPolicy - 當內(nèi)存緩存達到最大,有新的element加入的時候, 移除緩存中element的策略。默認是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出)
二級應用場景
對于訪問多的查詢請求且用戶對查詢結(jié)果實時性要求不高,此時可采用mybatis二級緩存技術(shù)降低數(shù)據(jù)庫訪問量,提高訪問速度,業(yè)務(wù)場景比如:耗時較高的統(tǒng)計分析sql、電話賬單查詢sql等。
實現(xiàn)方法如下:通過設(shè)置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據(jù)數(shù)據(jù)變化頻率設(shè)置緩存刷新間隔flushInterval,比如設(shè)置為30分鐘、60分鐘、24小時等,根據(jù)需求而定。
二級緩存局限性
mybatis二級緩存對細粒度的數(shù)據(jù)級別的緩存實現(xiàn)不好,比如如下需求:對商品信息進行緩存,由于商品信息查詢訪問量大,但是要求用戶每次都能查詢最新的商品信息,此時如果使用mybatis的二級緩存就無法實現(xiàn)當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因為mybaits的二級緩存區(qū)域以mapper為單位劃分,當一個商品信息變化會將所有商品信息的緩存數(shù)據(jù)全部清空。解決此類問題需要在業(yè)務(wù)層根據(jù)需求對數(shù)據(jù)有針對性緩存。
相關(guān)文章
Java?SpringBoot?獲取接口實現(xiàn)類匯總
這篇文章主要介紹了Java?SpringBoot?獲取接口實現(xiàn)類匯總,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09關(guān)于注解FeignClient的使用規(guī)范
這篇文章主要介紹了關(guān)于注解FeignClient的使用規(guī)范,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03Java實現(xiàn)自定義Excel數(shù)據(jù)排序的方法詳解
通常,我們可以在Excel中對指定列數(shù)據(jù)執(zhí)行升序或者降序排序,在需要自定義排序情況下,我們也可以自行根據(jù)排序需要編輯數(shù)據(jù)排列順序。本文將通過Java應用程序來實現(xiàn)如何自定義排序,需要的可以參考一下2022-09-09Spring中@Scheduled和HttpClient的連環(huán)坑
這篇文章主要給大家介紹了關(guān)于Spring中@Scheduled和HttpClient的連環(huán)坑,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2018-03-03