项目使用mybatis操作数据库最初支持的数据库是oracle,后来需求要同时支持mysql问题来了,oracle和mysql或多或少有差别,最初的设计没有考虑到兼容支持多种数据库,设计上严重依赖oracle(比如序列生成主键)之前《mybatis Oracle到Mysql迁移 记录》有稍微记录了一下,使用的方法是:1)建表和函数|存储过程 模拟oracle的sequence,2)使用spring profile切换配置,一个项目里有2套xml组长说这样不行2套xml太麻烦但是用VendorDatabaseIdProvider另外写有差异的sql又有点乱说要弄一个规范,执行的时候再按照数据库类型生成sql那意思就是要做编译,一个自定义的sql语法转换成特定数据库的sql语法?这样有2个地方可以进行修改:一种SqlSessionFactoryBean生产configuration后的mappingstatements里修改,一种mybatis的StatementHandle或Executor 的插件拦截能力有限写不出性能很好的编译器,使用第二种的话更影响执行速度第一种的话mappingstatments, sqlNode的字段都是私有需要反射,强行不好。
然后点来点去点来点去点到xml,瞄到动态标签和ognl白想那么多了,可以使用动态标签静态函数生成特定的sql片段但是mybatis不支持自定义标签,需要改源码使用foreach标签比较繁琐还是可以用的${item} (可能还有其他比较简洁的写法,试了几种目前没想到)最后还是决定自定义一个标签再搭配一些sql转换的静态方法 关于mybatis解析sql过程可以参考 这篇文章 目标: 自定义标签 返回表达式的字符串1)修改org.apache.ibatis.builder.xml下的mybatis-3-mapping.dtd,加入value的校验规则,参看include标签的规则定义下面是片段2)实现接口org.apache.ibatis.scripting.xmltags.SqlNode ValueSqlNode 翻译处理value标签。
public class ValueSqlNode implements SqlNode { private final String expression; public ValueSqlNode(String expression) { this.expression=expression; } @Override public boolean apply(DynamicContext context) { context.appendSql(OgnlCache.getValue(expression, context.getBindings()).toString()); return true; }}3)XMLScriptBuilder添加nodeHandler NodeHandler nodeHandlers(String nodeName) { Map map = new HashMap(); map.put("trim", new TrimHandler()); map.put("where", new WhereHandler()); map.put("set", new SetHandler()); map.put("foreach", new ForEachHandler()); map.put("if", new IfHandler()); map.put("choose", new ChooseHandler()); map.put("when", new IfHandler()); map.put("otherwise", new OtherwiseHandler()); map.put("bind", new BindHandler()); map.put("value",new ValueHandler()); return map.get(nodeName); } private class ValueHandler implements NodeHandler { public ValueHandler() { // Prevent Synthetic Access 求问这个作用? } @Override public void handleNode(XNode nodeToHandle, List targetContents) { final String expression = nodeToHandle.getStringAttribute("expr"); final ValueSqlNode node = new ValueSqlNode(expression); targetContents.add(node); } }4)一些sql转换的静态方法。
代码也很简单: id="xx" flushCache="true" parameterType="java.util.List" > INSERT INTO xxxx(xxx,xxx) values (#{item.xxxx},#{item.xxxx}) 字符串连接JS.SCHEMA_CODE like '%'||#{schemaCode}||'%'改成JS.SCHEMA_CODE like 关键字处理T."ORDER"改成T.返回记录个数限制AND ROWNUM<2改成ROWNUM<2改成获取序列下一个值SEQ_FILE_ID.NEXTVAL改成获取序列下n个值SELECT SEQ_OPERATE_PARAMS_ID.NEXTVAL FROM DUAL CONNECT BY LEVEL<=#{n}改成日期格式to_date( #{qRuntimeLastBegin},'yyyy-MM-dd HH24:mi:ss')改成to_date( #{qRuntimeLastBegin},'yyyy-MM-dd')改成当前日期sysdate改成项目使用mybatis操作数据库。
最初支持的数据库是oracle,后来需求要同时支持mysql问题来了,oracle和mysql或多或少有差别,最初的设计没有考虑到兼容支持多种数据库,设计上严重依赖oracle(比如序列生成主键)之前《mybatis Oracle到Mysql迁移 记录》有稍微记录了一下,使用的方法是:1)建表和函数|存储过程 模拟oracle的sequence,2)使用spring profile切换配置,一个项目里有2套xml组长说这样不行2套xml太麻烦但是用VendorDatabaseIdProvider另外写有差异的sql又有点乱说要弄一个规范,执行的时候再按照数据库类型生成sql那意思就是要做编译,一个自定义的sql语。