近日工作内容是技术调研,研究iBatis使用方法及源码,源码阅读中总是能看到很多设计模式的影子,整理下来,获益匪浅。
ibatis client包结构
设计原则学习
1) 接口定义:上图中顶层接口定义的功能职责单一,聚在一个接口中,共同支撑一个功能。
2) 好莱坞原则:SqlMapClient的实现类,是顶层调用者,SqlMapExecutorDelegate类是底层的工作者,提供基本的功能,供上层调用。
3) 面向接口编程原则:SessionScope依赖的是抽象接口SqlMapClient、SqlMapExecutor、SqlMapTransactionManager、Transaction属性。
4) iBatis提供给客户端使用的接口是SqlMapClientBuilder类的buildSqlMapClient方法,使用的是建造者模式。SqlMapClient的实例对象的创建,关联关系复杂,在buildSqlMapClient的build方法中完成了对象的属性组装,并将最终的完整对象返回给客户端使用。
5) 面向接口编程的原则,容易被忽视,有的项目直接就写Service的实现层。在集成Spring的项目开发过程中服务层的接口抽象是有必要的,MVC三层结构模式下,action和dao层代码结果通常较固定,如果业务流程有变更,只需要修改中间的服务层。服务层保持接口的编程原则,提供不同的实现,可以使得应用更灵活。
例如:89项目中英文不同模式下,某个操作的流程有差异,那么我们可以针对同一个接口提供两种实现方式,根据项目部署的语言,配置所引用的实现类,而调用代码仅仅依赖的是接口类型。
Spring的bean管理,最初给我印象深刻的一点就是:它可以针对同一个id的bean,根据需要设置不同的class属性(即实现类),而所有依赖该id的bean的地方,本质上是依赖同一个抽象类型的。这也是对OOP面向抽象编程原则的支持。
一条SQL的执行时序图
关联类的创建
1)SqlMapClient实例的创建,由SqlMapClientBuilder类提供静态方法,解析SqlMapClientConfig.xml配置文件创建一个SqlMapConfiguration实例对象,并返回它的SqlMapClientImpl成员变量值。
2)SqlMapClientImpl关联了一个ThreadLocal属性,存储的值是SqlMapSessionImpl对象,它的getLocalSqlMapSession()方法提供了获取SqlMapSessionImpl实例的方法,如果ThreadLocal的get方法能取到值,则返回,如果没有则新建一个SqlMapSessionImpl对象,并set到ThreadLocal中,再返回该对象。
3)SqlMapSessionImpl类关联了一个SessionScope、SqlMapExecutorDelegate,SqlMapSessionImpl类的构造依赖一个SqlMapClient参数,如下:
public SqlMapSessionImpl(SqlMapClientImpl client) { this.delegate = client.getDelegate(); this.sessionScope = this.delegate.beginSessionScope(); this.sessionScope.setSqlMapClient(client); this.sessionScope.setSqlMapExecutor(client); this.sessionScope.setSqlMapTxMgr(client); this.closed = false; }
iBatis的不足之处
我们使用iBatis实现DAO层代码时,通常DAO类需要写大量重复、类似的代码,形如:
//DAO of Person public static void insertPerson (Person person) throws SQLException { sqlMapper.insert("Person.insert", person); } public static void deletePerson (int id) throws SQLException { sqlMapper.delete("Person.deleteById", id); } public static List selectAllPerson () throws SQLException { return sqlMapper.queryForList("Person.selectAll"); } public static Person selectById (int id) throws SQLException { return (Person) sqlMapper.queryForObject("Person.selectById", id); }
这些重复的CRUD的调用,其实本质上只需要提供statement id,及调用参数。这也是myBatis对iBatis的改进,myBatis简化了开发者编写DAO层的工作,将上述调用过程直接与sqlMap配置文件中的statement一一对应,大大简化了DAO的内容,减少了Code工作量。 例如:
DAO类定义:
sqlMap映射配置:
所以,个人更偏好myBatis一些,DAO层在必要的时候可以自己实现,也可以直接通过接口和配置完成,方便又简单。简单项目中,很少涉及到设计模式的运用,再加上公司开发流程不规范,写代码就有些信马由缰,甚至为了赶进度而忽略了基本的编码规范了。阅读框架源码,看到遍地都是设计模式的思想,还是很受启发的。