缓存机制,也是基于 Key-Value 的方式,确定了 Key 的来龙去脉能很好的认识缓存的生存周期。
从配置文件解析说起:
parser.addNodelet("/sqlMap/cacheModel" , new Nodelet() {
public void process(Node node) throws Exception ... {
Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
String id = state.applyNamespace(attributes.getProperty("id" ));
String type = attributes.getProperty("type" );
String readOnlyAttr = attributes.getProperty("readOnly" );
Boolean readOnly = readOnlyAttr == null || readOnlyAttr.length() <= 0 ? null : new Boolean("true" .equals(readOnlyAttr));
String serializeAttr = attributes.getProperty("serialize" );
Boolean serialize = serializeAttr == null || serializeAttr.length() <= 0 ? null : new Boolean("true" .equals(serializeAttr));
type = state.getConfig().getTypeHandlerFactory().resolveAlias(type);
Class clazz = Resources.classForName(type);
if (readOnly == null ) ... {
readOnly = Boolean.TRUE;
}
if (serialize == null ) ... {
serialize = Boolean.FALSE;
}
CacheModelConfig cacheConfig = state.getConfig().newCacheModelConfig(id, (CacheController) Resources.instantiate(clazz), readOnly.booleanValue(), serialize.booleanValue());
state.setCacheConfig(cacheConfig);
}
});
上面红色的代码很关键,它是 Statement 中,对应的Cache 的Key 就是它。返回的对象:CacheModelConfig,对应的部分代码如下:
0 1 private ErrorContext errorContext;
0 2 private CacheModel cacheModel;
0 3
0 4 CacheModelConfig(SqlMapConfiguration config, String id, CacheController controller, boolean readOnly, boolean serialize) ... {
0 5 this .errorContext = config.getErrorContext();
0 6 this .cacheModel = new CacheModel();
0 7 SqlMapClientImpl client = config.getClient();
0 8 errorContext.setActivity("building a cache model" );
0 9 cacheModel.setReadOnly(readOnly);
10 cacheModel.setSerialize(serialize);
11 errorContext.setObjectId(id + " cache model" );
12 errorContext.setMoreInfo("Check the cache model type." );
13 cacheModel.setId(id);
14 cacheModel.setResource(errorContext.getResource());
15 try ... {
16 cacheModel.setCacheController(controller);
17 } catch (Exception e) ... {
18 throw new RuntimeException("Error setting Cache Controller Class. Cause: " + e, e);
19 }
20 errorContext.setMoreInfo("Check the cache model configuration." );
21 if (client.getDelegate().isCacheModelsEnabled()) ... {
22 client.getDelegate().addCacheModel(cacheModel);
23 }
24 errorContext.setMoreInfo(null );
25 errorContext.setObjectId(null );
26 }
注意红色的代码,再来看看 CacheModel,如下:
1 public class CacheModel implements ExecuteListener
ExecuteListener 是一个监听器,它在 Update、insert,delete 操作执行后,触发些事件:源头如下:
0 1 com.ibatis.sqlmap.engine.mapping.statement.MappedStatement
0 2 public int executeUpdate(StatementScope statementScope, Transaction trans, Object parameterObject)
0 3 throws SQLException ... {
0 4 ErrorContext errorContext = statementScope.getErrorContext();
0 5 errorContext.setActivity("preparing the mapped statement for execution" );
0 6 errorContext.setObjectId(this .getId());
0 7 errorContext.setResource(this .getResource());
0 8
0 9 statementScope.getSession().setCommitRequired(true );
10
11 try ... {
12 parameterObject = validateParameter(parameterObject);
13
14 Sql sql = getSql();
15
16 errorContext.setMoreInfo("Check the parameter map." );
17 ParameterMap parameterMap = sql.getParameterMap(statementScope, parameterObject);
18
19 errorContext.setMoreInfo("Check the result map." );
20 ResultMap resultMap = sql.getResultMap(statementScope, parameterObject);
21
22 statementScope.setResultMap(resultMap);
23 statementScope.setParameterMap(parameterMap);
24
25 int rows = 0;
26
27 errorContext.setMoreInfo("Check the parameter map." );
28 Object[] parameters = parameterMap.getParameterObjectValues(statementScope, parameterObject);
29
30 errorContext.setMoreInfo("Check the SQL statement." );
31 String sqlString = sql.getSql(statementScope, parameterObject);
32
33 errorContext.setActivity("executing mapped statement" );
34 errorContext.setMoreInfo("Check the statement or the result map." );
35 rows = sqlExecuteUpdate(statementScope, trans.getConnection(), sqlString, parameters);
36
37 errorContext.setMoreInfo("Check the output parameters." );
38 if (parameterObject != null ) ... {
39 postProcessParameterObject(statementScope, parameterObject, parameters);
40 }
41
42 errorContext.reset();
43 sql.cleanup(statementScope);
44 notifyListeners();
45 return rows;
46 } catch (SQLException e) ... {
47 errorContext.setCause(e);
48 throw new NestedSQLException(errorContext.toString(), e.getSQLState(), e.getErrorCode(), e);
49 } catch (Exception e) ... {
50 errorContext.setCause(e);
51 throw new NestedSQLException(errorContext.toString(), e);
52 }
53 }
54
55 public void notifyListeners() ... {
56 for (int i = 0, n = executeListeners.size(); i < n; i++) ... {
57 ((ExecuteListener) executeListeners.get(i)).onExecuteStatement(this );
58 }
59 }
再看 CacheModel 是如何处理的:
0 1 public void onExecuteStatement(MappedStatement statement) {
0 2 flush();
0 3 }
0 4
0 5 public void flush() ... {
0 6 synchronized (this ) ... {
0 7 controller.flush(this );
0 8 lastFlush = System.currentTimeMillis();
0 9 if ( log.isDebugEnabled() ) ... {
10 log("flushed" , false , null );
11 }
12 }
13 }
controller 是实现以下接口的类
public interface CacheController
实现以下几种:
FifoCacheController
LruCacheController
MemoryCacheController
OSCacheController
再看看具体的实现,如MemoryCacheController的:
private Map cache = Collections.synchronizedMap(new HashMap());
1 public void flush(CacheModel cacheModel) {
2 cache.clear();
3 }
其它的几种实现,也基本大致如此,不在具体贴代码了。
从上面的代码分析可以看出 IBatis 的缓存的更新机制大概如下:
执行 update,insert,delete 等操作时,触发对应的事件,注册的事件响应此操作,根据XML配置的缓存,对其数据做清空操作,也就是这个缓存中的数据全部清空,而不是
清空某一个 Key对应的值,即如我更新了一个 id= 5 的数据,则这个缓存中的数据全部将清空。这样倒很简单也很彻底,由此带来的问题就很多了,如果你的 update 等相关的操作太频繁了,这里的缓存则失去意义,且加大了系统本身的开销。因此,我认为对于更新不是很频繁的数据可以用 IBatis 自身的缓存机制,如果是很频繁的数据就不要使用 IBatis 自身的缓存。举例说明:
< cacheModel id ="product-cache" imlementation ="LRU" >
< flushInterval hours ="24" />
< flushOnExecute statement ="insertProduct" />
< flushOnExecute statement ="updateProduct" />
< flushOnExecute statement ="deleteProduct" />
< property name ="size" value ="1000" />
</ cacheModel >
< statement id ="getProductList" parameterClass ="int" cacheModel ="product-cache" >
select * from PRODUCT where PRD_CAT_ID = #value#
</ statement >
如果你配置了以上操作,如果做了 insert_product,updateProduct,deleteProduct 中的任何一个操作,都将清空缓存 product-cache 中的所有数据,也就是 getProductList 方法将执行数据库操作,不能从缓存中获取数据。
最后一句,大家不要对 IBatis 的缓存抱太大的希望,虽然我们的系统中使用的是 IBatis