ibatis--部分更新表记录字段的方法使用ibatis,如果要更新表记录,一般常用的做法就是,查找出记录,然后修改
ibatis--部分更新表记录字段的方法
使用ibatis,如果要更新表记录,一般常用的做法就是,查找出记录,然后修改部分字段,进行update操作.
以member表为例:
MemberDO?member?=?memberDAO.findById(1);
member.setName("stone");
memberDAO.update(member);
这种是最常用的方法.不错,在很多应用场景下,这么干,完全没有问题.
但是(往往存在但是),如果member表中存在一个或者多个text(或者blob)字段.难道仅仅为了更新一个name字段,需要重新update那些本不需要更新的text/blob字段吗?
于是乎,人们又想出了一个办法,参数采用map,把需要更新的字段put到map中,
演示代码(省略ibatis的sqlmap文件):
Map<String,Object>?map?=?new?HashMap<String,Object>();
map.put("name","stone");
memberDAO.update(map);
没错,这种方法不错.需要更新哪些字段,只需要动态put到map中就可以.
但是,对于这种方法,需要调用更新的地方,需要手工维护数据库的字段名,如果在put的时候,一不小心拼错字段名,那么更新操作肯定和你预计的会有差别.
比如上面的代码:
Map<String,Object>?map?=?new?HashMap<String,Object>();
map.put("nama","stone");
memberDAO.update(map);
不小心把name拼成了nama,那么新的name字段就无法保存到数据库中.试想一下,任何需要更新字段的地方,都存在拼写错误的风险.
于是乎,人们又想到了参数类,比如就把MemberDO当成参数类:
MemberDO?memberParam?=?new?MemberDO();
memberParam.setName("stone");
memberDAO.update(memberParam);
sqlmap.xml如下:
update?member
set?gmt_modified?=?current_date
<dynamic>
<isNotNull?property="loginId",prepend=",">
login_id?=?#loginId#
</isNotNull>
???<isNotNull?property="name",prepend=",">
name?=?#name#
</isNotNull>
???

</dynamic>
where?id?=?#id#?
这方法貌似不错,不会存在字段名拼写错误的风险.并且需要更新哪些字段,动态set一下就可以.
但是,如果要把某个字段设置为null,那怎么办?那没辙咯...(sqlmap中约定,只有不为null的时候,才更新).
那...那...那怎么办呢?
貌似只有Map才能满足需求嘛...因为sqlmap中有个
"isPropertyAvailable"和"isNull"属性支持.只要配合这两个属性,就能区分需要更新为null,还是不更新保持原字段内容.
sqlmap文件演示:
<isPropertyAvailable?property="loginId"?prepend=",">
????????<isNotNull?property="loginId">
??????????<![CDATA[
????????????login_id?=?#loginId#
??????????]]>
????????</isNotNull>
????????<isNull?property="loginId">
??????????<![CDATA[
????????????login_id?=?null
??????????]]>
????????</isNull>
</isPropertyAvailable>
只要map不put loginId,那么更新的时候,就不会更新这个字段,如果map.put("loginId",null),那么就会把loginId更新为null.
看来只有map能胜任.
不是说,使用map,维护字段内容很麻烦嘛.但是好像又只能使用它?
于是乎,又想到了一种思路(也是本文要介绍的一个方法)
通过方法拦截,在设置参数类的时候,把设置的属性值put到map中.(cglib是很胜任这样的场合的)
首先,需要一个BaseDO.java DataObject的基类,仅仅用于维护一份Map对象.
BaseDO.java:
public?class?BaseDO?implements?Serializable?{
????private?static?final?long?serialVersionUID?=?-315506079592557582L;
????private?Map<String,?Object>?setterMap;
????public?synchronized?void?initSetterMap()?{
????if?(setterMap?==?null)?{
????????setterMap?=?new?HashMap<String,?Object>();
????}
????}
????public?Map<String,?Object>?getSetterMap()?{
????return?setterMap;
????}
}
采用Cglib,写一个对set方法的拦截器:
SetterInterceptor.java 用于对截获set操作,把set的对象put到map中
public?class?SetterInterceptor?implements?MethodInterceptor?{
????private?static?final?String?SET_METHOD?=?"set";
????@Override
????public?Object?intercept(Object?obj,?Method?method,?Object[]?args,
????????MethodProxy?proxy)?throws?Throwable?{
????//?拦截DataObject中所有的set方法,把set的属性放入到map中
????if?(method.getName().startsWith(SET_METHOD))?{
????????if?(obj?instanceof?BaseDO)?{
????????BaseDO?baseDO?=?(BaseDO)?obj;
????????baseDO.initSetterMap();
????????String?attribute?=?StringUtils.substring(method.getName(),
????????????SET_METHOD.length());
????????attribute?=?StringUtils.uncapitalize(attribute);
????????if?(args?!=?null?&&?args.length?==?1)?{
????????????baseDO.getSetterMap().put(attribute,?args[0]);
????????}
????????}
????}
????return?proxy.invokeSuper(obj,?args);
????}
}
写一个创建Setter的工厂类,用于创建带方法拦截的DataObject对象
public?class?SetterFactory?{
????private?static?final?SetterInterceptor?setterInterceptor?=?new?SetterInterceptor();
????@SuppressWarnings("unchecked")
????public?static?<T?extends?BaseDO>?T?getSetterInstance(Class<T>?clazz)?{
????Enhancer?enhancer?=?new?Enhancer();
????enhancer.setSuperclass(clazz);
????enhancer.setCallback(setterInterceptor);
????return?(T)?enhancer.create();
????}
}
那么对于client调用,就非常简单了.
如:
public?class?Client?{
????private?static?final?Log?log?=?LogFactory.getLog(Client.class);
????private?static?final?String?APP_CONFIG_FILE?=?"cn/zeroall/javalab/ibatis/app.xml";
????public?static?void?main(String[]?args)?{
????ApplicationContext?ctx?=?new?ClassPathXmlApplicationContext(
????????APP_CONFIG_FILE);
????MemberDAO?memberDAO?=?(MemberDAO)?ctx.getBean("memberDAO");
????MemberDO?setter?=?SetterFactory.getSetterInstance(MemberDO.class);
????setter.setId(1);
????setter.setLoginId("stone1");
????setter.setName("stone1");
????memberDAO.updateById(setter);
????MemberDO?member?=?memberDAO.findById(1);
????log.info(member.getLoginId());
????}
}
sqlmap文件如下:
<update?id="update-by-id"?parameterClass="java.util.Map">
????<![CDATA[
??????update?member
??????set?gmt_modified?=?current_date
????]]>
????<dynamic>
??????<isPropertyAvailable?property="loginId"?prepend=",">
????????<isNotNull?property="loginId">
??????????<![CDATA[
????????????login_id?=?#loginId#
??????????]]>
????????</isNotNull>
????????<isNull?property="loginId">
??????????<![CDATA[
????????????login_id?=?null
??????????]]>
????????</isNull>
??????</isPropertyAvailable>
??????<isPropertyAvailable?property="password"?prepend=",">
????????<isNotNull?property="password">
??????????<![CDATA[
????????????password?=?#password#
??????????]]>
????????</isNotNull>
????????<isNull?property="password">
??????????<![CDATA[
????????????password?=?null
??????????]]>
????????</isNull>
??????</isPropertyAvailable>
??????<isPropertyAvailable?property="name"?prepend=",">
????????<isNotNull?property="name">
??????????<![CDATA[
????????????name?=?#name#
??????????]]>
????????</isNotNull>
????????<isNull?property="name">
??????????<![CDATA[
????????????name?=?null
??????????]]>
????????</isNull>
??????</isPropertyAvailable>
??????<isPropertyAvailable?property="profile"?prepend=",">
????????<isNotNull?property="profile">
??????????<![CDATA[
????????????profile?=?#profile#
??????????]]>
????????</isNotNull>
????????<isNull?property="profile">
??????????<![CDATA[
????????????profile?=?null
??????????]]>
????????</isNull>
??????</isPropertyAvailable>
????</dynamic>
????<![CDATA[
????????where?id?=?#id#
????]]>
??</update>
一旦采用了Setter对象,那么对于表记录的更新操作,仅仅需要一个sql,就能解决.比较方便.
附件中,把整个演示代码附上,有兴趣的朋友,可以了解下:
采用maven构建,workspace编码采用utf-8.数据库采用pgsql
demo附件
备注:
member表创建sql如下:
--?Table:?member
--?DROP?TABLE?member;
CREATE?TABLE?member
(
??id?serial?NOT?NULL,
??login_id?character?varying(16),
??"password"?character?varying(16),
??"name"?character?varying(32),
??profile?text,
??gmt_created?timestamp?without?time?zone,
??gmt_modified?timestamp?without?time?zone,
??CONSTRAINT?member_pkey?PRIMARY?KEY?(id)
)
WITH?(OIDS=FALSE);
ALTER?TABLE?member?OWNER?TO?javalab;