In most cases, the use of cache is a huge performance improvement for efficiency improvement. However, in the context of this performance improvement, whether the cache data is valid or not can support notification updates, whether the cache supports cluster distribution must be considered during system design.
As you know, ibatis provides excellent support for Oscache. It is satisfactory to update and refresh the cache and support clusters. During normal development, some non-SQL query cache needs will also be met. For example, some login users must be able to cache the information in a timely manner after being logged on to the system. This is a user-level cache, the cache range is not session level. If the database-related data changes, the cached data needs to be notified and updated. To solve this problem, we can use two methods:
1. query the cached data using SQL statements and configure a refresh policy on the Oscache Cache Policy;
2. directly use the cache refresh policy in Oscache to directly store the memory object data;
Method 1 is implemented based on ibatis's own mechanism for SQL caching, but such implementation is based on the SQL form. If the cached data is not good enough to pass the SQL query results, there are still some restrictions. In order to get rid of the SQL cache, find a direct cache and get the relevant data change update method, we need to adopt method 2.
In method 2, Oscache cache in ibatis can be directly used, and data change notification can be adopted. My principle is very simple. In a JVM, I directly find the Oscache reference of ibatis to implement custom data caching. Based on this idea, let's take a look at the Oscache reference code com. ibatis. sqlmap. Engine. cache. Oscache. oscachecontroller in ibatis. We can see the red part of the oscachecontroller code below. The Oscache is applied as a static private variable or final, Which is better. We will not modify its reference anyway, and this object has only one copy in JVM. You should pay attention to the flush code. Through the debug practice, we can conclude that it is the cache clearing method that will be called when the cache update policy is effective. This is very important. Based on this mechanism, it is a key part of implementing custom caching.
/* * Copyright 2004 Clinton Begin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.ibatis.sqlmap.engine.cache.oscache; import com.ibatis.sqlmap.engine.cache.CacheController;import com.ibatis.sqlmap.engine.cache.CacheModel;import com.opensymphony.oscache.base.NeedsRefreshException;import com.opensymphony.oscache.general.GeneralCacheAdministrator; import java.util.Properties; /** * Cache implementation for using OSCache with iBATIS */public class OSCacheController implements CacheController { private static final GeneralCacheAdministrator CACHE = new GeneralCacheAdministrator(); public void flush(CacheModel cacheModel) { CACHE.flushGroup(cacheModel.getId()); } public Object getObject(CacheModel cacheModel, Object key) { String keyString = key.toString(); try { int refreshPeriod = (int) (cacheModel.getFlushIntervalSeconds()); return CACHE.getFromCache(keyString, refreshPeriod); } catch (NeedsRefreshException e) { CACHE.cancelUpdate(keyString); return null; } } public Object removeObject(CacheModel cacheModel, Object key) { Object result; String keyString = key.toString(); try { int refreshPeriod = (int) (cacheModel.getFlushIntervalSeconds()); Object value = CACHE.getFromCache(keyString, refreshPeriod); if (value != null) { CACHE.flushEntry(keyString); } result = value; } catch (NeedsRefreshException e) { try { CACHE.flushEntry(keyString); } finally { CACHE.cancelUpdate(keyString); result = null; } } return result; } public void putObject(CacheModel cacheModel, Object key, Object object) { String keyString = key.toString(); CACHE.putInCache(keyString, object, new String[]{cacheModel.getId()}); } public void setProperties(Properties props) { } }
The next thing is very simple. Let's look at the code section of staitc and use the reflection mechanism to get the oscachecontroller in JVM. reference of cache member variables, even if it is private, can still be obtained under sun's legal specifications. :) After obtaining the reference, we also implemented the putobject, GetObject, and removeobject methods based on the official interface, but we did not use the ibatis official cachemodel to cache objects in these methods, makes the method simpler. Note that the putobject method we provide carries the cacheid parameter. this parameter is very important. According to the official cache update policy mentioned above, the flush method will be called when it takes effect, when caching custom data, we must specify the ID of the cache refresh policy.Because of the reflection mechanism, we get the oscachecontroller cache reference, and the cache is a static variable. The cached data and ibatis cache data use the same oacache, in addition, when it updates the cache policy, the Oscache object that I take will also be updated at the same time..
/*** Copyright (c) 2005 Ceno techonologies, Ltd. ** history: * 11-10-11 created by tiwen */package COM. zjhcsoft. biap; import Java. lang. reflect. field; import Org. slf4j. logger; import Org. slf4j. loggerfactory; import COM. ibatis. sqlmap. engine. cache. oscache. oscachecontroller; import COM. opensymphony. oscache. base. needsrefreshexception; import COM. opensymphony. oscache. general. generalcacheadministrat Or;/*** combined with ibatis cache collection help class ** @ author <a href = "mailto: tiwen@qq.com "> tiwen </a> * @ version 1.0 11-10-11 */public class ibatiscachehelper {Private Static logger = loggerfactory. getlogger (ibatiscachehelper. class); Private Static generalcacheadministrator cache = NULL;/*** by default, each cache is cleared once every hour */Private Static int flushintervalseconds = 1000*60*60; // Based on the reflection principle, obtain the static reference static {T of the ibatis object in JVM Ry {field = oscachecontroller. class. getdeclaredfield ("cache"); field. setaccessible (true); cache = (generalcacheadministrator) field. get (null); field. setaccessible (false);} catch (exception e) {logger. warn ("can't acquire ibatis cache! ", E) ;}} public static void putobject (string key, object content, string cacheid) {If (Cache! = NULL) {cache. putincache (Key, content, new string [] {cacheid});} else {logger. warn ("can't acquire ibatis cache, data can not put into cache") ;}} public static object GetObject (string key) {If (Cache! = NULL) {try {return cache. getfromcache (Key, flushintervalseconds);} catch (needsrefreshexception e) {cache. cancelupdate (key); return NULL ;}} else {logger. warn ("can't acquire ibatis cache, data can not put into cache"); return NULL ;}} public static void removeobject (string key) {If (Cache! = NULL) {string keystring = key. tostring (); try {object value = cache. getfromcache (keystring, flushintervalseconds); If (value! = NULL) {cache. flushentry (keystring) ;}} catch (needsrefreshexception e) {try {cache. flushentry (keystring);} finally {cache. cancelupdate (keystring) ;}} else {logger. warn ("can't acquire ibatis cache, data can not put into cache ");}}}
Next, let's take an example. Our biap integrated system management platform improves the unified application display framework and dynamically generates menus with user access permissions when users log on, the menu is an HTML character generated by logging on to the user permission information, which takes a long time to generate. During the login process, we have performed ibatis cache for all login-related queries. The update policy is as follows:
<sqlMap namespace="sysLogin"> <cacheModel id="operator-cache" type="OSCACHE"> <flushInterval hours="24"/> <flushOnExecute statement="insertSysResource"/> <flushOnExecute statement="updateResource"/> <flushOnExecute statement="deleteResource"/> <flushOnExecute statement="deleteUserDataAccess"/> <flushOnExecute statement="insertUserDataAccess"/> <flushOnExecute statement="insertRelRoleRes"/> <flushOnExecute statement="deleteRelRoleRes"/> <flushOnExecute statement="insertRelation"/> <flushOnExecute statement="deleteRelationByFromId"/> <flushOnExecute statement="deleteRelationByTargetId"/> <property name="size" value="1000"/> </cacheModel>
…………
In the logon code, modify the code of the Assembly menu. The cached key uses the fixed constant cache_menu + User Logon ID. In the green part, first check whether there is any processed menu data in the cache. If not, call the private menuprocess (operator) method to process the menu, and in the following red code section, place the processed menu data menu in the cache, and use the constant cache_id. The cache_id value isSyslogin. Operator-CacheYes, that is, the ID combination of sqlmap namespace + cachemodel in our update policy.
Protected string loginprocess (loginsessionobject operator) {menu = (string) ibatiscachehelper. getObject (cache_menu + operator. getuserrowid (). tostring (); // if no navigation data is found in the cache, process the IF (stringutil. isinvalid (menu) {// processing menu menuprocess (operator); // Processing System icon loginserv. systempicprocess (lstsytem); // resource permission operator. setenableresource (resrowid. append ("[[split]"). append (resource ). tostring (); // after processing, put the processed data The ibatis cache does not need to process ibatiscachehelper in the next time. putobject (cache_menu + operator. getuserrowid (). tostring (), menu, cache_id); ibatiscachehelper. putobject (cache_system_menu + operator. getuserrowid (). tostring (), operator. getlstsytem (), cache_id); ibatiscachehelper. putobject (cache_enable_res_ids + operator. getuserrowid (). tostring (), operator. getenableresourceids (), cache_id); ibatiscachehelper. putobject (CA Che_resources + operator. getuserrowid (). tostring (), operator. getenableresource (), cache_id);} else {// restore system menu operator. setlstsytem (list <basemenu>) ibatiscachehelper. getObject (cache_system_menu + operator. getuserrowid (). tostring (); lstsytem = Operator. getlstsytem (); // restore the operator that the user can access. setenableresourceids (set <long>) ibatiscachehelper. getObject (cache_enable_res_ids + operator. getuserrowid (). tostr Ing (); // restores the operator set of user permission information. setenableresource (string) ibatiscachehelper. getObject (cache_resources + operator. getuserrowid (). tostring ();} If (Session. get (constants. jxportalsyn. portalssologin )! = NULL &&! "". Equals (string) session. get (constants. jxportalsyn. portalssologin) {If (operator. getlstsytem () = NULL | Operator. getlstsytem (). size () = 0) {return "portalssoerror" ;}}// value the data access range lstuserdateaccess = Operator. getuserdataaccesses (); // operator. setuserdataaccesses (null); Return "Main ";}
Next, let's debug to see the effect. When I log on for the first time, the menu of the hacker is not retrieved from the cache to process data.
We save the processed data in the Oscache cache.
In the second login, we found the previous data cached for this user.
Let's modify the menu to check whether the cache update policy can be used to update our cache.
In debug mode, we can see that our syslogin. Operator-Cache update policy in generalcacheadministrator has been called.
The third login. Congratulations, our cache has been cleared by the update policy. We don't have to worry about the data in the cache being outdated.
Conclusion: We learned about the cache refresh mechanism and Cache Usage mechanism in ibatis by observing the official source code, and used a reasonable and standardized reflection technology to obtain the Oscache object used by ibatis in JVM, according to the ibatis refresh specification, the cache refresh policy is used for saving during cache, which enables you to customize the cache and dynamically update the cache. What is important is not your understanding of this function, but your understanding of the cache design philosophy.
Original article, if reproduced, please mark the author: Tian Wen csdn address: http://blog.csdn.net/tiwen818