stone2083

          ibatis--部分更新表記錄字段的方法

          使用ibatis,如果要更新表記錄,一般常用的做法就是,查找出記錄,然后修改部分字段,進(jìn)行update操作.
          以member表為例:
          MemberDO member = memberDAO.findById(1);
          member.setName(
          "stone");
          memberDAO.update(member);

          這種是最常用的方法.不錯(cuò),在很多應(yīng)用場景下,這么干,完全沒有問題.
          但是(往往存在但是),如果member表中存在一個(gè)或者多個(gè)text(或者blob)字段.難道僅僅為了更新一個(gè)name字段,需要重新update那些本不需要更新的text/blob字段嗎?

          于是乎,人們又想出了一個(gè)辦法,參數(shù)采用map,把需要更新的字段put到map中,
          演示代碼(省略ibatis的sqlmap文件):
          Map<String,Object> map = new HashMap<String,Object>();
          map.put(
          "name","stone");
          memberDAO.update(map);

          沒錯(cuò),這種方法不錯(cuò).需要更新哪些字段,只需要?jiǎng)討B(tài)put到map中就可以.
          但是,對(duì)于這種方法,需要調(diào)用更新的地方,需要手工維護(hù)數(shù)據(jù)庫的字段名,如果在put的時(shí)候,一不小心拼錯(cuò)字段名,那么更新操作肯定和你預(yù)計(jì)的會(huì)有差別.
          比如上面的代碼:
          Map<String,Object> map = new HashMap<String,Object>();
          map.put(
          "nama","stone");
          memberDAO.update(map);
          不小心把name拼成了nama,那么新的name字段就無法保存到數(shù)據(jù)庫中.試想一下,任何需要更新字段的地方,都存在拼寫錯(cuò)誤的風(fēng)險(xiǎn).

          于是乎,人們又想到了參數(shù)類,比如就把MemberDO當(dāng)成參數(shù)類:
          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# 
          這方法貌似不錯(cuò),不會(huì)存在字段名拼寫錯(cuò)誤的風(fēng)險(xiǎn).并且需要更新哪些字段,動(dòng)態(tài)set一下就可以.
          但是,如果要把某個(gè)字段設(shè)置為null,那怎么辦?那沒轍咯...(sqlmap中約定,只有不為null的時(shí)候,才更新).

          那...那...那怎么辦呢?
          貌似只有Map才能滿足需求嘛...因?yàn)閟qlmap中有個(gè)
          "isPropertyAvailable"和"isNull"屬性支持.只要配合這兩個(gè)屬性,就能區(qū)分需要更新為null,還是不更新保持原字段內(nèi)容.
          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,那么更新的時(shí)候,就不會(huì)更新這個(gè)字段,如果map.put("loginId",null),那么就會(huì)把loginId更新為null.
          看來只有map能勝任.

          不是說,使用map,維護(hù)字段內(nèi)容很麻煩嘛.但是好像又只能使用它?
          于是乎,又想到了一種思路(也是本文要介紹的一個(gè)方法)
          通過方法攔截,在設(shè)置參數(shù)類的時(shí)候,把設(shè)置的屬性值put到map中.(cglib是很勝任這樣的場合的)

          首先,需要一個(gè)BaseDO.java DataObject的基類,僅僅用于維護(hù)一份Map對(duì)象.
          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,寫一個(gè)對(duì)set方法的攔截器:
          SetterInterceptor.java 用于對(duì)截獲set操作,把set的對(duì)象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);
              }

          }

          寫一個(gè)創(chuàng)建Setter的工廠類,用于創(chuàng)建帶方法攔截的DataObject對(duì)象
          public class SetterFactory {

              
          private static final SetterInterceptor setterInterceptor = new SetterInterceptor();

              @SuppressWarnings(
          "unchecked")
              
          public static <extends BaseDO> T getSetterInstance(Class<T> clazz) {
              Enhancer enhancer 
          = new Enhancer();
              enhancer.setSuperclass(clazz);
              enhancer.setCallback(setterInterceptor);
              
          return (T) enhancer.create();
              }

          }

          那么對(duì)于client調(diào)用,就非常簡單了.
          如:
          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對(duì)象,那么對(duì)于表記錄的更新操作,僅僅需要一個(gè)sql,就能解決.比較方便.

          附件中,把整個(gè)演示代碼附上,有興趣的朋友,可以了解下:
          采用maven構(gòu)建,workspace編碼采用utf-8.數(shù)據(jù)庫采用pgsql

          demo附件

          備注:
          member表創(chuàng)建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;


          特別說明:
          此方法原創(chuàng)作者為公司同事,本文只是盜用了他的創(chuàng)意.


          posted on 2008-06-26 22:46 stone2083 閱讀(7879) 評(píng)論(2)  編輯  收藏 所屬分類: java

          Feedback

          # re: ibatis--部分更新表記錄字段的方法[未登錄] 2013-01-29 14:35 JAKE

          不錯(cuò),學(xué)習(xí)了下  回復(fù)  更多評(píng)論   

          # re: ibatis--部分更新表記錄字段的方法 2014-08-31 23:48 yanick

          不錯(cuò)  回復(fù)  更多評(píng)論   

          主站蜘蛛池模板: 贵州省| 祁门县| 通化市| 兰西县| 洛扎县| 常熟市| 滁州市| 田林县| 马关县| 林州市| 乐安县| 南平市| 陵川县| 岐山县| 怀化市| 德州市| 尚志市| 西安市| 宁化县| 仁布县| 星子县| 白水县| 铁岭市| 道孚县| 衡水市| 万载县| 米林县| 阜城县| 江山市| 老河口市| 隆化县| 繁昌县| 齐河县| 苍溪县| 佛坪县| 金川县| 中方县| 浦县| 黎川县| 青阳县| 平定县|