[框架那点事儿-快速开发季]编写自己的数据持久层(7)总结篇

// 备注:本章节的代码较多,在IE下显示有点问题,请用firefox浏览,或者请留下邮箱,我会将整个工程打包发送

 

在前面的六篇文章中,针对日常开发常用到的DB的操作对spring框架的jdbcTemplate进行了一定的封装,从增删查改四个方面,针对于全字段、部分字段、单一、批量几个方面展开来一一封装,提供了30多个API,基本能够覆盖日常开发所需的DB操作,降低一定的工作成本,提高工作效率。在这个封装的过程中,遇到了不少在进行这个专题工作前没有思考到的问题,也伴随着不断的代码新增、重构优化,将能考虑到的问题解决,首先先总结一下,自己都遇到哪些方面的问题:

 

1、数据库中获取数据如何交给DO类?同样的,在避免代码中出现sql的情况下,只使用API的时候,如何使用尽量少的参数来完成诸如查询sql语句条件参数设置?

第一个小问题,数据库中获取的数据,要传递给DO类,最快捷的方式就是通过set方法,而set方法的调用并没有一个特定的渠道,针对不同的字段都能自动完成,所以针对不同的字段,我们需要自己去判断调用其相应的set方法。这样来操作,是否我们需要针对于每一个字段都去判断名称,然后手动的调用一次set方法呢?

当然不能那么的人肉化,我们的方案是,根据字段的名称,自动解析set方法名称,然后通过反射区调用set方法,这里调用set方法还需要一个参数,这个参数就是从数据库中取出的值,这样就完成了字段的赋值。这里遇到了一个问题,就是参数类型不匹配的问题,抛出argument not match type异常,这个问题的解决我们将在下面的第2个问题中解决。

上面第二个小问题,关于sql语句查询条件的问题,我们想既然我们能够用过反射的方法调用set方法,那么肯定也可以通过反射的方法调用get方法来完成Do中的值的取出过程。所以这个问题也就迎刃而解了!

 

同时自动装配sql,我们通过引入自定义注解@Table来实现DO和Table的对应。

 

2、数据库的类型和java类型转换的问题。

从数据库中取出的ResultSet后根据name获取特定字段的值,拿到的数据的类型并非是Do类中对应字段的类型,而是一个笼统的Object类型,所以需要我们反向获取DO中的类型,然后以此进行转换,再利用反射调用set方法设置DO中向上提供。

这里还遇到一个问题是,封装的类型放入Map<String,Object>保存的时候,由于之前set进去的数据是String类型,后续再次放入的Date类型的数据,会自动变成字符串类型,时间值成为了一个字符串,在上层方法进行转换的时候,会发生数据类型转换错误异常,所以,这里需要针对于不能直接与String进行转换的参数进行手动的转换。这个解释不知道是否是发生这个类型自动转换问题的原因,至少目前debug来看,这个过程是难以控制的。

 

3、数据库获取Date类型字段空值的问题其实在连接数据库的时候使用zeroDateTimeBehavior=convertToNull就可以解决,但是这个方法一直是不是很满意的解决方案,虽然这样可以避免出现上述问题,但是从代码健壮性来看,应该至少提供一种空值的控制手段,来避免发生这种“可预知”的异常。

 

4、API的覆盖面的问题。

这个问题也是最困扰我的问题,我一直尝试能够使用纯API的方式覆盖日常所有的sql操作,但是封装到一定复杂度的时候我发现,我的代码的复杂度,尤其是API使用上的复杂度,已经远远超出了解决这条sql的使用问题,也就是说使用最笨拙的封装sql来完成操作都要简单的多。

而且这样的问题不断的涌现出来,尤其是到了后期,当简单应用场景都解决掉之后,基本上对于稍微复杂点的应用场景,都要重新开始提供一个API来解决,包括其中的级联查询问题等,甚至是使用简单的and和or组合的查询等,都日益明显的暴露我们现有的简单API的狭隘性。所以我回顾了hibernate和ibatis在使用上的一些习惯,发现他们很聪明的利用调用者编写sql,同时在sql中使用占位符的形式标识参数的手段来解决掉各种各样的应用场景无法自动封装sql的问题。所以我在想我的工作是否走进了死胡同,是否我应该退出来,重新尝试用一种新的手段来解决上述sql不能都统一自动生成的问题。

于是我删除了大片大片的代码,并且决定借用ibatis的这个方式,即使用占位符的形式,接受不同的sql语句,渲染占位符并进行操作的方式来完成不同应用场景的需求。所以这样继续我的工作,我将API分成大致三类:

1、纯使用java代码来完成的简单需求,诸如简单的不同字段的and组合查询,单值查询,这类的sql我通过自动的sql生成机制来完成;

2、使用jdbcTemplate默认占位符"?"(问号)的形式,这种方式的优点在于用户自定义sql,然后只需要传入sql和相应的参数就可以满足不同的需求,我们的参数采用的是Map的形式,这样的类型选择带来的致命问题是:sql中对于一个字段的多次操作,也就是需求是一个字段key多个value的时候,将无法满足!所以我们可以知道,为什么jdbcTemplate采用的参数类型Object[],而不是其他的类型,而且在其低层实现上,它也是通过次序来一次渲染完成。所以一旦我们Object[]赋值顺序异常,会导致sql无法执行或者执行结果错误。

既然我们采用Map来不分次序的传递参数,程序自己解析对应的参数应该放到什么位置,但却丧失了处理一个字段在sql中多次操作的能力,如between and 或者一个字段多个or的操作等。

这是否让这种API成了鸡肋?

3、针对于上述的一些问题,我引入了类似ibatis的采用自定义名称的占位符,如"#param1#"或者"#name#"等,这样,用户传入的sql只需要是使用占位符的形式,并且传入Map中指定不同占位符的value值,我们通过自动的解析装配,来渲染最终的sql。

这样彻底解决了单列多操作的问题,也解决了Map单纯使用DO属性名称无法传递多个value的问题。比如针对于id字段,在 id between 1 and 10的应用场景中,我们可以通过 id between #id1# and #id2# 来满足。

 

还是要说一句的是,在没有思考清楚前就错误的选择Map来作为参数传递值,确实是一个失误。在后面的工作中,我考虑过如何解决Map的问题,如果真的要解决这个问题,根本的就是解决一个key多个value的问题,很自然的想到了List,但是难道要使用List<Map>来完成吗?那样调用者在组装参数的时候会疯掉……所以还是保留Map吧,针对于简单操作,作为一种便捷API,其他的不能满足的方面,有第三种自定义占位符的方式解决。

 

好了,一些细节的问题,就不去罗嗦,现在先看一下我们目前所有开发出来的API:

 

1、新增一条记录API(点击expand resource可以展开代码)

 

 

2、查询对象类API(点击expand resource可以展开代码)

 

 

3、查询列表类API(点击expand resource可以展开代码)

 

4、查询属性列表类API(点击expand resource可以展开代码)

 

5、分页类API(点击expand resource可以展开代码)

 

6、查询记录数类API(点击expand resource可以展开代码)

 

7、修改类API(点击expand resource可以展开代码)

 

8、批量插入/修改类API(点击expand resource可以展开代码)

 

9、删除类API(点击expand resource可以展开代码)

 

以上是目前封装的所有的API,共分成9个大类,可以满足日常开发所需。其中包含一下三个文件:

 

AbstractBaseDAO

 代码较多,请参见:

 

 [框架那点事儿-快速开发季]编写自己的数据持久层(7)总结篇 代码  

  http://blog.csdn.net/quzishen/archive/2010/08/03/5786323.aspx

分页器Paginal

 

 

注解Table

 

 

 

其中的使用代码和单元测试代码如下:

 

假设我们现在要开发一个热报系统(所谓的热报就是CMS的一种实现方式,大致就是在后天填写数据,在前台某个页面的部分展示)

 

数据库结构:

 

 

热报DAO interface

 

其具体的实现类,继承了上述的抽象父类,并调用其中的方法,内部为了测试基本上调用了所有的父类API:

 

热报DO类

 

单元测试代码:

 

(完)

 

因为第一章上了首页,很多朋友看了第一章的内容,感谢大家给予的理解和鼓励,愿与大家共同进步!

 

2010-08-02

  • 0
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 139
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 139
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值