MyBatis(8)延遲加載&緩存


什么是延遲加載?
resultMap可以實現高級映射,association,collection具有延遲加載的功能。
 
當我們需要查詢某個信息的時候,再去查詢,達到按需查詢,就是延遲加載
 
可以大大提高數據庫的性能
 
那么我們代碼擼起來把:
延遲加載我們首先要在全局配置文件中開啟:
SQlMapConfig.xml:
     <!-- 延遲加載 -->
     <settings>
           <setting name="lazyLoadingEnabled" value="true"/>
           <setting name="aggressiveLazyLoading" value="false"/>
     </settings>
lazyLoadingEnabled:全局性設置懶加載。如果設為‘false’,則所有相關聯的都會被初始化加載。
aggressiveLazyLoading:當設置為‘true’的時候,懶加載的對象可能被任何懶屬性全部加載。否則,每個屬性都按需加載。
 
其次是OrderMapperCustomer.xml映射文件:
     <!-- 延遲加載 -->
           <resultMap type="com.MrChengs.po.Orders" id="slow">
                <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理解為:
                     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="com.MrChengs.po.User"
                select="com.MrChengs.mapper.UserMapper.findUserById" column="user_id"></association>
                
           </resultMap>
           <select id="findSlowing" resultMap="slow">
                SELECT * from orders
           </select>

 測試文件:

//延遲加載
           @Test
           public void testfindSlowing() throws Exception {
                SqlSession sqlSession = getSqlSessionFactory().openSession();
                
                //代理對象
                OrderMapperCustomer mapper = sqlSession.getMapper(OrderMapperCustomer.class);
                
                //測試findOrderUsers
                List<Orders> orders = mapper.findSlowing();
                
                for(Orders order : orders){
                   User user = order.getUser();
                     System.out.println(user);
                }
                
                sqlSession.close();
           }

 結果:

DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@8e24743]
DEBUG [main] - ==>  Preparing: SELECT * from orders
DEBUG [main] - ==> Parameters:
DEBUG [main] - <==      Total: 3
DEBUG [main] - ==>  Preparing: select * from user where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, username=王五, birthday=null, sex=2, address=null]
User [id=1, username=王五, birthday=null, sex=2, address=null]
DEBUG [main] - ==>  Preparing: select * from user where id=?
DEBUG [main] - ==> Parameters: 10(Integer)
DEBUG [main] - <==      Total: 1
User [id=10, username=張三, birthday=Thu Jul 10 00:00:00 CST 2014, sex=1, address=北京市]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@8e24743]

 

 
 
緩存:
用於減輕數據庫壓力,提高數據庫的性能
mybatis提供一級緩存&二級緩存
在操作數據庫時需構造sqlsession對象,在對象中有一個數據結構(HashMap)用於存儲數據
不同的sqlsession之間的緩存數據區域時互不影響的
 
一級緩存:是sqlsession級別的緩存
 
二級緩存:是mapper級別的緩存多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。
 
 
為什么需要緩存:
如果緩存中有數據就不需要從數據庫中獲取,提高系統性能。
 
 
 
 一級緩存:
 工作原理:

第一次查詢先去緩存中查詢,若沒有則取數據庫中查詢

 
如果sqlsession去執行commit操作(插入,刪除,更新),清空sqlsession中的一級緩存,使存儲區域得到最新的
信息,避免臟讀。
 
 
第二次在查詢第一次數據,首先在緩存中查找,找到了則不再去數據庫想查詢
 
 默認支持一級緩存,不需要手動去開啟。
 
 
 
測試代碼:
在testUserMapper.java
//一級緩存
     @Test
     public void testCahseFiret() throws Exception{
           SqlSession sqlSession = getSqlSessionFactory().openSession();
           UserMapper mapper = sqlSession.getMapper(UserMapper.class);
           //第一次查詢
           
           User user = mapper.findUserById(1);
           System.out.println(user);
           
           //第二次查詢
           User user1 = mapper.findUserById(1);
           System.out.println(user1);

            sqlsession.close();
     }
結果:
由此可見,查詢時,只查詢了一次
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@8e24743]
DEBUG [main] - ==>  Preparing: select * from user where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, username=王五, birthday=null, sex=2, address=null]
User [id=1, username=王五, birthday=null, sex=2, address=null]

 

 有清空操作:
@Test
     public void testCahseFiret() throws Exception{
           SqlSession sqlSession = getSqlSessionFactory().openSession();
           UserMapper mapper = sqlSession.getMapper(UserMapper.class);
           //第一次查詢
           
           User user = mapper.findUserById(1);
           System.out.println(user);
           
           //commit
           User adduser = new User();
           adduser.setUsername("Mr");
           adduser.setSex(1);
           mapper.addUser(user);
           //清空緩存
           sqlSession.commit();
           
           //第二次查詢
           User user1 = mapper.findUserById(1);
           System.out.println(user1);
           
           sqlSession.close();
     }

 結果: 

DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 149047107.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@8e24743]
DEBUG [main] - ==>  Preparing: select * from user where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, username=王五, birthday=null, sex=2, address=null]
DEBUG [main] - ==>  Preparing: insert into user(id,username,birthday,sex,address) value(?,?,?,?,?)
DEBUG [main] - ==> Parameters: 1(Integer), 王五(String), null, 2(Integer), null

 

一級緩存的應用:
正式開發,是將mybatis和spring進行整合,事物控制在service中
一個servic包括很多mapper方法調用
 
 
 
 
 
 二級緩存:
 
首先開啟mybatis的二級緩存。
sqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息會將查詢數據存儲到二級緩存中。
 
如果SqlSession3去執行相同 mapper下sql,執行commit提交,清空該 mapper下的二級緩存區域的數據。
 
sqlSession2去查詢用戶id為1的用戶信息,去緩存中找是否存在數據,如果存在直接從緩存中取出數據
二級緩存與一級緩存區別,二級緩存的范圍更大,多個sqlSession可以共享一個UserMapper的二級緩存區域。
 
UserMapper有一個二級緩存區域(按namespace分) ,其它mapper也有自己的二級緩存區域(按namespace分)。
 
每一個namespace的mapper都有一個二緩存區域,兩個mapper的namespace如果相同,這兩個mapper執行sql查詢到數據將存在相同 的二級緩存區域中。
 
 
 
 
 
開啟二級緩存:
 
    <settings>
           <!-- 開啟二級緩存 -->
           <setting name="cacheEnabled" value="true"/>
     </settings>

 cacheEnabled對在此配置文件下的所有cache 進行全局性開/關設置。

 
開啟mapper下的二級緩存:
UserMapper.xml
     <!-- 開啟本mapper的namespace下的二級緩存 -->
     <cache>
     
     </cache>

 實現pojo類實現里序列化接口:

public class User  implements Serializable{
......
}

 為了將存儲數據取出執行反序列化的操作,以內二級緩存存儲介質多種多種楊,不一定在內存

 
 測試類 :
//二級緩存
     @Test
     public void testCahseSecond() throws Exception{
           SqlSession sqlSession = getSqlSessionFactory().openSession();
           SqlSession sqlSession1 = getSqlSessionFactory().openSession();
           //第一次查詢
           UserMapper mapper = sqlSession.getMapper(UserMapper.class);
           User user = mapper.findUserById(1);
           System.out.println(user);
           //將執行關閉操作,將sqlsession寫道二級緩存
           sqlSession.close();
           
           //第二次查詢
           UserMapper mapper2 = sqlSession1.getMapper(UserMapper.class);
           User user1 = mapper2.findUserById(1);
           System.out.println(user1);
           sqlSession1.close();
     }

 結果:

 
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
DEBUG [main] - ==>  Preparing: select * from user where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, username=王五, birthday=null, sex=2, address=null]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
DEBUG [main] - Returned connection 428910174 to pool.
DEBUG [main] - Cache Hit Ratio [com.MrChengs.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 1873859565.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6fb0d3ed]
DEBUG [main] - ==>  Preparing: select * from user where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, username=王五, birthday=null, sex=2, address=null]

 

 
 
一些簡單的參數配置:
 
1.
useCache:為fasle時,禁用緩存
<select id="" useCache="true"></select>
針對每次查詢都需要最新數據的sql
 
2.
flushCache:刷新緩存,實質就是清空緩存,刷尋緩存可以避免數據的臟讀
<select id="" flushCache="true"></select>
 
3.
flushInterval:刷新間隔,可以設置任意的毫秒數,代表一個何況i的時間段
<cache flushInterval="" />
 
 
 
 
mybatis整合ehcache
 
ehcache分布式的緩存
 
不使用分布緩存,緩存的數據在各各服務單獨存儲,不方便系統 開發。所以要使用分布式緩存對緩存數據進行集中管理。
mybatis無法實現分布式緩存,需要和其它分布式緩存框架進行整合。
 
 
整和方法:
mybatis提供了cache接口,如果要實現自己的緩存邏輯,實現cache接口即可
 
 在mybatis包里的cache類里面
 

 

 
 
 
二級緩存應用場景:
對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可采用mybatis二級緩存技術降低數據庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。
實現方法如下:通過設置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,比如設置為30分鍾、60分鍾、24小時等,根據需求而定。
 
 
 
二級緩存的局限性:
mybatis二級緩存對細粒度的數據級別的緩存實現不好,比如如下需求:對商品信息進行緩存,由於商品信息查詢訪問量大,但是要求用戶每次都能查詢最新的商品信息,此時如果使用mybatis的二級緩存就無法實現當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因為mybaits的二級緩存區域以mapper為單位划分,當一個商品信息變化會將所有商品信息的緩存數據全部清空。解決此類問題需要在業務層根據需求對數據有針對性緩存。
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM