最近在做执法系统重构功能的开发,发现Ibatis的缓存一个应用的问题,跟大家分享一下。
使用过Ibatis缓存的同事可能也会遇到过这个问题。
背景:
接过项目经理的一个案件转办的任务,功能大概是这样的,一个表单的新增、修改(新增、修改都只需要修改关联表的数据,主表数据是不需要用的)等,然后就提交审核,功能完成,提交给测试组。很简单吧。
第二天,MT跟我说:“怎么你的转办功能这样子的呀???都保存不了数据。”然后我又回来测了下,没发现问题,郁闷中……。再回来找找问题,后来发现原来是在不同的环境下体现出来的效果不同,在开发环境下是可以正常读到数据,但在IIS下就读不到数据。在网上找一下,没找到解决方法,怎么办,又郁闷中……。后来跑去问土华,土华给了我一个很有很好的提示:是Ibatis缓存的问题,具体问题出在哪里,再找找咯。然后我就跟着这个思路去找,找找找……。终于找到了,爽呀!真的是Ibatis缓存问题。
数据表设计:
看代码,了解下数据表的结构和数据读取
主表(CaseSummary)
<?xml version="1.0" encoding="utf-8" ?>
<sqlMap namespace="CaseSummary" xmlns="http://ibatis.apache.org/mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!--别名-->
<alias>
<typeAlias alias="CaseSummary" type="Comit.TE.Web.Domain.Cases.CaseSummary, Comit.TE.Web.Domain" />
</alias>
<!--缓存模型-->
<cacheModels>
<cacheModel id="CaseSummaryCache" implementation="MEMORY">
<flushInterval hours="24"/>
<flushOnExecute statement="CaseSummary.AddCaseSummary"/>
<flushOnExecute statement="CaseSummary.UpdateCaseSummary"/>
<flushOnExecute statement="CaseSummary.DeleteCaseSummary"/>
<flushOnExecute statement="CaseSummary.DeleteCaseSummarys"/>
<flushOnExecute statement="CaseSummary.UpdateCaseSummaryName"/>
<property name="Type" value="WEAK"/>
</cacheModel>
</cacheModels>
<!--字段映射-->
<resultMaps>
<resultMap id="CaseSummaryResult" class="CaseSummary">
<result property="Id" column="ID" />
<result property="Code" column="CODE" />
<result property="File" column="FILE" />
<result property="Name" column="NAME" />
<result property="Administration" column="ADMINISTRATION" />
<result property="Category" column="CATEGORY" />
<result property="Subject" column="SUBJECT" />
<result property="Items" column="ITEMS" />
<result property="Time" column="TIME" />
<result property="Address" column="ADDRESS" />
<result property="Income" column="INCOME" />
<result property="Created" column="CREATED" />
<result property="Creater" column="CREATER" />
<result property="Organization" column="ORGANIZATION" />
<result property="Amerce" column="AMERCE" />
<result property="Status" column="STATUS" />
<result property="Description" column="DESCRIPTION" />
<result property="Road" column="ROAD" />
<result property="PileStart" column="PILE_START" />
<result property="PileEnd" column="PILE_END" />
<result property="Propertys" column="ID" select="CaseProperty.GetPropertysBySummaryID" />
<result property="PunishType" column="PUNISH_TYPE" />
</resultMap>
</resultMaps>
<statements>
<!--获取多条-->
<select id="GetCaseSummarys" resultMap="CaseSummaryResult" parameterClass="CaseSummary" cacheModel="CaseSummaryCache">
select "ID", CODE, "FILE", "NAME", ADMINISTRATION, "CATEGORY", SUBJECT, ITEMS, "TIME", ADDRESS, INCOME, CREATED, CREATER, "ORGANIZATION",AMERCE, STATUS, DESCRIPTION, ROAD, PILE_START, PILE_END, PUNISH_TYPE
from T_CASE_SUMMARY
where 1=1
<include refid="WhrClauseEqual" />
</select>
</statements>
</sqlMap>
关联表(CaseProperty)
<?xml version="1.0" encoding="utf-8" ?>
<sqlMap namespace="CaseProperty" xmlns="http://ibatis.apache.org/mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!--别名-->
<alias>
<typeAlias alias="CaseProperty" type="Comit.TE.Web.Domain.Cases.CaseProperty, Comit.TE.Web.Domain" />
</alias>
<!--缓存模型-->
<cacheModels>
<cacheModel id="CasePropertyCache" implementation="MEMORY">
<flushInterval hours="24"/>
<flushOnExecute statement="CaseProperty.AddCaseProperty"/>
<flushOnExecute statement="CaseProperty.UpdateCaseProperty"/>
<flushOnExecute statement="CaseProperty.DeleteCaseProperty"/>
<flushOnExecute statement="CaseProperty.DeleteCasePropertys"/>
<flushOnExecute statement="CaseProperty.DeleteCasePropertyBySummary"/>
<property name="Type" value="WEAK"/>
</cacheModel>
</cacheModels>
<!--字段映射-->
<resultMaps>
<resultMap id="CasePropertyResult" class="CaseProperty">
<result property="Id" column="ID" />
<result property="Summary" column="SUMMARY" />
<result property="Name" column="NAME" />
<result property="Value" column="VALUE" />
<result property="Shift" column="SHIFT" />
</resultMap>
</resultMaps>
<statements>
<!--根据主表ID获取信息-->
<select id="GetPropertysBySummaryID" resultMap="CasePropertyResult" parameterClass="int" cacheModel="CasePropertyCache">
select ID, SUMMARY, NAME, VALUE, SHIFT
from T_CASE_PROPERTY
where SUMMARY=$SUMMARY$
</select>
</statements>
</sqlMap>
public ActionResult Edits(string code) { try {//读取(在IIS下读取数据是有问题的,只能读取到上一次的缓存数据,取不到最新的数据) caseSummary = CaseSummaryService.GetCaseSummarys(HttpContext, new CaseSummary() { Code = code }).FirstOrDefault(); } catch (Exception e) { HandleException(Response, e); } return Json(caseSummary, JsonRequestBehavior.AllowGet); } public ActionResult Edits([ModelBinder(typeof(JsonBinder<CaseProperty>))]List<CaseProperty> propertys) { try { CasePropertyService.UpdateCaseProperty(HttpContext, propertys); //对表单修改,只修改关联表(已经对property的数据进行更新了,但还是取不到数据) } catch (Exception e) { HandleException(Response, e); } return Json(true); }
问题就出在“只修改关联表”,这样一来Ibatis把propertys的物理数据表和Ibatis缓存同时更新。在代码执行“GetCaseSummarys“时,读到propertys的是缓存数据是并不是最新的。实际上,此时propertys的缓存数据是有两个版本,要拿到最新的数据有两种方法:
1.修改读取的方法,直接调用“Getpropertys”读取property表的数据;
2.修改更新的方法,先调用“UpdateCaseSummary”把主表的缓存版本更新,再执行“GetCaseSummarys”;
小结,一个小实验来证明得来,Ibatis的缓存在VS的调试环境下没起到缓存的做用,在IIS下程序就能真正起到数据缓存的作用,并且同一个数据对象可能会存在多个版本的缓存数据。如果有哪位同事用到Ibatis缓存机制来优化系统的性能,那就要留意缓存的问题。在VS调用下看到的效果并不是真实的效果。小提醒:你看到的并不一定是你所想的,小心被VS给忽悠了。