mybats redis二級緩衝擴充

來源:互聯網
上載者:User

mybatis實現自訂二級緩衝,最簡單的做法,實現一個org.apache.ibatis.cache.Cache介面就可以了

然後就是在sql xml檔案裡使用,<cache eviction="LRU" type="com.mark.demo.shiro.mybatis.cache.MyBatisRedisCache" ></cache>

這種簡單實現有更新操作時會到期同一個設定檔裡的查詢快取,但是跨設定檔就不好處理了。

為了處理跨設定檔更新到期,需要自己實現RedisCachingExecutor implements Interceptor 

下面貼代碼:

package com.mark.demo.shiro.mybatis.cache;import java.io.Serializable;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;import org.apache.ibatis.cache.Cache;import org.apache.ibatis.cache.CacheKey;import org.apache.ibatis.cache.CacheKey;import com.mark.demo.shiro.utils.JedisUtils;import com.mark.demo.shiro.utils.ObjectUtils;/**hxp(hxpwangyi@126.com)*2017年9月8日**/public class MyBatisRedisCache implements Cache,Serializable{public static final String mybatis_cache_prefix="mybatis_cache";private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private String id;public MyBatisRedisCache(final String id) {    if (id == null) {        throw new IllegalArgumentException("緩衝沒有初始化id");    }    this.id = id;}@Overridepublic String getId() {    return this.id;}@Overridepublic int getSize() {    return JedisUtils.getMapLen(mybatis_cache_prefix);}@Overridepublic void putObject(Object key, Object value) {CacheKey cacheKey=(CacheKey)key;String [] keyAry=cacheKey.toString().split(":");String myKey=keyAry[2];    JedisUtils.setMapField(mybatis_cache_prefix, ObjectUtils.serialize(myKey), value);}@Overridepublic Object getObject(Object key) {CacheKey cacheKey=(CacheKey)key;String [] keyAry=cacheKey.toString().split(":");String myKey=keyAry[2];    return JedisUtils.getMapFiled(mybatis_cache_prefix, ObjectUtils.serialize(myKey));    }@Overridepublic Object removeObject(Object key) {    Object ret=JedisUtils.getMapFiled(mybatis_cache_prefix, ObjectUtils.serialize(key));JedisUtils.removeMapField(mybatis_cache_prefix, ObjectUtils.serialize(key));return ret;}@Overridepublic void clear() {    JedisUtils.del(mybatis_cache_prefix);}@Overridepublic ReadWriteLock getReadWriteLock() {    return readWriteLock;}}
package com.mark.demo.shiro.mybatis.cache;import java.util.HashSet;import java.util.Properties;import java.util.Set;import org.apache.ibatis.cache.CacheKey;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.plugin.Intercepts;import org.apache.ibatis.plugin.Invocation;import org.apache.ibatis.plugin.Plugin;import org.apache.ibatis.plugin.Signature;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;/**hxp(hxpwangyi@126.com)*2017年9月8日**/@Intercepts(value = {@Signature(args = {MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class}, method = "query", type = Executor.class),@Signature(args = {MappedStatement.class, Object.class}, method = "update", type = Executor.class),@Signature(args = {boolean.class}, method = "commit", type = Executor.class),@Signature(args = {boolean.class}, method = "rollback", type = Executor.class),@Signature(args = {boolean.class}, method = "close", type = Executor.class)})public  class RedisCachingExecutor implements Interceptor {private Set<String>   updateStatementOnCommit = new HashSet<String>();RedisCachingManager cachingManager = RedisCachingManagerImpl.getInstance();public Object intercept(Invocation invocation) throws Throwable {String name = invocation.getMethod().getName();Object result =null;if("query".equals(name)){result = this.processQuery(invocation);}else if("update".equals(name)){result = this.processUpdate(invocation);}else if("commit".equals(name)){result = this.processCommit(invocation);}else if("rollback".equals(name)){result = this.processRollback(invocation);}else if("close".equals(name)){result = this.processClose(invocation);}return result;}public Object plugin(Object target) {return Plugin.wrap(target, this);}/** * when executing a query operation * 1. record this statement's id and it's corresponding Cache Object into Global Caching Manager; * 2. record this statement's id and   * @param invocation * @return * @throws Throwable */protected Object processQuery(Invocation invocation) throws Throwable {Object result = invocation.proceed();return result;}protected Object processUpdate(Invocation invocation) throws Throwable {Object result = invocation.proceed();MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];updateStatementOnCommit.add(mappedStatement.getId());return result;}protected Object processCommit(Invocation invocation) throws Throwable {Object result  = invocation.proceed();refreshCache();return result;}protected Object processRollback(Invocation invocation) throws Throwable {    Object result = invocation.proceed();    clearSessionData();return result;}protected Object processClose(Invocation invocation) throws Throwable {Object result = invocation.proceed();boolean forceRollback = (Boolean) invocation.getArgs()[0];if(forceRollback){clearSessionData();}else{refreshCache();}return result;}/** * when the sqlSession has been committed,rollbacked,or closed, * session buffer query CacheKeys and update Statement collections should be cleared. *  * 當SqlSession 執行了commit()、rollback()、close()方法, * Session層級的查詢語句產生的CacheKey集合以及  執行的更新語句集合應該被清空 */private synchronized void clearSessionData(){    updateStatementOnCommit.clear();}/** * refresh the session cache,there are two things have to do: * 1. add this session scope query logs to global cache Manager  * 2. clear the related caches according to the update statements as configured in "dependency" file * 3. clear the session data */private synchronized void refreshCache(){cachingManager.clearRelatedCaches(updateStatementOnCommit);clearSessionData();}/** *  *  * Executor外掛程式配置資訊載入點 * properties中有 "dependency" 屬性來指示 配置的緩衝依賴配置資訊,讀取檔案,初始化EnhancedCacheManager */public void setProperties(Properties properties) {if(!cachingManager.isInitialized()){cachingManager.initialize(properties);}}}

package com.mark.demo.shiro.mybatis.cache;import java.util.Properties;import java.util.Set;import org.apache.ibatis.cache.Cache;public interface RedisCachingManager {public boolean isInitialized();/** * MyBatis是否開啟了二級緩衝,即 <setting name="cacheEnabled" value="true"/>  * @return */public boolean isCacheEnabled();/** *  * 初始化 緩衝管理器,應該只被調用一次; *  * @param properties * properties 中至少包含兩個屬性: * dependency : 該值表示著緩衝依賴設定檔的位置 * cacheEnbled: "true" or "false",該配置必須要與<setting name="cacheEnabled">的值保持一致 */public void initialize(Properties properties);/** * 整個外掛程式的核心,根據指定的statementId更新與之關聯的二級緩衝 * 傳入的StatementId集合是由會話層級的update語句對應的StatementId, * EnhancedCachingManager將通過查詢相應的StatementId去查看是否配置了依賴關係,如果有,則將依賴關係中的statementId的查詢快取全部清空 * @param set */public void clearRelatedCaches(Set<String> set);}

package com.mark.demo.shiro.mybatis.cache;import java.io.IOException;import java.io.InputStream;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Properties;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import org.apache.ibatis.cache.Cache;import org.apache.ibatis.io.Resources;import org.apache.ibatis.parsing.XNode;import org.apache.ibatis.parsing.XPathParser;import com.mark.demo.shiro.utils.JedisUtils;import com.mark.demo.shiro.utils.PropertiesLoader;public class RedisCachingManagerImpl implements RedisCachingManager{//每一個statementId 更新依賴的statementId集合private Map<String,Set<String>> observers=new ConcurrentHashMap<String,Set<String>>();private boolean initialized = false;private boolean cacheEnabled = false;private volatile static RedisCachingManagerImpl enhancedCacheManager;private RedisCachingManagerImpl(){}public static RedisCachingManagerImpl getInstance(){if(enhancedCacheManager==null){synchronized (RedisCachingManagerImpl.class) {if(enhancedCacheManager==null){enhancedCacheManager=new RedisCachingManagerImpl();}}}return enhancedCacheManager;}public void clearRelatedCaches(final Set<String> set) {for(String observable:set){Set<String> relatedStatements = observers.get(observable);for(String statementId:relatedStatements){JedisUtils.removeMapField(MyBatisRedisCache.mybatis_cache_prefix, statementId);}}}public boolean isInitialized() {return initialized;}public void initialize(Properties properties){PropertiesLoader loader=new PropertiesLoader("mybatis.properties");String dependency = loader.getProperty("dependencys");if(!("".equals(dependency) || dependency==null)){InputStream inputStream;try {inputStream = Resources.getResourceAsStream(dependency);XPathParser parser = new XPathParser(inputStream);List<XNode> statements = parser.evalNodes("/dependencies/statements/statement");for(XNode node :statements){Set<String> temp = new HashSet<String>();List<XNode> obs = node.evalNodes("observer");for(XNode observer:obs){temp.add(observer.getStringAttribute("id"));}this.observers.put(node.getStringAttribute("id"), temp);}initialized = true;} catch (IOException e) {e.printStackTrace();}}//cacheEnabledString cacheEnabled = properties.getProperty("cacheEnabled", "true");if("true".equals(cacheEnabled)){this.cacheEnabled = true;}}public boolean isCacheEnabled() {return cacheEnabled;}}

<?xml version="1.0" encoding="UTF-8"?><dependencies>    <statements>        <!-- 角色授權後重新整理角色緩衝 -->        <statement id="com.mark.demo.shiro.mapper.MenuMapper.updateMenu">        <observer id="com.mark.demo.shiro.mapper.UserMapper.getMenuTopLever"/>        </statement>    </statements></dependencies>


<?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>    <!-- 全域參數 -->    <settings>        <!-- 使全域的映射器啟用或禁用緩衝。 -->        <setting name="cacheEnabled" value="true"/>        <!-- 全域啟用或禁用消極式載入。當禁用時,所有關聯對象都會即時載入。 -->        <setting name="lazyLoadingEnabled" value="false"/>        <!-- 當啟用時,有消極式載入屬性的對象在被調用時將會完全載入任意屬性。否則,每種屬性將會按需要載入。 -->        <setting name="aggressiveLazyLoading" value="true"/>        <!-- 是否允許單條sql 返回多個資料集  (取決於驅動的相容性) default:true -->        <setting name="multipleResultSetsEnabled" value="true"/>        <!-- 是否可以使用列的別名 (取決於驅動的相容性) default:true -->        <setting name="useColumnLabel" value="true"/>        <!-- 允許JDBC 產生主鍵。需要磁碟機支援。如果設為了true,這個設定將強制使用被產生的主鍵,有一些磁碟機不相容不過仍然可以執行。  default:false  -->        <setting name="useGeneratedKeys" value="false"/>        <!-- 指定 MyBatis 如何自動對應 資料基表的列 NONE:不隱射 PARTIAL:部分  FULL:全部  -->        <setting name="autoMappingBehavior" value="PARTIAL"/>        <!-- 這是預設的執行類型  (SIMPLE: 簡單; REUSE: 執行器可能重複使用prepared statements語句;BATCH: 執行器可以重複執行語句和批次更新)  -->        <setting name="defaultExecutorType" value="REUSE"/>        <!-- 使用駝峰命名法轉換欄位。 -->        <setting name="mapUnderscoreToCamelCase" value="true"/>        <!-- 設定本機快取範圍 session:就會有資料的共用  statement:語句範圍 (這樣就不會有資料的共用 ) defalut:session -->        <setting name="localCacheScope" value="STATEMENT"/>        <!-- 設定但JDBC類型為空白時,某些驅動程式 要指定值,default:OTHER,插入空值時不需要指定類型 -->        <setting name="jdbcTypeForNull" value="NULL"/>    </settings><typeAliases>     <package name="com.mark.demo.shiro.entity" /></typeAliases>    <!-- 外掛程式配置 -->    <plugins>        <plugin interceptor="com.mark.demo.shiro.mybatis.PaginationInterceptor"/>        <plugin interceptor="com.mark.demo.shiro.mybatis.cache.RedisCachingExecutor">            <property name="dependency" value="dependencys.xml"/>            <property name="cacheEnabled" value="true"/>        </plugin>    </plugins></configuration>
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mark.demo.shiro.mapper.UserMapper"><cache eviction="LRU" type="com.mark.demo.shiro.mybatis.cache.MyBatisRedisCache" ></cache>    <resultMap type="User" id="userMap">    <result column="userId" property="id" />    <result column="userName" property="userName" />    <result column="password" property="password" />    <result column="age" property="age" />    <result column="sex" property="sex" />    <result column="phone" property="phone" />    </resultMap>        <select id="getUserByUserName" parameterType="String" resultMap="userMap">    select * from user where userName=#{userName}    </select>        <resultMap type="Menu" id="menuMap">    <result column="menuId" property="id"/>    <result column="menuName" property="menuName"/>    <result column="menuDesc" property="menuDesc"/>    <result column="link" property="link"/>    <result column="icon" property="icon"/>    </resultMap>        <select id="getMenuTopLever"  resultMap="menuMap" useCache="true">    select * from menu where pid=-1    </select></mapper>

demo地址:https://github.com/13567436138/shiro-demo.git

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.