Mybats Redis level Two cache extension

Source: Internet
Author: User
Tags commit redis rollback throwable

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

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.