实现ibatis手动控制加载sqlmap文件,终于不用重启应用了
来源:互联网 发布:java开发分几种 编辑:程序博客网 时间:2024/05/16 10:04
大学毕业之后到公司,就是velocity+springMVC+srping+ibatis,所以一直在用ibatis做持久层,其他的几个框架也都是稍有了解。
好了屁话少说进入正题:之前有写一篇文章 《java webapp嵌入jetty》 为的就是能快速开发,直接在eclipse做debug很是方便。但是呢,用了ibatis,在sqlmap中写了sql,如果每次修改了sqlmap,那么就要每次都重启应用才行,使用起来很是蛋疼,如果项目小,也就是分分钟的事,如果工程足够大,那么重启一次就够受了!
于是就在考虑,能否每次手动来控制ibatis重新加载已经修改好的sqlmap呢?这就可以不用重启了。
答案肯定是可以的~ 毕竟在spring做bean初始化的时候就会加载ibatis的sqlmap,所以只要我们找到对应的代码,然后做出一些调整就可以实现重新加载了。
OK,看代码~~
首先SqlmapClientFactoryBean是spring给ibatis做的适配,那么我们就要从这个类看起。
可以看到ibatis的sqlmapClient是通过configParser返回的。
在new SqlMapConfigParser()的时候,由于以下的引用关系。
SqlMapConfigParser--XmlParserState--SqlMapConfiguration---SqlMapExecutorDelegate
SqlMapConfigParser会将上面的类初始化,并且在SqlMapConfiguration初始化的时候,将new的SqlMapExecutorDelegate赋值给SqlMapClientImpl而SqlMapExecutorDelegate才是真正的执行代理类,并且所有的sqlmap解析都被它保存着。
解析过程不详细表述了,感兴趣的同学可以看源码。
最终通过SqlMapExecutorDelegate.addMappedStatement方法,把解析出来的sqlmapstatement保存到map中sqlmap的id为key值
代码如上,可以看到每次添加会验证id是否重复。这也就是为啥一个sql中如果有两个相同id就会报错的原因。
好了!代码看清楚了,接下来就看我们怎么来修改代码了。
主要就是SqlmapClientFactoryBean、SqlMapExecutorDelegate、SqlMapClientImpl三个类
我们想重新刷新,那么就要操作SqlMapExecutorDelegate中的map,然后ibatis和spring的接口只有SqlMapClientImpl这个类,并且spring的适配是SqlmapClientFactoryBean
于是乎,思路就来了,只要我们重写SqlmapClientFactoryBean让它返回我们重写过的SqlMapClientImpl,在SqlMapClientImpl添加刷新的方法,然后通过SqlMapClientImpl来调用我们代理的SqlMapExecutorDelegate就可以实现重新加载了~
代码如下:
SqlmapClientFactoryBean
在添加返回前,声明出我们重写的对sqlmapClientImpl的代理
package com.h2o3.right.dal.platform;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Field;import java.util.Properties;import org.springframework.core.NestedIOException;import org.springframework.core.io.Resource;import com.ibatis.sqlmap.client.SqlMapClient;import com.ibatis.sqlmap.engine.builder.xml.SqlMapConfigParser;import com.ibatis.sqlmap.engine.builder.xml.XmlParserState;import com.ibatis.sqlmap.engine.config.SqlMapConfiguration;import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl;/** * sqlMapClientImpl的代理类。 * * <pre> * 这个类是所有sql操作的入口,并且存放了SqlMapExecutorDelegate这个sql执行器的代理类。 * 并且这个类是在spring中的入口。 * * 所以代理这个类,将自己的delegate设置进去,并且反射自己的delegate到SqlMapConfiguration中 * 保证重新加载sqlmap的时候,操作的是自己的delegate,这样就不会触发原生delegate中的重复判断。 * </pre> * * @author yuezhen * */public class H2o3SqlMapClientImpl extends SqlMapClientImpl {/** * Delegate for SQL execution */public H2o3SqlMapExecutorDelegate h2o3Delegate;/** * sqlmap的路径 */private Resource[] configLocations;/** * config转换器 */private SqlMapConfigParser configParser;/** * sqlmapclient配置的properties,spring传入 */private Properties properties;/** * 构造方法。 * * @param client * @param configLocations * @param configParser * @param properties */public H2o3SqlMapClientImpl(SqlMapClient client,Resource[] configLocations, SqlMapConfigParser configParser,Properties properties) {super(new H2o3SqlMapExecutorDelegate(((ExtendedSqlMapClient) client).getDelegate()));this.h2o3Delegate = (H2o3SqlMapExecutorDelegate) this.delegate;this.configLocations = configLocations;this.configParser = configParser;this.properties = properties;relfectDelegate();}/** * 重新刷新。 * * @throws IOException */public void fresh() throws IOException {// 调用configParser来重新加载for (Resource configLocation : configLocations) {InputStream is = configLocation.getInputStream();try {configParser.parse(is, properties);} catch (RuntimeException ex) {throw new NestedIOException("Failed to parse config resource: "+ configLocation, ex.getCause());}}}/** * 反射将自己的delegate,反射到SqlMapConfiguration中。 */public void relfectDelegate() {try {Field stateField = this.configParser.getClass().getDeclaredField("state");stateField.setAccessible(true);XmlParserState state = (XmlParserState) stateField.get(this.configParser);Field configFiled = state.getClass().getDeclaredField("config");configFiled.setAccessible(true);SqlMapConfiguration configField = (SqlMapConfiguration) configFiled.get(state);Field clientField = configField.getClass().getDeclaredField("client");clientField.setAccessible(true);clientField.set(configField, this);Field delegateField = configField.getClass().getDeclaredField("delegate");delegateField.setAccessible(true);delegateField.set(configField, this.delegate);} catch (Exception e) {e.printStackTrace();}}public H2o3SqlMapExecutorDelegate getMydelegate() {return h2o3Delegate;}}
代理中做保存自己所写的SqlMapExecutorDelegate代理,并且把自己的SqlMapExecutorDelegate通过反射的方式放到SqlMapConfiguration中,以保证从新加载的时候用的是我们的SqlMapExecutorDelegate。
并且实现刷新的方法:将传入的sqlmap路径,在调用一次parser的解析。
在SqlMapExecutorDelegate方法中,由于这个类太多不可见,没办法,只能很土的方法,一是写个代理,代理他所有的方法,并且重写addmapping的方法。二是通过反射把所有的属性反射进去。然后重写addmapping。
这里采用第一种方案
将以前的重复判断给去掉。
最后,web层做一个刷新的controller,将sqlmapclient注入进去,然后通过refesh方法来进行刷新即可。
这样,每次修改了sql之后,访问一下refersh.htm就重新加载了sqlmap!!
这里只是实现通过url手动刷新,如果大家感兴趣,还可以设置,没法访问db都去刷新,方法类似,都是修改SqlMapExecutorDelegate这个类的不同调用方法即可。
已经测试通过,ibatis基于org/apache/ibatis/ibatis-sqlmap/2.3.4.726/ibatis-sqlmap-2.3.4.726-sources.jar
附上四个文件下载链接 http://download.csdn.net/detail/lywybo/5613303
H2o3SqlMapClientFactoryBean.java
H2o3SqlMapClientImpl.java
H2o3SqlMapExecutorDelegate.java
RefershController.java
使用的时候将文件放到代码中,然后配置sqlmapclient为H2o3SqlMapClientFactoryBean即可。
然后配合上篇文章 《java webapp嵌入jetty》 来启动,一个很方便的开发环境就做好了。
等过几天有空,会把这个空的框架搭起来,方便大家使用。
- 实现ibatis手动控制加载sqlmap文件,终于不用重启应用了
- 修改Java文件后终于不用重启服务器了
- 修改Java文件后终于不用重启服务器了.
- 修改Java文件后终于不用重启服务器了.
- maven_jetty_jrebel,修改Java文件后终于不用重启服务器了
- 实现ibatis的动态加载sqlmap配置文件
- 实现ibatis的动态加载sqlmap配置文件 .
- sqlmap namespace ibatis 加载
- Ibatis SqlMap文件(转)
- iBatis SqlMap文件配置
- IBatis SqlMap解析及加载
- 终于不用上学了
- 终于不用裸奔了
- 不用重启服务器自动加载项目
- eclpise热加载,不用重启tomcate
- Eclipse修改代码,不用手动重启Tomcat
- ibatis sqlmap文件中文注释注意事项
- iBatis.Net的SqlMap.config文件
- Peacock数据统计优化
- 在Windows64位环境下.net访问Oracle解决方案
- oracle 一次诊断和解决CPU利用率高的问题分析
- android 调试环境
- 内存分配问题
- 实现ibatis手动控制加载sqlmap文件,终于不用重启应用了
- Java 加载Properties文件
- 供意图转战手机平台的掌机开发者参考的10点建议
- 电商打通金融链 是"颠覆"银行还是另辟蹊径
- 在mapreduce任务中使用distributedCache
- PHP里的字符串定义小技巧汇总
- 做好人好事不吃亏,转转,传递正能量
- Linux、Windows共享目录,一改全改
- 高级Bash脚本编程指南(24):时间/日期 命令