这一章题目是思考,是因为工作进行到现在,我开始反思着一些API,到底能覆盖多少的日常工作,带来多少的便捷。从最开始的插入开始,到现在已经完成了根据class="tags" href="/tags/SQL.html" title=sql>sql来实现分页查询,我发现用这种纯面向对象的API封装思想去开展我的工作已经无法满足自己大脑中能思考到的使用场景。
比如目前我们的查询API,其参数形式都是默认采用AND的关系,同时由于采用的是Map来保存参数,导致无法使用多个同样参数的条件,比如下面这2条class="tags" href="/tags/SQL.html" title=sql>sql:
select id from person where age=25 or age =27;
select id from person where age=25 and age=27;
他们有两个查询条件,但是却只有一个age列作为查询一句,同时对于参数也是具有两个不同的值,并且,一个是AND的关系,另一个是OR的关系,使用我们封装的API,将无法满足这样的需求。那么该怎么办呢?我想过很多种方案,但是直到现在,我依旧能想出新的应用场景,导致上一种方案无法满足需求。一种极端的思想:
Map<Map<String,Object>,OperateEnum> 其中的key值是具体的条件键值对,后面的是当前的条件与其他的条件是什么关系的枚举。
然后组装class="tags" href="/tags/SQL.html" title=sql>sql的时候可以使用上述的内容解析。但是,下面一种情况呢:
SELECT * FROM PERSON WHERE (AGE=25 OR AGE=26) AND STATUS=1;
我们知道在同时具有or和and的表达式中,表达or关系的语句必须要包含其来,否则很容易出现关系结合错位。
当这种情况出现的时候,即便是那种极端的方案我们还是一筹莫展。该怎么办呢?曾经我一度陷入到这个问题中,于是去看hibernate,看ibatis,发现hibernate提供了非常复杂的API,纵然解决了这个问题,但是对于使用者而言,并没有减少工作反而增加不少,而且很容易出错。再看看ibatis,这个问题很容易的被解决是因为他使用的是映射文件的形式,其中不过是些class="tags" href="/tags/SQL.html" title=sql>sql,所以这个问题对其而言,迎刃而解。那么我们该怎么做,是学习hibernate去封装一堆的API出来,让使用者不断的采用add的方法去添加后缀,然后得到一个并不复杂的class="tags" href="/tags/SQL.html" title=sql>sql,然后执行?
这样的工作是无意义的,我们的目的是降低工作量,而不是增加复杂度和使用门槛。所以无奈之下,我想还是沿用ibatis的思想吧,就是接收class="tags" href="/tags/SQL.html" title=sql>sql,就让使用者来告诉我,这个class="tags" href="/tags/SQL.html" title=sql>sql是什么样子吧!
所以我们接纳合法的class="tags" href="/tags/SQL.html" title=sql>sql:
select * from table where param1=? and (param2=? or param3=?) order by param4 desc;
这样的一条class="tags" href="/tags/SQL.html" title=sql>sql,使用了占位符的形式,对于使用者而言并不困难。
而对于只查询一条结果的情况,干脆就直接使用具体的值来替代占位符吧,这样省去了使用者使用Map<String,Object>的麻烦。
那么我们就需要提供支持使用class="tags" href="/tags/SQL.html" title=sql>sql来进行数据库操作的API,增加以下几个API:
这几个API,支持了class="tags" href="/tags/SQL.html" title=sql>sql作为参数直接进行数据库操作。而之前的那几个api,是我一直很希望能达到的一种结果,既不要像ibatis这样使用配置文件编写class="tags" href="/tags/SQL.html" title=sql>sql,导致映射文件和java文件同时编写增加出错率,也不要像hibernate那样api使用起来需要特殊学习。那他们的定位就是作为便捷方法吧,虽然他们的功能,上面这几个方法也能实现,但是在使用上,他们并不需要编写具体的class="tags" href="/tags/SQL.html" title=sql>sql,只需要像操作API一样的调用方法,传入参数,拿到结果就可以了。
工作进行到目前为止,这个过程中,在进行工作前很多的没想到的问题都已经逐渐清晰,比如封装的过程中,会遇到哪些问题,需要注意哪些问题,这里可以简单的总结一下:
1、封装的api的类型,增删查改只是一个笼统是说法,单纯就 查 而言,就可以细化成无数种,比如使用单个字段查,使用多个字段联合查,联合查的字段的关系是AND还是OR还是BETWEEN还是同时存在,比如分页查询某列,分页查询对象,排序查询等等等等。增加可以分成单个对象增加,批量增加等等。难倒这些都要去通过封装提供吗?对于查询的分类而言,是无法通过一个又一个api去满足,然后提供给使用者一堆的API,让他们去费劲脑汁的选择,这样是得不偿失的,不能单纯的为了封装而封装。而如果要达到一个简练的封装,是否要提供一个解析算法,动态的解析使用者到底要什么样的class="tags" href="/tags/SQL.html" title=sql>sql?这样该如何去设计,至少现在我还没想好。
2、对于数据库类型和DO类中类型的转换问题,是使用一个Object笼统的概括掉,还是使用? extends Object更精准的定位,对于一个api的返回值,在多种应用场景下,是否满足大多数的而且是便捷而友好的?
3、该如何去选择jdbcTemplate提供的方法来实现我们的功能,是query还是queryForList,是update,还是batchUpdate,这些都是要在特定的api中具体思考的。
4、整个抽象类的设计上,抽象出来的原子方法是否可以为不同的包装api服务?比如类中queryForList和queryForMap两个方法,还有rowMapper的转换方法,我们抽象出来以后,向上封装了多种多样的api,虽然底层都是这几个基础服务,但是对外丰富的封装手段将使得我们的父类功能完善多种多样。
5、这个封装的过程,到底为了什么,我们将是为了提高开发效率,降低编码工作量,所以在设计api的时候,其输入输出一定要保证简捷,易用,至少目前虽然采用了@Table的注解方法解决获取表名称的问题,还是会想是否有更好的方法,比如外部传入对象类型的问题等等。
就让这个过程当做是自己的学习和修炼吧,以后再次遇到相同的工作,多想想这一次的经历,我得到了什么样的经验,遇到了哪些问题,这或许才是本次工作最重要的收获吧!
最后附上目前为止的整个类的内容://后续将继续封装。。。。
注解方法: