MyBatis implementation of a custom level two cache, the simplest way to implement a Org.apache.ibatis.cache.Cache interface can be
Then it is used in the SQL XML file, <cache eviction= "LRU" type= "Com.mark.demo.shiro.mybatis.cache.MyBatisRedisCache" ></ Cache>
This simple implementation has an update operation that expires the query cache in the same configuration file, but the cross-configuration file is not handled well.
In order to handle cross-profile update expiration, you need to implement Rediscachingexecutor implements yourself interceptor
Paste the following code:
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) * September 8, 2017 * */public class Mybatisrediscache implements cache,serializable{public static fi
NAL 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 ("cache has no initialization id"
);
} this.id = ID;
} @Override Public String getId () {return this.id;
} @Override public int getsize () {return Jedisutils.getmaplen (Mybatis_cache_prefix); } @Override Public 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);
} @Override public 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)); } @Override public Object Removeobject (object key) {Object ret=jedisutils.getmapfiled (Mybatis_cache_prefix, Objec
Tutils.serialize (key));
Jedisutils.removemapfield (Mybatis_cache_prefix, Objectutils.serialize (key));
return ret;
} @Override public void Clear () {Jedisutils.del (mybatis_cache_prefix);
} @Override Public 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) * September 8, 2017 * * * @Intercepts (value = {@Signature (args = {Mappedstatement.class, object.class, Rowbounds.class,resulthandler.class}, Method = "Query", type = Executor.class), @Signature (args = {Mappedstatement.clas S, 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 Processque
RY (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 have been committed,rollbacked,or closed, * session buffer query Cachekeys and Updat
e Statement collections should be cleared. * * When Sqlsession executes the commit (), rollback (), close () method, the CacheKey collection generated by the session-level query statement and the set of updated statements to be executed should be emptied */private synch
ronized void Clearsessiondata () {updatestatementoncommit.clear (); }/** * Refresh the session cache,there is things has 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 plug-in configuration information load point * properties have "dependency" property to indicate configuration cache dependency configuration information, read file, initialize Enhancedcachemanager */ public void SetProperties (properties properties) {if (!cachingmanager.isinitialized ()) {Cachingmanager.initia
Lize (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 Whether the level two cache is turned on, i.e. <setting name= "cacheenabled" value= "true"/>
* @return *
/public
Boolean iscacheenabled ();
/**
*
* Initialize the cache manager, should only be called once;
*
* @param
Properties * property contains at least two attributes:
* Dependency : This value indicates the location of the cache dependent configuration file
* cacheenbled : "true" or "false", the configuration must be consistent with the value of <setting name= "cacheenabled" >
* * Public
void Initialize (properties properties);
/**
* The core of the entire plugin, according to the specified Statementid update the level two cache associated with it
* The incoming Statementid collection is the statementid corresponding to the session-level UPDATE statement,
* Enhancedcachingmanager will check the appropriate statementid to see if the dependencies are configured, and if so, empty the query cache for Statementid in the dependency relationship
* @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{//each Statementid update dependent Statementid collection Private MAP&L T
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> R
Elatedstatements = Observers.get (observable); for (String statementid:relatedstatements) {Jedisutils.removemapfield (Mybatisrediscache.mybatis_cache_prefix, stat
Ementid);
}}} 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 ();
}}//cacheenabled String 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>
<!-- Refresh role Cache After role authorization--
<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> <!--global Parameters--<settings> <!--enable or disable caching for the global mapper. --<setting name= "cacheenabled" value= "true"/> <!--globally enables or disables lazy loading. When disabled, all associated objects are loaded immediately. --<setting name= "lazyloadingenabled" value= "false"/> <!--when enabled, an object with lazy loading properties will fully load any property when called. Otherwise, each property will be loaded as needed. --<setting name= "aggressivelazyloading" value= "true"/> <!--whether to allow single SQL to return multiple datasets (depending on driver compatibility) Default:true--<setting name= "multipleresultsetsenabled" value= "true"/> <!--whether a column alias can be used (depending on For driver compatibility) Default:true---<setting name= "Usecolumnlabel" value= "true"/> <!--allow JDBC to generate primary keys. Drive support is required. If set to true, this setting enforces the generated primary key, and some drives are incompatible but can still be executed. Default:false--<setting name= "Usegeneratedkeys" ValUe= "false"/> <!--specifies how MyBatis automatically maps the columns of the Data base table NONE: insinuate Partial: Partial full: All---<setting name= "a Utomappingbehavior "value=" PARTIAL "/> <!--This is the default execution type (simple: easy; reuse: The executor may reuse prepared statements statements; BA TCH: Executor can repeat statement and batch update)--<setting name= "Defaultexecutortype" value= "Reuse"/> <!--using Hump naming method Change the field. --<setting name= "Mapunderscoretocamelcase" value= "true"/> <!--set Local cache scope session: There will be data sharing s
Tatement: statement scope (so there is no sharing of data) Defalut:session-<setting name= "Localcachescope" value= "STATEMENT"/> <!--set But the JDBC type is empty, some drivers want to specify a value, Default:other, insert null value without specifying type--<setting name= "Jdbctypefornull" value= " NULL "/> </settings> <typeAliases> <package name=" com.mark.demo.shiro.entity "/> </t Ypealiases> <!--plug-in configuration-<plugins> <plugin interceptor= "COM.MARK.DEMO.SHIRO.MYBATIS.P AginationinterceptoR "/> <plugin interceptor=" Com.mark.demo.shiro.mybatis.cache.RedisCachingExecutor "> <proper Ty Name= "dependency" value= "Dependencys.xml"/> <property name= "cacheenabled" value= "true"/> &
Lt;/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"/> <res Ult 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&
Gt
</mapper>
Demo Address: Https://github.com/13567436138/shiro-demo.git