在 ibatis 配置文件修改后生效这个问题上,小朋友我尝试了很多不科学和科学的方式,通过单元测试 or 各种插件。
单元测试这一方式我比较推荐,不需花费额外的很多时间,可靠和简单。
至于后者呢,前有JRebel、HotCode,后有HotCode2,各种折腾。你要问我为什么不换成 MyBatis,我也不知道。
所以小朋友我决定自己做一个,或者说是尝试,哈哈。
空想
我们在调用 ibatis 的时候,它在背后是如何工作的?
例如最常见的一个 delete 操作:
(List<DogeDO>) sqlMapClientTemplate.delete(NAMESPACE + “delete", dogeQuery)
在developworks上找到了这样一张时序图 [1]
显然,在 SqlMapExecutorDelegate 14 号环节,ibatis 从 MappedStatement 中获得 DeleteStatement,这里的 Statement,可以粗鄙的视作真实的 SQL语句,即
delete * from database;
MappedStatement 是如何初始化的?
学习参考文件[2],了解到经过一系列的解析之后,通过如下过程
addStatementNodelets { statementParser.parseGeneralStatement { newMappedStatementConfig { delegate.addMappedStatement(mappedStatement) } }}
添加到 mappedStatements 时,进行了键值的重复判断
delegate.addMappedStatement(mappedStatement) { if (mappedStatements.containsKey(ms.getId())) { throw new SqlMapException("There is already a statement named " + ms.getId() + " in this SqlMap."); }}
如果想在 SQL XML 更改之后重新解析并写入到 mappedStatements,要把这个重复判断干掉,需要通过以下的这些空想步骤。
重写 addMappedStatement
就需要能介入 delegate
就需要能介入 delegate 初始化的地方,即介入 SqlMapClientImpl
就需要能介入 SqlMapClientImpl 初始化的地方,即介入 SqlMapClientFactoryBean
Google和Stackoverflow一通后,发现可以通过重写 delegate 等类的方式解决上述问题的,[3]是一个完整的实现,大神啊。
已有的方案
先正向的看看别人做了什么,可以用来抄袭
DySqlMapClientFactoryBean
在 SqlMapClientFactoryBean 的基础上进行了如下扩展:
初始化时将 configLocation、configLocations 保存下来,有何用?
重写了 buildSqlMapClient 方法,返回重写后的 DySqlMapClient
DySqlMapClient
对 SqlMapClientImpl 进行了如下扩展:
将超类初始化为 重写后的 DySqlMapExecutorDelegate
自行解析 configLocation || configLocations 对应的配置文件,生成 SQL.XML -> SQL_ID 映射关系
DySqlMapExecutorDelegate
对 SqlMapExecutorDelegate 进行了如下扩展:
addMappedStatement,ID值重复时 remove 掉
增加了 checkAndRefreshSqlMap 方法,用来检查和刷新 Map
重写各操作实现接口,增加 checkAndRefreshSqlMap 调用
DySqlMapParser
对 SqlMapParser 进行了如下扩展:
增加 resetSqlNodeLets,对 XmlParserState 进行重置。可能会引起 State 混乱
SqlMapConfigUtils
readSqlMapFileMapping,解析包含的 SQL XML 文件列表
readSqlMap,解析每个 SQL XML 中的 SQL ID
改进的方案
对[3]进行了测试,发现了一个比较重大的不同。我们的工程将 SQL XML 打包成了 jar,并且,即便是现成的文件 ,流程也变成了修改部署 war 包中的 XML 文件,而不是修改源文件,然后直接生效。所以现在面临了两个问题。
如何解决 jar 带来的文件操作
SqlMapFile 直接与源文件关联
编不下去了
周末撩妹时思考了一下之前遇到的两个问题
晚间溜达的时候想到的一出,回家实现勉强能用。
中心思想是搭个HTTP服务,将源文件doge.xml资源化为,iBatis刷新时通过文件名构造URL,读取HTTP资源并进行解析。
改造了一个http server
./http-server /src/main/resources/storage/sqlmap -p 59999 -j true
其实你也可以用
python -m SimpleHTTPServer 59999
弊端就是每次打开不同的工程得自己切到目录下敲命令启动服务器
事实上,如果你用 Intellij,默认配置下它会在 63342 端口下启动一个静态服务器
路径在
http://localhost:63342/your-project-home-/very-long-module-path/doge.xml
打完收工
[1] 深入分析 iBATIS 框架之系统架构与映射原理,
[2] ibatis源码学习(二)初始化和配置文件解析,
[3]