IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    基于Ibatis2.*的DAO层重构

    longhao (longtask@gmail.com)发表于 2010-12-03 01:43:04
    love 0

        Ibatis框架的架构在不断的演进,稳定的业务系统在不断完善业务的过程中,多数程序员们希望代码风格能够保持一致,导致系统中到ibatis配置写法仍然停留在多年前的水平。一般情况下,程序员更注重业务层的重构,抽象服务的接口,归并重复的方法,封装公用的逻辑等。由于Dao层的配置并不是显得那么刺眼,所以关注度也显得明显不够。在复杂的业务系统中,DAO层一般都是自动生成的,然而需求不断增加的后,一个文件中一堆的ibatis配置,您或许第一想法是业务好复杂,然后就只能望着配置兴叹了。

        本文通过讨论ibatis的配置重构优化,来提高DAO层的易用性,简洁性,达到更好封装DAO层的目的。

         参数传递对象,不要用Map

        使用map传递参数的好处是,无论多少,都可以put进去,然后在配置中获取到。这样做到坏处是:每次加入了新的参数,都需要修改dao层,严重违反了开闭原则(OCP)。传递对象的好处就是更好到封装了参数的变化,添加参数dao层不用修改;建议是所有的对象都继承一个父类,这个类中封装了分页参数,里面有一个map来传递变量。

    Map<String,Object> params;
        public Object getParam(String key){
            return params.get(key);
        }
        public void setParam(String key,Object value){
            if(params == null){
                params = new HashMap<String,Object>();
            }
            params.put(key, value);
        }
        //在对象中setParam后,可以在ibatis中这样调用:
        <isNotEmpty prepend="and" property = "params.name">
                    name = #params.name#
        </isNotEmpty>
        //同时,我们也可以把查询出来的结果set到params中,然后在service层getParam来获取相关的结果()。
        <resultMap id="user4group" class="user">
            <result property="userId" column="user_id" />
            <result property="name" column="name" />
            <result property="params.groupName" column="group_name" />
        </resultMap>

         用标签<sql>封装查询条件

        查询表中单条记录,列表记录以及查询表中数据总数的条件基本上都是一样的,所以后面的条件可以写在一个里面,在查询条件不属于对象属性时,可以在动态参数中增加条件。

    <sql id="query_user">
            <dynamic prepend="WHERE">
                    <isNotEmpty prepend="and" property = "name">
                        name = #name#
                    </isNotEmpty>
                    <isNotEmpty prepend="and" property = "params.names">
                        ip_address_id in ($params.names$)
                    </isNotEmpty>
            </dynamic>
        </sql>
        //在<select>中添加查询条件<include refid="query_user" />    (list_user,list_user_count是一样的添加):
        <select id="find_user" resultMap="user_result" parameterClass="user">
            SELECT
                   name,age
            FROM user
            <include refid="query_user" />   
        </select>

         合理的使用<iterate>

        查询条件中,某字段需要传递多值查询到情况比较普遍,有一种做法是直接拼接成字符串传递给ibatis;存在这样的情况:查询所有user中的vip用户和高级用户,参数传递将如何呢?拼接一下,然后params.type动态搞定? ibatis提供了一种迭代器的方式来操作类似的需求(不仅仅是这样的需求)

    public class UserServiceImpl{
            int[] type = {1,2};//定义的状态
            User user = new User();
            user.setParam("type",type);
            List<User> userList = userDao.listUser(user);
        }
       // ibatis 中的写法是:
        <select id="list_user_group" parameterClass="user">
            select user from user where type in
                <iterate prepend="and" property="params.type" open="("
                  close=")" conjunction=",">
                  #params.type[]:INTEGER#
                </iterate>
          </select>

        这样做的好处是减少了在java代码中拼字符串,可以把List或者是数组直接传递给ibatis,让程序员容易理解传递到内容。

         延迟加载

        查询group相关信息,然后做一系列的操作,而后返回结果需要查询group下面的user的信息。可以使用ibatis提供的延时加载机制处理。具体信息参考本人前面写到一篇文章《ibatis的延迟加载机制》

         缓存配置表和字典表

        业务中经常查询数据库中的配置表和字典表的信息,高压力的系统中会直接使用memcache来搞定缓存,可是一般系统压力都达不到使用memcache的条件,本地缓存就够了(我只能说没事玩memcache的人是过度设计)。

        在java的业务层也可以封装,也就是读取配置的时候查询到内容缓存到一个static的对象中。ibatis为我们提供了更底层的封装,可以直接借用配置完成我们想要的功能。

    <cacheModel id="user-cache" imlementation="LRU">
           <flushInterval hours="24"/>
           <flushOnExecute statement="insertUser"/>
           <flushOnExecute statement="updateUser"/>
           <flushOnExecute statement="deleteUser"/>
           <property name="size" value="1000" />
        </cacheModel>

        <select id="find_user" resultMap="user_result" cacheModel="user-cache">
               SELECT name,age FROM user WHERE name like '%'||#name#||'%'
        </select>

       系统中还有很多小细节带来DAO层更好到封装以及足够的简洁,行动中开火吧!



沪ICP备19023445号-2号
友情链接