mysql 影子表_Mybatis通过Interceptor来简单实现影子表进行动态sql读取和写入

首先进行Mybatis 拦截器介绍

拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。打个比方,对于Executor,Mybatis中有几种实现:BatchExecutor、ReuseExecutor、SimpleExecutor和CachingExecutor。这个时候如果你觉得这几种实现对于Executor接口的query方法都不能满足你的要求,那怎么办呢?是要去改源码吗?当然不。我们可以建立一个Mybatis拦截器用于拦截Executor接口的query方法,在拦截之后实现自己的query方法逻辑,之后可以选择是否继续执行原来的query方法。

对于拦截器Mybatis为我们提供了一个Interceptor接口,通过实现该接口就可以定义我们自己的拦截器。我们先来看一下这个接口的定义:

package org.apache.ibatis.plugin;

import java.util.Properties;

public interface Interceptor {

Object intercept(Invocation invocation) throws Throwable;

Object plugin(Object target);

void setProperties(Properties properties);

}

org.apache.ibatis.plugin;

import java.util.Properties;

public interface Interceptor {

Object intercept(Invocation invocation) throws Throwable;

Object plugin(Object target);

void setProperties(Properties properties);

}

我们可以看到在该接口中一共定义有三个方法,intercept、plugin和setProperties。plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法,当然也可以调用其他方法,这点将在后文讲解。setProperties方法是用于在Mybatis配置文件中指定一些属性的。

定义自己的Interceptor最重要的是要实现plugin方法和intercept方法,在plugin方法中我们可以决定是否要进行拦截进而决定要返回一个什么样的目标对象。而intercept方法就是要进行拦截的时候要执行的方法。

对于plugin方法而言,其实Mybatis已经为我们提供了一个实现。Mybatis中有一个叫做Plugin的类,里面有一个静态方法wrap(Object target,Interceptor interceptor),通过该方法可以决定要返回的对象是目标对象还是对应的代理。

我们先看一下Plugin的wrap方法,它根据当前的Interceptor上面的注解定义哪些接口需要拦截,然后判断当前目标对象是否有实现对应需要拦截的接口,如果没有则返回目标对象本身,如果有则返回一个代理对象。而这个代理对象的InvocationHandler正是一个Plugin。所以当目标对象在执行接口方法时,如果是通过代理对象执行的,则会调用对应InvocationHandler的invoke方法,也就是Plugin的invoke方法。所以接着我们来看一下该invoke方法的内容。这里invoke方法的逻辑是:如果当前执行的方法是定义好的需要拦截的方法,则把目标对象、要执行的方法以及方法参数封装成一个Invocation对象,再把封装好的Invocation作为参数传递给当前拦截器的intercept方法。如果不需要拦截,则直接调用当前的方法。Invocation中定义了定义了一个proceed方法,其逻辑就是调用当前方法,所以如果在intercept中需要继续调用当前方法的话可以调用invocation的procced方法。

这就是Mybatis中实现Interceptor拦截的一个思想,如果用户觉得这个思想有问题或者不能完全满足你的要求的话可以通过实现自己的Plugin来决定什么时候需要代理什么时候需要拦截。以下讲解的内容都是基于Mybatis的默认实现即通过Plugin来管理Interceptor来讲解的。

对于实现自己的Interceptor而言有两个很重要的注解,一个是@Intercepts,其值是一个@Signature数组。@Intercepts用于表明当前的对象是一个Interceptor,而@Signature则表明要拦截的接口、方法以及对应的参数类型。来看一个自定义的简单Interceptor:

首先看setProperties方法,这个方法在Configuration初始化当前的Interceptor时就会执行,这里只是简单的取两个属性进行打印。

其次看plugin方法中我们是用的Plugin的逻辑来实现Mybatis的逻辑的。

接着看MyInterceptor类上我们用@Intercepts标记了这是一个Interceptor,然后在@Intercepts中定义了两个@Signature,即两个拦截点。第一个@Signature我们定义了该Interceptor将拦截Executor接口中参数类型为MappedStatement、Object、RowBounds和ResultHandler的query方法;第二个@Signature我们定义了该Interceptor将拦截StatementHandler中参数类型为Connection的prepare方法。

最后再来看一下intercept方法,这里我们只是简单的打印了一句话,然后调用invocation的proceed方法,使当前方法正常的调用。

对于这个拦截器,Mybatis在注册该拦截器的时候就会利用定义好的n个property作为参数调用该拦截器的setProperties方法。之后在新建可拦截对象的时候会调用该拦截器的plugin方法来决定是返回目标对象本身还是代理对象。对于这个拦截器而言,当Mybatis是要Executor或StatementHandler对象的时候就会返回一个代理对象,其他都是原目标对象本身。然后当Executor代理对象在执行参数类型为MappedStatement、Object、RowBounds和ResultHandler的query方法或StatementHandler代理对象在执行参数类型为Connection的prepare方法时就会触发当前的拦截器的intercept方法进行拦截,而执行这两个接口对象的其他方法时都只是做一个简单的代理。

册拦截器是通过在Mybatis配置文件中plugins元素下的plugin元素来进行的。一个plugin对应着一个拦截器,在plugin元素下面我们可以指定若干个property子元素。Mybatis在注册定义的拦截器时会先把对应拦截器下面的所有property通过Interceptor的setProperties方法注入给对应的拦截器。所以,我们可以这样来注册我们在前面定义的MyInterceptor:

Mybatis拦截器只能拦截四种类型的接口:Executor、StatementHandler、ParameterHandler和ResultSetHandler。这是在Mybatis的Configuration中写死了的,如果要支持拦截其他接口就需要我们重写Mybatis的Configuration。Mybatis可以对这四个接口中所有的方法进行拦截。

下面将介绍一个Mybatis拦截器的实际应用。Mybatis拦截器常常会被用来进行分页处理。我们知道要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis在执行Sql语句前也会产生一个包含Sql语句的Statement对象,而且对应的Sql语句是在Statement之前产生的,所以我们就可以在它成Statement之前对用来生成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的prepare方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用StatementHandler对象的prepare方法,即调用invocation.proceed()。更改Sql语句这个看起来很简单,而事实上来说的话就没那么直观,因为包括sql等其他属性在内的多个属性都没有对应的方法可以直接取到,它们对外部都是封闭的,是对象的私有属性,所以这里就需要引入反射机制来获取或者更改对象的私有属性的值了。对于分页而言,在拦截器里面我们常常还需要做的一个操作就是统计满足当前条件的记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。

下面是一个简单的实现影子表切换的功能:数据库mysql8.0.12,数据库连接池:Druid1.1.10,mybatis版本3.4.6,springboot版本2.0.3,使用mybatis-plus插件版本3.0.7.1

主要数据库相关pom文件如下:

mysql

mysql-connector-java

8.0.12

org.mybatis.spring.boot

mybatis-spring-boot-starter

1.3.2

org.mybatis

mybatis

org.mybatis

mybatis-spring

com.alibaba

druid

1.1.10

org.springframework.boot

spring-boot-starter-jdbc

com.baomidou

mybatisplus-spring-boot-starter

1.0.5

com.baomidou

mybatis-plus-generator

3.0.7.1

org.mybatis.spring.boot

mybatis-spring-boot-autoconfigure

1.3.2

org.apache.velocity

velocity-engine-core

2.0

org.freemarker

freemarker

2.3.28

com.ibeetl

beetl

2.9.8

mybatis配置如下:

########################## mysql ##########################

spring.datasource.url: "jdbc:mysql://localhost:3306/chinotan?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"

spring.datasource.username: root

spring.datasource.password:

spring.datasource.driver-class-name: "com.mysql.cj.jdbc.Driver"

logging.level.com.shyroke.mapper: debug

########################## mybatis ##########################

mybatis.mapper-locations: classpath:mybatis/*.xml

########################## druid配置 ##########################

spring.datasource.type: com.alibaba.druid.pool.DruidDataSource

# 初始化大小,最小,最大

spring.datasource.initialSize: 5

spring.datasource.minIdle: 5

spring.datasource.maxActive: 20

# 配置获取连接等待超时的时间

spring.datasource.maxWait: 60000

# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒

spring.datasource.timeBetweenEvictionRunsMillis: 60000

# 配置一个连接在池中最小生存的时间,单位是毫秒

spring.datasource.minEvictableIdleTimeMillis: 300000

# 校验SQLÿ

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值