• Java EE开发平台随手记2——Mybatis扩展1


      今天来记录一下对Mybatis的扩展,版本是3.3.0,是和Spring集成使用,mybatis-spring集成包的版本是1.2.3,如果使用maven,如下配置:

    <properties>元素下添加

    1 <mybatis.version>3.3.0</mybatis.version>
    2 <mybatis.spring.version>1.2.3</mybatis.spring.version>
    View Code

    <dependencies>元素下添加

     1 <dependency>
     2   <groupId>org.mybatis</groupId>
     3   <artifactId>mybatis</artifactId>
     4   <version>${mybatis.version}</version>
     5 </dependency>
     6 <dependency>
     7   <groupId>org.mybatis</groupId>
     8   <artifactId>mybatis-spring</artifactId>
     9   <version>${mybatis.spring.version}</version>
    10 </dependency>
    View Code

      mybatis-spring的集成配置如下:

     1 <bean id="DialectDatabaseIdProvider" class="com.forms.beneform4j.core.dao.mybatis.provider.DialectDatabaseIdProvider"/>
     2     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:sqlSessionFactoryBeanName="sqlSessionFactory" p:basePackage="com.forms"
     3         p:annotationClass="org.springframework.stereotype.Repository"/> 
     4     <bean id="sqlSessionFactory" p:dataSource-ref="dataSource" class="com.forms.beneform4j.core.dao.mybatis.SqlSessionFactoryBeanForSpring"
     5         p:configLocation="classpath:mybatis/mybatis-config.xml" p:databaseIdProvider-ref="DialectDatabaseIdProvider">
     6         <property name="mapperLocations">
     7             <array>
     8                 <value>classpath*:sql-mapper/ds1/*.xml</value>
     9                 <value>classpath*:com/forms/beneform4j/**/*.ds1.xml</value>
    10             </array>
    11         </property>
    12     </bean> 
    View Code

    这里的p:databaseIdProvider-ref="DialectDatabaseIdProvider"算是我们的第一个扩展:

    扩展1:添加自定义的数据库ID提供者

      添加这个扩展的目的,是为了能够根据不同的数据库写不同的sql-mapper配置,比如如下的配置,就可兼容不同数据库:

     1 <choose>
     2     <when test="_databaseId == 'oracle'">
     3     </when>
     4     <when test="_databaseId == 'db2'">
     5     </when>
     6     <when test="_databaseId == 'mysql'">
     7     </when>
     8     <when test="_databaseId == 'oracle'">
     9     </when>
    10     <when test="_databaseId == 'ase'">
    11     </when>
    12     <when test="_databaseId == 'iq'">
    13     </when>
    14     <when test="_databaseId == 'h2'">
    15     </when>
    16 </choose>
    View Code

    那这里的oracle、db2字符串是怎么确定的呢?这就是名称为DialectDatabaseIdProvider的bean所做的事情了,基本上和Mybatis原生提供的VendorDatabaseIdProvider类似,根据数据源对象DataSource获取DatabaseMetaData,继而调用getDatabaseProductName获取产品名称,然后根据配置好的关键字确定数据库类型。可能读者会觉得这个扩展没多大必要,直接使用VendorDatabaseIdProvider就好了,的确如此,除了自己写的类有更大的控制权之外,没有增加多少功能。但DialectDatabaseIdProvider的实现是从数据库方言角度来做的,从这里可以引出第二个扩展:

    扩展2:实现物理分页

    Mybatis的分页查询是逻辑分页,这对于大多数应用是不适合的,于是需要实现物理分页。

    主要的实现步骤:

    1、添加数据库方言接口

     1 public interface IDialect {
     2     
     3     /**
     4      * 数据库类型
     5      */
     6     enum DBType {
     7         Oracle, DB2, H2, MySql, ASE, IQ
     8     }
     9     
    10     /**
    11      * 获取数据库类型
    12      * @return 数据库类型枚举常量
    13      */
    14     public DBType getType();
    15     
    16     /**
    17      * 获取可能的数据库驱动类名称
    18      * @return 驱动类名数组
    19      */
    20     public String[] getDriverClassNames();
    21     
    22     /**
    23      * 生成计算总记录数的SQL
    24      * @param sql 原始SQL
    25      * @return 计算总记录数的SQL
    26      */
    27     public String getTotalSql(String sql);
    28     
    29     /**
    30      * 获取查询指定范围记录的SQL
    31      * @param sql     原始SQL
    32      * @param offset  返回的开始记录索引
    33      * @param limit   查询的数据大小
    34      * @return 查询第(offset, offset + limit]条记录的SQL,索引从1开始
    35      */
    36     public String getScopeSql(String sql, long offset, int limit);
    37 }
    View Code

    不同的数据库具体怎么实现就不贴了;

    2、添加mybatis分页和物理分页的适配类

    1 public class PageAdapter extends RowBounds{
    2 
    3 }
    View Code

    在调用Mybatis的分页查询时,使用该适配类参数,例如:

    1 public <E> List<E> selectList(String sqlId, Object parameter, IPage page) {
    2         RowBounds adapter = new PageAdapter(page);
    3         return sqlSession.selectList(sqlId, parameter, adapter);
    4 }
    View Code

    这里IPage封装了分页对象,可以获取当前页数、分页大小等等。

    3、编写Mybatis插件,拦截sql执行,如果包含PageAdapter参数,则做分页逻辑处理

    (1)获取数据库方言接口的具体实现类,这里可以根据Connection对象识别、也可以使用ThreadLocal对象,还可以在适配类中添加方言对象传进来

    (2)如果需要计算总记录条数(分页对象中不包含总记录数),根据数据库方言,获取计算总记录条数的sql,然后执行,并将总计录条数反写会分页对象

    (3)根据数据库方言和分页对象,获取需要从数据库查询的当前页数据的sql,然后调用拦截器的执行方法继续之前执行

    4、将mybatis插件配置到spring配置文件或mybatis配置文件中,启用该插件即可。

      说到Mybatis插件,实际上并不只是做了分页处理一件事,还可以格式化sql、进行sql拦截处理等等,这里就包括我们的第三个扩展:

    扩展3:SQL拦截1——语句拦截

    主要的实现步骤:

    1、添加SQL字符串拦截接口

     1 public interface ISqlInterceptor {
     2     
     3     /**
     4      * 执行SQL拦截
     5      * @param jndi    数据源
     6      * @param src     原SQL
     7      * @param context 上下文环境
     8      * @param root    根对象
     9      * @return 拦截后的SQL
    10      */
    11     public String intercept(IJndi jndi, String src, Map<String, Object> context, Object root);
    12 }
    View Code

    这里的IJndi对象是数据源对象的一个抽象,可以获取相应的数据库方言对象、和数据源对应的特定属性等

     1 public interface IJndi{
     2 
     3     /**
     4      * 获取数据源名称
     5      * <p>
     6      *       默认为spring配置中的beanId
     7      * </p>
     8      * @return 数据源名称
     9      */
    10     public String getName();
    11     
    12     /**
    13      * 是否默认数据源
    14      * <p>
    15      *   默认情况下,如果spring配置中的beanId等于dataSource(不区分大小写)则设置为默认数据源,如果不存在dataSource,则设置第一个数据源为默认数据源
    16      * </p>
    17      * @return 是否默认数据源
    18      */
    19     public boolean isDefault();
    20     
    21     /**
    22      * 获取数据库方言
    23      * <p>
    24      *      根据数据库产品名称自动判断哪个数据库方言
    25      * </p>
    26      * @return 和数据源对应的数据库方言
    27      */
    28     public IDialect getDialect();
    29     
    30     /**
    31      * 获取数据源
    32      * @return 对应的数据源
    33      */
    34     public DataSource getDataSource();
    35     
    36     /**
    37      * 获取属性
    38      * @return
    39      */
    40     public Properties getProperties();
    41 }
    View Code

    2、在平台配置中,添加sql拦截的配置,可以配置一系列的拦截

    3、在Mybatis插件中,根据Mybatis执行上下文找到IJndi对象、参数对象、拦截前的sql,调用平台配置中的sql拦截,并将返回值反写会mybatis上下文中

      可能有人会问,这种语句拦截有什么作用呢?

      其实我们就是利用这种机制,来实现不同应用系统使用不同的表前缀,比如平台中有一个BF_USER表,A系统建表的时候统一添加A_前缀,B系统建表的时候统一添加前缀B_,那么,在平台中怎么知道访问的具体表呢?我们的做法是,在平台中添加一个平台表前缀的配置,然后实现一个SQL拦截,将符合特别语法{{BF}}的替换为Beneform4jConfig.getBeneform4jTablePrefix() + "BF",这种SQL语句的拦截,有点类似于使用${}语法。

    扩展4:SQL拦截2——参数拦截

      如果说SQL语句拦截是使用${}语法,那么参数拦截则是#{}语法了。

    主要实现步骤:

    1、添加参数拦截接口

     1 public interface IStatementParameterResolver {
     2 
     3     /**
     4      * 是否可以解析表达式
     5      * @param jndi  数据源对象
     6      * @param expression 表达式
     7      * @return 如果可以解析,返回true,否则返回false
     8      */
     9     public boolean isSupport(IJndi jndi, String expression);
    10     
    11     /**
    12      * 执行参数解析
    13      * @param jndi  数据源对象
    14      * @param parameterObject  参数对象
    15      * @param expression       表达式
    16      * @return 解析后的值
    17      */
    18     public Object resolver(IJndi jndi, Object parameterObject, String expression);
    19 }
    View Code

    这里有两个方法,第一个方法判断当前的拦截器实现类是否支持#{}内部的表达式,如果支持,就调用第二个方法处理参数。这种先判断是否支持,然后在支持的情况下再继续处理的类责任链模式在Spring MVC中用的也相当普通。

    2、编写Mybatis原生接口ParameterHandler的实现类,在其中调用参数拦截接口来实现不同参数的解析

    3、在Mybatis插件中,替换ParameterHandler接口的实现类。

      在新平台中,利用参数拦截主要实现了一种效果,就是在mybatis的sql-mapper文件中直接执行SpEL表达式或OGNL表达式。例如:

    1 <select id="selectList">
    2         SELECT * FROM BF_PARAM_ENUM_DEF 
    3         <where>
    4             <if test="null != paramCode and '' != paramCode">
    5             and PARAM_CODE = #{@bean.getName(), jdbctType=VARCHAR}
    6             </if>
    7         </where>
    8     </select>
    View Code

    这里的bean是Spring容器管理的bean,getBean就是调用其getBean方法了。

      今天记录到这里,下周继续。

  • 相关阅读:
    mysql——前面内容——前期整理笔记00
    mysql——触发器——概念
    mysql——触发器——示例
    mysql——多表——外连接查询——左连接、右连接、复合条件查询
    mysql——多表——合并查询结果
    IT职场人生系列之七:学外语
    IT职场人生系列之十三:技术?管理?业务?
    IT职场人生系列之五:怎样面试
    IT职场人生系列之十二:语言与技术I
    IT职场人生系列之四:怎样写简历
  • 原文地址:https://www.cnblogs.com/linjisong/p/5538311.html
Copyright © 2020-2023  润新知