小站重新装修,欢迎到访。
标签 #Spring

Omad构建期Mybatis Mapper文件语法检查

去年是忙碌的一年,今年陆续总结回顾下吧。

我目前所在的项目,使用了Mybatis做ORM,SQL是写到Mapper配置里,所以就导致一个问题,Mapper的语法无法在编译期被检查。而且Mapper错误在运行时的提醒非常晦涩,很难快速定位解决(比如resultType写成了resultMap,服务会不停的重新加载,直到OOM)。特别是开发比较紧张的时期,如果有人写了有问题的Mapper部署到开发服务器上,从发现到解决问题几个回合下来,浪费的不只是一个人的时间,被阻塞的有可能是整个研发团队。

我于是思考能否在发布前发现类似问题。翻了下资料,Mybaits没有相关检查的工具类,没办法,自己动手,丰衣足食。

细细想一下,首先是时机问题,编译期是不用考虑了,那么夹在编译期和运行时中间的只有一个构建期,我们项目用了我厂的omad发布系统,构建脚本是ant写的,想到这思路突然就有了:我在构建期启动一下看报不报错不就齐活了?

于是着手研究错误Mapper导致的异常栈,走读了相关Spring代码,发现第一个DAO方法的调用会触发所有Mapper文件的检查,那么事情就更简单了。

首先是修改构建脚本,在编译打包完成后主动调用一个main函数,启动一下Mybatis相关的部分:

1
2
3
4
<target name="check-mapper">
<echo message="Now to check mybatis mapper files"/>
<exec failonerror="true" command="java -cp ${basedir}/compressed/WEB-INF/lib/*:${basedir}/compressed/WEB-INF/classes com.netease.ysf.device.util.CompileTimeMapperChecker"/>
</target>

这里failonerror设置为true ,把这个target放到整个打包的最后一步:

1
2
3
4
5
6
7
<target name="deploy">
<echo message="begin auto deploy......"/>
<antcall target="compile"/>
<antcall target="compress-web"/>
<antcall target="cleanTarget"/>
<antcall target="check-mapper"/> <!--我在这里-->
</target>

然后最核心的问题就是这个函数该怎么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CompileTimeMapperChecker {
public static void main(String[] args) {
try {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:config/spring-for-mapper-checker.xml");
context.start();
context.getBean(AccountDAO.class).query(0L);
} catch (Exception e) {
System.out.println(e.getClass());
String msg = e.getMessage();
if (msg.contains("CannotGetJdbcConnectionException")) {
System.exit(0);
} else {
System.out.println(e.getMessage());
System.exit(-1);
}
}
}
}

如上,短小精悍,写一个Mapper检查专用的Spring配置,启动上下文后主动调用一次DAO访问,然后检查只要不是CannotGetJdbcConnectionException,就认为必然出了问题,而且极大可能是Mapper的语法不正确。

这里之所以放过CannotGetJdbcConnectionException是因为omad打包是在独立的打包机执行的,这机器必然没有我们数据库的访问权限,而且只有Mapper检查无误后才会执行真正的DAO操作,也就意味着,它能抛出CannotGetJdbcConnectionException的话,Mapper的检查就是通过的。

最后看一下专用的Spring配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="driverClassName"/>
<property name="url" value="url"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
<property name="defaultAutoCommit" value="false"/>
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:config/mybatis.xml"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.netease.ysf.device.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>

注意dataSource的配置,参数的value部分随便填填就好了。

就是这么简单,我只修改了开发环境几个重要工程的打包脚本,添加了这步Mapper文件检查,其他环境也不需要浪费这个时间。后来这几段代码屡立战功,数次把错误的发布挡在运行之前。

1

博主是一个不是很聪明的码农。完美主义者,强迫症中期。这里会记录一些回忆和点滴,以博为镜。

武器库:

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-SA 3.0  授权发布。最后生成于 2017-02-20.