一、Mybatis拦截器
Mybatis拦截器的供给用户在某些时候可以实现自己的逻辑而不必去改动Mybatis的固有逻辑,通过Mybatis拦截器可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。
Mybatis的核心对象之一: SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。
Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、ParameterHandler、StatementHandler、ResultSetHandler四个对象里面的方法。
将拦截器类交给spring管理,使用配置文件、配置类、或直接使用@Component注解均可。
目的都是将拦截器类注入spring容器中。
配置文件 :
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.qxy.mybatis.interceptor.TableLimitInterceptor"/>
</plugins>
</configuration>
配置类:
@Configuration
public class MyBatisConfiguration {
@Bean
TableLimitInterceptor tableLimitInterceptor(){
return new TableLimitInterceptor();
}
}
@Component 注解:(直接在拦截器类上添加注解)
Mybatis拦截器代码实现 :实现org.apache.ibatis.plugin.Interceptor接口,重写以下方法:
public interface Interceptor {
// 拦截器拦截后对象后,执行自己的业务逻辑
Object intercept(Invocation invocation) throws Throwable;
// 判断是否拦截这个类型对象(根据@Intercepts注解决定),然后决定是返回一个代理对象还是返回原对象。
Object plugin(Object target);
// 可给拦截器设置一些变量对象
void setProperties(Properties properties);
}
@Component
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class TableLimitInterceptor implements Interceptor {
private static final Logger log = LoggerFactory.getLogger(ChannelBusiProcessorFactory.class);
// 拦截器拦截后对象后,执行自己的业务逻辑
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 入参invocation指拦截到的对象
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
MySqlStatementParser mySqlStatementParser = new MySqlStatementParser(sql);
SQLStatement statement = mySqlStatementParser.parseStatement();
if (statement instanceof SQLSelectStatement) {
SQLSelect selectQuery = ((SQLSelectStatement) statement).getSelect();
MySqlSelectQueryBlock sqlSelectQuery = (MySqlSelectQueryBlock) selectQuery.getQuery();
String tableName = sqlSelectQuery.getFrom().toString();
SQLLimit limit = sqlSelectQuery.getLimit();
// 不拦截 带有limit 并且 表名中含有 dict的sql
if(null == limit && !tableName.contains("dict") ){
SQLLimit sqlLimit = new SQLLimit();
sqlLimit.setRowCount(1000);
sqlSelectQuery.setLimit(sqlLimit);
String newSql = sqlSelectQuery.toString();
// 修改 sql
ReflectUtil.setFieldValue(boundSql, "sql", newSql);
}
}
return invocation.proceed(); //程序继续运行
}
//判断是否拦截这个类型对象(根据@Intercepts注解决定),然后决定是返回一个代理对象还是返回原对象。
//每经过一个拦截器对象都会调用插件的plugin方法,也就是说,该方法会调用4次。根据@Intercepts注解来决定是否进行拦截处理。
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
}
// 可给拦截器设置一些变量对象
@Override
public void setProperties(Properties properties) {
}
}
MyBatis四大核心对象(四个可以被拦截的对象):
ParameterHandler: 处理sql 参数对象
ResultsetHandler: 处理sql 返回结果集
StatementHandler: 数据库的处理对象,执行sql语句
Executor: MyBatis执行器, 用于执行增删改查操作
例如:
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Intercepts
Intercepts注解需要一个Signature(拦截点)参数数组。通过Signature来指定拦截哪个对象里面的哪个方法。
@Intercepts:标识该类是一个拦截器;
@Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法;
type:上述四种对象类型中的一种;
method:对应接口中的哪类方法(因为可能存在重载方法);
args:对应哪一个方法的入参;
(注:type、method、args三个值共同来确定需要拦截的具体方法)
拦截类型 | 拦截方法 |
Executor | update , query, flushStatements , commit , rollback , getTransaction |
ParameterHandler | getParameterObject, setParemeters |
StatementHandler | prepare , parameterize, batch , update, query |
ResultSetHandler | handleResultSets , handleOutputParameters |
总结:拦截器可拦截上述的任意一个或多个方法, 拦截到方法后,在方法中拿到对应的参数,可根据自己的业务逻辑对参数进行修改,实现自己的业务,非常的灵活。可同时使用多个拦截器,拦截多个不同的方法不会产生冲突,并且多个拦截器可同时拦截同一个方法,此时多个拦截器会依次执行,这里就要考虑拦拦截器执行的先后顺序了。
二、with as table 建立临时表
with t as(
select '1' as id from dual
union all
select '2' as id from dual
·····
)
select i.*
from table i
where i.id in(
select t.id from t
)
三、使用or
方法1
select * from table where id in (1, 2, ..., 1000) or id in(1001, ....., 1999)
后台使用:
public String createSql(String col ,List<Integer> umIds) {
StringBuffer sb = new StringBuffer();
sb.append(col + " in(");
int size = umIds.size();
int index = 0;
for (int i = 0; i < size; i++) {
index++;
if (index == 999) {
index = 0;
sb.append(")");
sb.append(" or " + col + " in ( ");
}
if (index + 1 == 999 || size - 1 == i) {
sb.append("" + umIds.get(i) + "");
} else {
sb.append("" + umIds.get(i) + ",");
}
}
sb.append(")");
return sb.toString();
}
方法2:
select * from table where id = 1 or id = 2 or id = 3 or ·····
超过1000报错:
<isNotEmpty property="ids" prepend="and">
t.id in
<iterate open="(" close=")" conjunction="," property="ids">
#ids[]#
</iterate>
</isNotEmpty>
更正为:
<isNotEmpty property="ids" prepend="and">
<iterate open="(" close=")" conjunction=" or " property="ids">
t.cltno = #ids[]#
</iterate>
</isNotEmpty>