iBatis3基于方言(Dialect)

发布时间:2019-07-08 18:00:57编辑:auto阅读(1596)

    (注:以下代码是基于ibatis3 beta4的扩展,ibatis3正式版如果实现改变,将会继续跟进修改)

    iBatis3默认使用的分页是基于游标的分页,而这种分页在不同的数据库上性能差异不一致,最好的办法当然是使用类似hibernate的基于方言(Dialect)的物理分页功能。

    iBatis3现在提供插件功能,通过插件我们可以编写自己的拦截器来拦截iBatis3的主要执行方法来完成相关功能的扩展。

    能够拦截的的类如下:

    Java代码 收藏代码
    Executor
    (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
    ParameterHandler
    (getParameterObject,setParameters)
    ResultSetHandler
    (handleResultSets,handleOutputParameters)
    StatementHandler
    (prepare,parameterize,batch,update,query)

    但此次我们感兴趣的是Executor.query()方法,查询方法最终都是执行该方法。

    该方法完整是:

    Java代码 收藏代码
    Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

    分页方言的基本实现:

    以Mysql数据库示例,即有一个方法 query(sql,offset,limit);

    Java代码 收藏代码
    query("select * from user",5,10);

    经过我们的拦截器拦截处理,变成

    Java代码 收藏代码
    query("select * from user limit 5,10",0,0);

    1.拦截类实现:

    Java代码 收藏代码
    @Intercepts({@Signature(
    type= Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
    public class OffsetLimitInterceptor implements Interceptor{
    static int MAPPED_STATEMENT_INDEX = 0;
    static int PARAMETER_INDEX = 1;
    static int ROWBOUNDS_INDEX = 2;
    static int RESULT_HANDLER_INDEX = 3;

    Dialect dialect;  
    
    public Object intercept(Invocation invocation) throws Throwable {  
        processIntercept(invocation.getArgs());  
        return invocation.proceed();  
    }  
    
    void processIntercept(final Object[] queryArgs) {  
        //queryArgs = query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)  
        MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];  
        Object parameter = queryArgs[PARAMETER_INDEX];  
        final RowBounds rowBounds = (RowBounds)queryArgs[ROWBOUNDS_INDEX];  
        int offset = rowBounds.getOffset();  
        int limit = rowBounds.getLimit();  
    
        if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {  
            BoundSql boundSql = ms.getBoundSql(parameter);  
            String sql = boundSql.getSql().trim();  
            if (dialect.supportsLimitOffset()) {  
                sql = dialect.getLimitString(sql, offset, limit);  
                offset = RowBounds.NO_ROW_OFFSET;  
            } else {  
                sql = dialect.getLimitString(sql, 0, limit);  
            }  
            limit = RowBounds.NO_ROW_LIMIT;  
    
            queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset,limit);  
            BoundSql newBoundSql = new BoundSql(sql, boundSql.getParameterMappings(), boundSql.getParameterObject());  
            MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));  
            queryArgs[MAPPED_STATEMENT_INDEX] = newMs;  
        }  
    }  
    
    private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {  
        Builder builder = new MappedStatement.Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());  
        builder.resource(ms.getResource());  
        builder.fetchSize(ms.getFetchSize());  
        builder.statementType(ms.getStatementType());  
        builder.keyGenerator(ms.getKeyGenerator());  
        builder.keyProperty(ms.getKeyProperty());  
        builder.timeout(ms.getTimeout());  
        builder.parameterMap(ms.getParameterMap());  
        builder.resultMaps(ms.getResultMaps());  
        builder.cache(ms.getCache());  
        MappedStatement newMs = builder.build();  
        return newMs;  
    }  
    
    public Object plugin(Object target) {  
        return Plugin.wrap(target, this);  
    }  
    
    public void setProperties(Properties properties) {  
        String dialectClass = new PropertiesHelper(properties).getRequiredString("dialectClass");  
        try {  
            dialect = (Dialect)Class.forName(dialectClass).newInstance();  
        } catch (Exception e) {  
            throw new RuntimeException("cannot create dialect instance by dialectClass:"+dialectClass,e);  
        }   
        System.out.println(OffsetLimitInterceptor.class.getSimpleName()+".dialect="+dialectClass);  
    }  
    
    public static class BoundSqlSqlSource implements SqlSource {  
        BoundSql boundSql;  
        public BoundSqlSqlSource(BoundSql boundSql) {  
            this.boundSql = boundSql;  
        }  
        public BoundSql getBoundSql(Object parameterObject) {  
            return boundSql;  
        }  
    }  

    }

    2.ibatis3配置文件内容:

    Xml代码 收藏代码
    <configuration>
    <plugins>
    <!-- 指定数据库分页方言Dialect, 其它方言:OracleDialect,SQLServerDialect,SybaseDialect,DB2Dialect,PostgreSQLDialect,MySQLDialect,DerbyDialect-->
    <plugin interceptor="cn.org.rapid_framework.ibatis3.plugin.OffsetLimitInterceptor">
    <property name="dialectClass" value="cn.org.rapid_framework.jdbc.dialect.MySQLDialect"/>
    </plugin>
    </plugins>

    <environments default="development">  
        <environment id="development">  
            <transactionManager type="JDBC" />  
            <dataSource type="POOLED">  
                <property name="driver" value="com.mysql.jdbc.Driver" />  
                <property name="url" value="jdbc:mysql://localhost:3306/test" />  
                <property name="username" value="root" />  
                <property name="password" value="123456" />  
            </dataSource>  
        </environment>  
    </environments>  
    
    <mappers>  
        <mapper resource="com/company/project/model/mapper/BlogMapper.xml" />  
    </mappers>  

    </configuration>

    3.MysqlDialect实现

    Java代码 收藏代码
    public class MySQLDialect extends Dialect{

    public boolean supportsLimitOffset(){  
        return true;  
    }  
    
    public boolean supportsLimit() {     
        return true;     
    }    
    
    public String getLimitString(String sql, int offset, int limit) {  
            return getLimitString(sql,offset,String.valueOf(offset),limit,String.valueOf(limit));  
    }  
    
    public String getLimitString(String sql, int offset,String offsetPlaceholder, int limit, String limitPlaceholder) {  
        if (offset > 0) {     
            return sql + " limit "+offsetPlaceholder+","+limitPlaceholder;   
        } else {     
            return sql + " limit "+limitPlaceholder;  
        }    
    }     

    }

    其它数据库的Dialect实现请查看:

    http://rapid-framework.googlecode.com/svn/trunk/rapid-framework/src/rapid_framework_common/cn/org/rapid_framework/jdbc/dialect/

    4.分页使用

    直接调用SqlSession.selectList(statement, parameter, new RowBounds(offset,limit))即可使用物理分页

    5.存在的问题

    现不是使用占位符的方式(即不是“limit ?,?”而是使用“limit 8,20”)使用分页

    完整代码可以查看即将发布的rapid-framework 3.0的ibatis3插件

关键字

上一篇: Python: xml转json

下一篇: 2017.12.29 3周5次课