- 浏览: 2489413 次
- 性别:
- 来自: 杭州
-
文章分类
- 全部博客 (574)
- Book (62)
- Architecture (6)
- Java (39)
- Taobao (41)
- Distributed (4)
- Life (72)
- Database (7)
- Spring (16)
- Photography (15)
- Bicycle (41)
- Test (20)
- jBPM (8)
- Business (12)
- Movie (3)
- Ajax (15)
- Code (7)
- Eclipse (96)
- VIM (2)
- Music (6)
- Groovy (10)
- AutoHotKey (3)
- Dorado (10)
- Maven (7)
- Scrum (5)
- English (20)
- Financial (12)
- OSGi (3)
- Other (4)
- Tool (6)
- Browser (1)
- PPT (1)
- Project Management (4)
- Agile (6)
- Nosql (1)
- Search engine (6)
- Shell (2)
- Open Source (4)
- Storm (10)
- Guava (3)
- Baby (1)
- netty (1)
- Algorithm (1)
- Linux (1)
- Python (2)
最新评论
-
roy2011a:
https://github.com/ebottabi/sto ...
storm的序列化问题及与spring的结合方式 -
roy2011a:
能抗能打 写道哥们儿,你好!能共享下那个storm与sprin ...
storm的序列化问题及与spring的结合方式 -
Alick1:
兄弟,你之前是不是在深圳的正阳公司呆过啊?
storm的ack和fail -
liuleixwd:
先点个赞,写的非常好!有个问题请教下,如果我再bolt里不用e ...
storm的ack和fail -
yao-dd:
solr的facet查询
最近对ibatis2.1.0.256这个版本(大部分应用都是用该版本)的源代码以及spring的SqlMapClientTemplate相关的代码看了下. 这个版本的ibatis有一个问题, 就是如果执行sql出错的话, 出错信息非常不友好, 连最基本的sql和paramter都没有, 给查找问题带来不小的麻烦, 本想看看代码能进行一下定制, 由于版本太老, 扩展性太差了, 基本没法扩展, 最后被迫放弃.
题外话, 现在ibatis都已经出到3.0了(为什么我们还不升级?)
发现spring和ibatis的代码风格相差还是很大的. 两相比较就可以看出差距来, 同时也对di有了进一步的理解.
在spring中几乎将所有的类都当一个个bean来看待, 因此所有的依赖要么是通过构造函数注入, 要么通过setter方式注入. 而ibatis则明显没有这样的概念, 很多地方对其他类的依赖都是直接在构造函数中new出来的, 这样给定制带来了麻烦, 可以说, 基本上是没法对ibatis进行扩展. 因此我们可以认为ibatis是一个非常封闭的dao框架(当然后续版本的ibatis有所改善).
基于ibatis这样的架构, 导致spring对ibatis的扩展也非常有限.其中最重要的类在我看来无非两个:SqlMapClientFactoryBean, SqlMapClientTemplate, 前者是一个典型的FactoryBean, 用来将ibatis纳入spring的ioc容器中管理. 这里我们可以借鉴一下spring自己的FactoryBean的做法. 一般的FactoryBean都要实现FactoryBean接口, 在getObject()方法中, 我们可以去具体实现到底要怎么创建所需要的bean, 当然简单的可以直接new, 一般情况下还需要借助其他的接口来获取所需要的bean, 比如这里还实现了InitializingBean接口, 即在bean的定义以及相关的依赖设置完毕之后, 调用了afterPropertiesSet()方法,此时便开始根据配置文件来构造SqlMapClient了, 在根据配置构造SqlMapClient的部分spring调用了ibatis的SqlMapClientBuilder这个构造器来处理的, 从这里开始, ibatis便关闭了我们灵活处理构造SqlMapClient的大门. 对内部的构造过程我们几乎完全无法控制, 如果希望有所改善, 必须复写大量的类.
SqlMapClientTemplate用来对ibatis的SqlMapClient进行包装, 以便在调用ibatis访问数据库的时候, 做一些手脚, 不过这里这里主要是为了将iBatis抛出的SqlException转换成spring统一的DataAccessException异常, 处理异常的CRUD统一入口代码如下:
因此如果你不喜欢spring的exception处理方式, 也可以不用它. 直接用SqlMapClient即可. spring之所以能将所有的操作归结到一个入口, 这个与SqlMapClientCallback接口的使用有很大的关系. 这里我们可以见到spring里面典型的接口->匿名类的用法.spring对匿名类用的那可是相当的多, 这个也是我们值得借鉴的一个地方, 不过匿名类不可滥用, 一般来说匿名类都是很简单的调用(10行左右), 如果有大段的代码调用, 那么匿名类就不合适了, 这里基本都很简单, 都是一个简单的转发, 即将对SqlMapClientTemplate的调用转发给SqlMapClient, 比如下面的代码:
另外这里需要说一下的是, SqlMapClient是ibatis用来访问数据库的一个入口, 即一个SqlMapClient表示一个数据库. 所有的CRUD操作都要经过这个接口.
下面我们就来历数ibatis的种种让人崩溃的做法.
ibatis主要做了两件事, 一个就是解析sqlmap配置文件, 就是我们配置的一个sql语句和javabean与表字段之间的映射关系, 这里我们不做多展开.另外一个就是调用jdbc驱动, 执行数据库操作.
这里我们先从解析开始.ibatis的解析入口是SqlMapClientBuilder类. 这个builder类将解析的过程封装到了SqlMapConfigParser中.代码如下:
万恶的new出现了, 这里我们已经没法对parser进行控制了.
接着我们来看看SqlMapConfigParser构造器:
又是大量的new操作, 再一次无语了.
而且只有get方法, 没有set方法, 再一次关闭了注入依赖的可能.
在SqlMapConfigParser中有两个类需要注意, 即SqlMapExecutorDelegate和SqlMapClientImpl, SqlMapExecutorDelegate这个封装了从配置文件解析的一些东东, SqlMapClientImpl所需要的一些配置就是从SqlMapExecutorDelegate获取的, 因此SqlMapClientImpl注入了SqlMapExecutorDelegate, 并在需要的时候转调SqlMapExecutorDelegate, SqlMapClientImpl因为是操作数据库的入口, 还需要负责管理事务, session等, 里面有一个很重要的东东:SqlExecutor, 这个也是在SqlMapExecutorDelegate中实现的.如同名字一样, 它是用来执行sql语句的.这个似乎是我们用来控制底层数据库操作的一个入口, 如果能进行配置将是一件幸福的事儿.它是在SqlMapExecutorDelegate中创建的, 很遗憾, 也是在构造函数中new出来的:
SqlExecutor是一个具体类, 而且没有setter方法, 再一次崩溃.
看到这里, 我们几乎完全失去了对ibatis的底层操作进行控制的可能, 我们继续看SqlExecutor是如何被使用的.
所有的sql语句的执行都是封装在ibatis的一个个MappedStatement中, 大部分的逻辑都是放在GeneralStatement中, 这里我们拿出查询的处理代码:
在这段代码中, 有sql语句, 参数的校验, 另外就是调用sqlExecutor执行了, 基本上很简单, 这里不得不暴露一个ibatis对异常处理非常糟糕的做法, 就是隐藏信息中,没有包含出错的sql语句和对应的参数, 我们仅仅只能知道哪个sqlmap文件配置的哪个sql有问题, 但是不知道最终的sql是个什么样子. 而我们实际开发过程中很大一部分都是因为bad sql导致的. 不能呈现bad sql 自然给解决问题带来不小的难度, 我曾经想了不少问题都没法解决.
另外, 对于在日志中输入正常的sql, 参数, 执行结果的做法, 这里ibatis用到了动态代理.比如这个PreparedStatementLogProxy类:
它的log很特殊是跟java.sql的一些类相关的:
因此很多人在问如何打印日志, 实际上就是要将java.sql这个package下相关类的DEBUG开关打开即可.
题外话, 现在ibatis都已经出到3.0了(为什么我们还不升级?)
发现spring和ibatis的代码风格相差还是很大的. 两相比较就可以看出差距来, 同时也对di有了进一步的理解.
在spring中几乎将所有的类都当一个个bean来看待, 因此所有的依赖要么是通过构造函数注入, 要么通过setter方式注入. 而ibatis则明显没有这样的概念, 很多地方对其他类的依赖都是直接在构造函数中new出来的, 这样给定制带来了麻烦, 可以说, 基本上是没法对ibatis进行扩展. 因此我们可以认为ibatis是一个非常封闭的dao框架(当然后续版本的ibatis有所改善).
基于ibatis这样的架构, 导致spring对ibatis的扩展也非常有限.其中最重要的类在我看来无非两个:SqlMapClientFactoryBean, SqlMapClientTemplate, 前者是一个典型的FactoryBean, 用来将ibatis纳入spring的ioc容器中管理. 这里我们可以借鉴一下spring自己的FactoryBean的做法. 一般的FactoryBean都要实现FactoryBean接口, 在getObject()方法中, 我们可以去具体实现到底要怎么创建所需要的bean, 当然简单的可以直接new, 一般情况下还需要借助其他的接口来获取所需要的bean, 比如这里还实现了InitializingBean接口, 即在bean的定义以及相关的依赖设置完毕之后, 调用了afterPropertiesSet()方法,此时便开始根据配置文件来构造SqlMapClient了, 在根据配置构造SqlMapClient的部分spring调用了ibatis的SqlMapClientBuilder这个构造器来处理的, 从这里开始, ibatis便关闭了我们灵活处理构造SqlMapClient的大门. 对内部的构造过程我们几乎完全无法控制, 如果希望有所改善, 必须复写大量的类.
SqlMapClientTemplate用来对ibatis的SqlMapClient进行包装, 以便在调用ibatis访问数据库的时候, 做一些手脚, 不过这里这里主要是为了将iBatis抛出的SqlException转换成spring统一的DataAccessException异常, 处理异常的CRUD统一入口代码如下:
public Object execute(SqlMapClientCallback action) throws DataAccessException { SqlMapSession session = this.sqlMapClient.openSession(); Connection ibatisCon = null; try { Connection springCon = null; try { .... return action.doInSqlMapClient(session); } catch (SQLException ex) { throw getExceptionTranslator().translate("SqlMapClient operation", null, ex); } finally { DataSourceUtils.releaseConnection(springCon, getDataSource()); } } finally { if (ibatisCon == null) { session.close(); } } }
因此如果你不喜欢spring的exception处理方式, 也可以不用它. 直接用SqlMapClient即可. spring之所以能将所有的操作归结到一个入口, 这个与SqlMapClientCallback接口的使用有很大的关系. 这里我们可以见到spring里面典型的接口->匿名类的用法.spring对匿名类用的那可是相当的多, 这个也是我们值得借鉴的一个地方, 不过匿名类不可滥用, 一般来说匿名类都是很简单的调用(10行左右), 如果有大段的代码调用, 那么匿名类就不合适了, 这里基本都很简单, 都是一个简单的转发, 即将对SqlMapClientTemplate的调用转发给SqlMapClient, 比如下面的代码:
public Object queryForObject(final String statementName, final Object parameterObject) throws DataAccessException { return execute(new SqlMapClientCallback() { public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { return executor.queryForObject(statementName, parameterObject); } }); }
另外这里需要说一下的是, SqlMapClient是ibatis用来访问数据库的一个入口, 即一个SqlMapClient表示一个数据库. 所有的CRUD操作都要经过这个接口.
下面我们就来历数ibatis的种种让人崩溃的做法.
ibatis主要做了两件事, 一个就是解析sqlmap配置文件, 就是我们配置的一个sql语句和javabean与表字段之间的映射关系, 这里我们不做多展开.另外一个就是调用jdbc驱动, 执行数据库操作.
这里我们先从解析开始.ibatis的解析入口是SqlMapClientBuilder类. 这个builder类将解析的过程封装到了SqlMapConfigParser中.代码如下:
public static SqlMapClient buildSqlMapClient(Reader reader) { // return new XmlSqlMapClientBuilder().buildSqlMap(reader); return new SqlMapConfigParser().parse(reader); }
万恶的new出现了, 这里我们已经没法对parser进行控制了.
接着我们来看看SqlMapConfigParser构造器:
public SqlMapConfigParser() { this(null, null); } public SqlMapConfigParser(XmlConverter sqlMapConfigConv, XmlConverter sqlMapConv) { super(new Variables()); parser.setValidation(true); parser.setEntityResolver(new SqlMapClasspathEntityResolver()); vars.sqlMapConfigConv = sqlMapConfigConv; vars.sqlMapConv = sqlMapConv; vars.delegate = new SqlMapExecutorDelegate(); vars.typeHandlerFactory = vars.delegate.getTypeHandlerFactory(); vars.client = new SqlMapClientImpl(vars.delegate); ... }
又是大量的new操作, 再一次无语了.
而且只有get方法, 没有set方法, 再一次关闭了注入依赖的可能.
在SqlMapConfigParser中有两个类需要注意, 即SqlMapExecutorDelegate和SqlMapClientImpl, SqlMapExecutorDelegate这个封装了从配置文件解析的一些东东, SqlMapClientImpl所需要的一些配置就是从SqlMapExecutorDelegate获取的, 因此SqlMapClientImpl注入了SqlMapExecutorDelegate, 并在需要的时候转调SqlMapExecutorDelegate, SqlMapClientImpl因为是操作数据库的入口, 还需要负责管理事务, session等, 里面有一个很重要的东东:SqlExecutor, 这个也是在SqlMapExecutorDelegate中实现的.如同名字一样, 它是用来执行sql语句的.这个似乎是我们用来控制底层数据库操作的一个入口, 如果能进行配置将是一件幸福的事儿.它是在SqlMapExecutorDelegate中创建的, 很遗憾, 也是在构造函数中new出来的:
public SqlMapExecutorDelegate() { mappedStatements = new HashMap(); cacheModels = new HashMap(); resultMaps = new HashMap(); parameterMaps = new HashMap(); requestPool = new ThrottledPool(RequestScope.class, DEFAULT_MAX_REQUESTS); sessionPool = new ThrottledPool(SessionScope.class, DEFAULT_MAX_SESSIONS); sqlExecutor = new SqlExecutor(); typeHandlerFactory = new TypeHandlerFactory(); dataExchangeFactory = new DataExchangeFactory(typeHandlerFactory); }
SqlExecutor是一个具体类, 而且没有setter方法, 再一次崩溃.
看到这里, 我们几乎完全失去了对ibatis的底层操作进行控制的可能, 我们继续看SqlExecutor是如何被使用的.
所有的sql语句的执行都是封装在ibatis的一个个MappedStatement中, 大部分的逻辑都是放在GeneralStatement中, 这里我们拿出查询的处理代码:
protected void executeQueryWithCallback(RequestScope request, Connection conn, Object parameterObject, Object resultObject, RowHandler rowHandler, int skipResults, int maxResults) throws SQLException { ErrorContext errorContext = request.getErrorContext(); errorContext.setActivity("preparing the mapped statement for execution"); errorContext.setObjectId(this.getId()); errorContext.setResource(this.getResource()); try { parameterObject = validateParameter(parameterObject); Sql sql = getSql(); errorContext.setMoreInfo("Check the parameter map."); ParameterMap parameterMap = sql.getParameterMap(request, parameterObject); errorContext.setMoreInfo("Check the result map."); ResultMap resultMap = sql.getResultMap(request, parameterObject); request.setResultMap(resultMap); request.setParameterMap(parameterMap); errorContext.setMoreInfo("Check the parameter map."); Object[] parameters = parameterMap.getParameterObjectValues(request, parameterObject); errorContext.setMoreInfo("Check the SQL statement."); String sqlString = sql.getSql(request, parameterObject); errorContext.setActivity("executing mapped statement"); errorContext.setMoreInfo("Check the SQL statement or the result map."); RowHandlerCallback callback = new RowHandlerCallback(resultMap, resultObject, rowHandler); sqlExecuteQuery(request, conn, sqlString, parameters, skipResults, maxResults, callback); errorContext.setMoreInfo("Check the output parameters."); if (parameterObject != null) { postProcessParameterObject(request, parameterObject, parameters); } errorContext.reset(); sql.cleanup(request); notifyListeners(); } catch (SQLException e) { errorContext.setCause(e); throw new NestedSQLException(errorContext.toString(), e.getSQLState(), e.getErrorCode(), e); } catch (Exception e) { errorContext.setCause(e); throw new NestedSQLException(errorContext.toString(), e); } } protected void sqlExecuteQuery(RequestScope request, Connection conn, String sqlString, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException { getSqlExecutor().executeQuery(request, conn, sqlString, parameters, skipResults, maxResults, callback); }
在这段代码中, 有sql语句, 参数的校验, 另外就是调用sqlExecutor执行了, 基本上很简单, 这里不得不暴露一个ibatis对异常处理非常糟糕的做法, 就是隐藏信息中,没有包含出错的sql语句和对应的参数, 我们仅仅只能知道哪个sqlmap文件配置的哪个sql有问题, 但是不知道最终的sql是个什么样子. 而我们实际开发过程中很大一部分都是因为bad sql导致的. 不能呈现bad sql 自然给解决问题带来不小的难度, 我曾经想了不少问题都没法解决.
另外, 对于在日志中输入正常的sql, 参数, 执行结果的做法, 这里ibatis用到了动态代理.比如这个PreparedStatementLogProxy类:
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { try { if (EXECUTE_METHODS.contains(method.getName())) { if (log.isDebugEnabled()) { log.debug("{pstm-" + id + "} PreparedStatement: " + removeBreakingWhitespace(sql)); log.debug("{pstm-" + id + "} Parameters: " + getValueString()); log.debug("{pstm-" + id + "} Types: " + getTypeString()); } clearColumnInfo(); if ("executeQuery".equals(method.getName())) { ResultSet rs = (ResultSet) method.invoke(statement, params); return ResultSetLogProxy.newInstance(rs); } else { return method.invoke(statement, params); } } else if (SET_METHODS.contains(method.getName())) { if ("setNull".equals(method.getName())) { setColumn(params[0], null); } else { setColumn(params[0], params[1]); } return method.invoke(statement, params); } else if ("getResultSet".equals(method.getName())) { ResultSet rs = (ResultSet) method.invoke(statement, params); return ResultSetLogProxy.newInstance(rs); } else { return method.invoke(statement, params); } } catch (Throwable t) { throw ClassInfo.unwrapThrowable(t); } }
它的log很特殊是跟java.sql的一些类相关的:
private static final Log log = LogFactory.getLog(PreparedStatement.class);
因此很多人在问如何打印日志, 实际上就是要将java.sql这个package下相关类的DEBUG开关打开即可.
发表评论
-
spring中map的定义, 包括value为class的定义
2013-03-11 22:26 37<bean id="fieldM ... -
spring的xml string applicationcontext实现
2013-03-06 07:26 2386这里有两种实现方式: import org.springf ... -
对多数据源进行aop声明式事务管理
2009-11-04 18:57 5013当在对数据库表进行横向切分(将一个表的数据拆分为到多个数据库中 ... -
编程方式实现sping bean延迟初始化
2009-10-09 10:12 2780在实际开发中, 碰到如下需求场景: 在线上需要spring容 ... -
使用spring aop 简化mock设计实现
2009-09-28 20:42 2872有时候为了去掉对外部系统的依赖, 我们需要针对外部依赖的接口创 ... -
自定义logger注解, 简化log4j的配置
2009-09-28 20:20 6083上次在参加支付宝架构培训的时候, 看到他们框架中有一个不错的对 ... -
使用spring aop碰到的几个问题及解决办法
2009-09-28 20:09 4004这里的问题只是针对spring 2.0.7 至于其他版本, ... -
本人常用的两种spring aop实现方式
2009-09-28 10:53 3908备忘一下, 基本的用法包括在配置文件中配置pointcut, ... -
spring aspectj的返回值
2009-05-31 08:40 4328最近使用到了基于aspectj的spring aop, aop ... -
spring map bean定义
2009-05-21 12:20 43582一般我们知道在property属性里面定义一个无id的map是 ... -
spring知识总结
2009-05-09 22:11 3272bean属性及构造器参数:直接量(基本类型、Strings类型 ... -
spring @Configration扩展使用一例
2009-02-05 22:03 5821最近对注解了解的比较多, 也在实际项目中实战了一把, 同时对s ... -
spring 注解学习笔记
2009-02-05 21:20 9839spring 注解讲的最详细的文章: http://www.i ... -
spring加载配置文件
2008-12-11 09:27 2383ClassPathXmlApplicationContext ... -
Spring动态数据源路由实现
2008-09-24 09:32 16865简单的翻译, 也算是一篇笔记. 原文:http://blog. ... -
spring aop学习笔记
2008-02-19 14:08 15608理解spring aop的路径:最初级的做法是通过使用代理将业 ...
相关推荐
SSM框架,即Spring、Spring MVC和MyBatis的组合,是Java开发中常见的Web应用程序框架。这个压缩包提供了一套...同时,通过阅读源码,可以加深对Spring、Spring MVC和MyBatis原理的理解,有助于提升Java Web开发技能。
《深入解析iBatis 2.3.4.726源码》 iBatis,作为一款轻量级的Java持久层框架,以其灵活、高效的特点,在许多项目中得到了广泛应用。本文将针对iBatis 2.3.4.726版本的源代码进行详尽解读,帮助开发者深入了解其内部...
9. **源码解析**:对于对底层原理感兴趣的开发者,可能包含iBatis源码的解读,帮助理解其工作原理。 10. **进阶内容**:可能涵盖MyBatis(iBatis的升级版)的特性,以及如何使用插件扩展iBatis功能。 通过深入学习...
Spring.rar则可能包含了Spring框架的详细文档、实战案例和源码解读,这对于Java后端开发者来说极其重要,因为Spring已经成为Java企业级应用的标准选择。学习Spring需要理解其核心模块,如IoC容器、AOP、MVC框架以及...
【标签】: "源码"和"工具"这两个标签表明,内容可能包括了对MyBatis源码的解读,以及如何将其作为工具整合到项目中的指导。源码分析有助于开发者理解MyBatis如何处理数据库操作,如动态SQL、缓存管理、事务控制等。...