Mybatis中SqlMapper配置的扩展与应用(2)
三、子表删除兼容问题
这个问题,使用SQL配置函数不太好处理,而且就算使用SQL配置函数,也不够直观,有点自动生成SQL的意味,太Hibernate了(不过要是可以兼收Hibernate和Mybatis两家之长,那也是一个不错的主意),下面我们使用自定义命名空间的方式来解决。
1、编写sqlmapper-extend命名空间的XSD文件,引进新的<db>元素
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://dysd.org/schema/sqlmapper-extend" targetNamespace="http://dysd.org/schema/sqlmapper-extend" xmlns:s="http://dysd.org/schema/sqlmapper" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" version="1.0"> <!-- 导入由mybatis官方DTD转换而来的XSD命名空间 --> <xsd:import namespace="http://dysd.org/schema/sqlmapper" schemaLocation="http://dysd.org/schema/sqlmapper.xsd"/> <xsd:element name="db"> <xsd:annotation><xsd:documentation><![CDATA[ 定义一个新的db元素,有一个枚举列表的属性type,表示数据库类型,以!开头表示否定 可以配置SQL文本、statement级元素、script级元素以及其它扩展空间元素等子元素 ]]></xsd:documentation></xsd:annotation> <xsd:complexType mixed="true"> <xsd:choice maxOccurs="unbounded"> <xsd:group ref="s:statementGroup" minOccurs="0" maxOccurs="1"/> <xsd:group ref="s:dynaScriptGroup" minOccurs="0" maxOccurs="1" /> </xsd:choice> <xsd:attribute name="type" use="required"> <xsd:simpleType> <xsd:list> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:enumeration value="!"/> <xsd:enumeration value="Oracle"/> <xsd:enumeration value="MySQL"/> <xsd:enumeration value="DB2"/> <xsd:enumeration value="H2"/> <xsd:enumeration value="SybaseASE"/> <xsd:enumeration value="SybaseIQ"/> </xsd:restriction> </xsd:simpleType> </xsd:list> </xsd:simpleType> </xsd:attribute> </xsd:complexType> </xsd:element> </xsd:schema>
2、将扩展命名空间和XSD文件路径配置到ini文件中
# 用于配置sqlmapper的命名空间 # SqlMapper命名空间 [http://dysd.org/schema/sqlmapper] schema = namespace/dysd-sqlmapper.xsd parser = org.dysd.dao.mybatis.schema.SchemaSqlMapperNamespaceParser # 扩展命名空间,因为不是根元素,所以不需要配置命名空间解析器 [http://dysd.org/schema/sqlmapper-extend] schema = namespace/dysd-sqlmapper-extend.xsd
3、编写db元素的解析处理器,因为db元素即可作为一级子元素(statement),也可作为脚本级元素(script),因此我们实现这两种处理器:
//Statement级处理器实现类 public class DbStatementHandler extends StatementHandlerSupport{ @Override public void handleStatementNode(Configuration configuration, SchemaSqlMapperParserDelegate delegate, XNode node) { if(SchemaHandlers.isMatch(configuration.getDatabaseId(), node.getStringAttribute("type"))){ delegate.doParseStatements(node.getNode()); } } } //Script级处理器实现类 public class DbScriptHandler extends ScriptHandlerSupport { @Override public void handleScriptNode(Configuration configuration, XNode node, List<SqlNode> targetContents) { if(!SchemaHandlers.isMatch(configuration.getDatabaseId(), node.getStringAttribute("type"))){ List<SqlNode> contents = parseDynamicTags(configuration, node); final MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); targetContents.add(new SqlNode(){ @Override public boolean apply(DynamicContext context) { mixedSqlNode.apply(context); return true; } }); } } } //SchemaHandlers中的帮助方法 public static boolean isMatch(String databaseId, String allowDatabaseIds){ if(!Tool.CHECK.isBlank(databaseId) && !Tool.CHECK.isBlank(allowDatabaseIds)){ String[] allows = allowDatabaseIds.split("\\s+");//使用空白字符分隔 boolean mode = !"!".equals(allows[0]);//是否肯定模式 if(mode){//肯定模式 for(int i = 0, l = allows.length; i < l; i++){ if(databaseId.equalsIgnoreCase(allows[i])){ return true; } } }else{// 否定模式,从第1项开始匹配 for(int i = 1, l = allows.length; i < l; i++){ if(databaseId.equalsIgnoreCase(allows[i])){ return false; } } return true; } } return false; }
4、在SchemaHandlers中注册实现类
// 注册自定义命名空间的处理器 registerExtend("db", new DbStatementHandler(), new DbScriptHandler());
5、修改SqlMapper中配置,为了不实际执行数据库操作,这里只以脚本级的查询语句为例
<?xml version="1.0" encoding="UTF-8" ?> <mapper xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://dysd.org/schema/sqlmapper" xmlns:e="http://dysd.org/schema/sqlmapper-extend" xsi:schemaLocation="http://dysd.org/schema/sqlmapper http://dysd.org/schema/sqlmapper.xsd http://dysd.org/schema/sqlmapper-extend http://dysd.org/schema/sqlmapper-extend.xsd" namespace="org.dysd.dao.mybatis.schema.IExampleDao"> <select id="selectString" resultType="string"> select PARAM_NAME from BF_PARAM_ENUM_DEF <e:db type="MySQL"> <if test="null != paramName and '' != paramName"> where PARAM_NAME $like{#{paramName, jdbcType=VARCHAR}} </if> </e:db> <e:db type="! MySQL"> <if test="null != paramName and '' != paramName"> where PARAM_CODE = 'DISPLAY_AREA' </if> </e:db> </select> </mapper>
需要注意,这里引入了两个命名空间。
6、执行测试
(1)H2
20161108 09:48:37,738 [main]-[DEBUG] ==> Preparing: select PARAM_NAME from BF_PARAM_ENUM_DEF where PARAM_NAME LIKE '%'||?||'%'
(2)MySQL
20161108 09:50:36,453 [main]-[DEBUG] ==> Preparing: select PARAM_NAME from BF_PARAM_ENUM_DEF where PARAM_CODE = 'DISPLAY_AREA'
从where条件可知已经实现预期的效果。
可能有人会说,其实根本不用麻烦,直接使用mybatis的<if>或<choose>,在其中判断_databaseId参数的值就可以。虽然mybatis可以实现这种情况,但需要开发人员记住_databaseId这个参数名,并且<if>或<choose>只能用作脚本级Script的元素使用,不能作为语句级Statement的元素使用;而且更重要的是,扩展提供了一种机制,上面的例子只是演示怎么引入自定义命名空间的元素,仅仅只是其中一个例子而已,开发人员尽可以发挥自己的聪明才智,自行扩展出更多实用的元素。
拣尽寒枝不肯栖,寂寞沙洲冷。
郴江幸自绕郴山,为谁流下潇湘去?
欲将心事付瑶琴,知音少,弦断有谁听?
倩何人,唤取红巾翠袖,揾英雄泪!
零落成泥碾作尘,只有香如故!
郴江幸自绕郴山,为谁流下潇湘去?
欲将心事付瑶琴,知音少,弦断有谁听?
倩何人,唤取红巾翠袖,揾英雄泪!
零落成泥碾作尘,只有香如故!