念奴娇 赤壁怀古
     [北宋]苏轼
大江东去,浪淘尽,千古风流人物。
故垒西边,人道是,三国周郎赤壁。
乱石穿空,惊涛拍岸,卷起千堆雪。
江山如画,一时多少豪杰。

遥想公瑾当年,小乔初嫁了,雄姿英发。
羽扇纶巾,谈笑间,樯橹灰飞烟灭。
故国神游,多情应笑我,早生华发。
人生如梦,一樽还酹江月。

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>
View Code

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的元素使用;而且更重要的是,扩展提供了一种机制,上面的例子只是演示怎么引入自定义命名空间的元素,仅仅只是其中一个例子而已,开发人员尽可以发挥自己的聪明才智,自行扩展出更多实用的元素。

 

posted @ 2016-11-08 10:19  linjisong  阅读(810)  评论(0编辑  收藏  举报