在日常工作中, 对于一些查询功能需要做到数据隔离的限制, 即不同用户查询的数据要不一样, 如何保证对业务程序低耦合, 又要保证功能的效果呢? 可以使用Mybatis提供插件,即Mybatis拦截器(详情可见下方官网)
Mybatis官网: https://mybatis.net.cn/configuration.html#plugins
Mybatis拦截器主要是拦截操作数据库的方法的调用, 并在拦截前后加上自定义的逻辑,从而实现最终功能的效果. 当然了, 也可以使用新的逻辑方法覆盖掉原有方法逻辑. 其设计的最初目的就是为了方便在某些时候可以实现自己的逻辑而不必去动mybatis固有的逻辑. 详情可见Mybatis官网-插件(plugins)部分.
Mybatis只能拦截特定对象的方法,使用范围如下:
Mybatis中所有Mapper的执行都是通过Executor接口完成的, Executor是MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护.
代码:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
/**
* 执行update/insert/delete
*/
int update(MappedStatement ms, Object parameter) throws SQLException;
/**
* 执行查询,先在缓存里面查找
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
/**
* 执行查询
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
/**
* 执行查询,查询结果放在Cursor里面
*/
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
/**
* 刷新Statement 批量返回结果
*/
List<BatchResult> flushStatements() throws SQLException;
/**
* 提交事物
*/
void commit(boolean required) throws SQLException;
/**
* 回滚事物
*/
void rollback(boolean required) throws SQLException;
/**
* 缓存key
*/
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
/**
* 是否缓存
*/
boolean isCached(MappedStatement ms, CacheKey key);
/**
* 清楚缓存
*/
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
/**
* 得到事物
*/
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
ParameterHandler是负责对用户传递的参数转换成JDBC Statement 所需要的参数,主要用来设置参数
代码:
public interface ParameterHandler {
Object getParameterObject();
/**
* 设置参数
*/
void setParameters(PreparedStatement ps)
throws SQLException;
}
ResultSetHandler是负责将JDBC返回的ResultSet结果集对象转换成List类型的集合.
代码:
public interface ResultSetHandler {
/**
* 将Statement执行后产生的结果集(可能有多个结果集)映射为结果列表
*/
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
/**
* 与上述一致
*/
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
/**
* 处理存储过程执行后的输出参数
*/
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
StatementHandler封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合等
代码:
public interface StatementHandler {
/**
* 从连接中获取一个Statement
*/
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
/**
* 设置statement执行里所需的参数
*/
void parameterize(Statement statement)
throws SQLException;
/**
* 批量处理statement
*/
void batch(Statement statement)
throws SQLException;
/**
* update/insert/delete语句 返回影响的行数
*/
int update(Statement statement)
throws SQLException;
/**
* 执行查询
*/
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
/**
* 执行查询
*/
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
/**
* 获取BoundSql, 主要是动态生成的SQL语句以及相应的参数信息
*/
BoundSql getBoundSql();
/**
* 获取ParameterHandler, 负责对用户传递的参数转换成JDBC Statement 所需要的参数
*/
ParameterHandler getParameterHandler();
}
只需实现 Interceptor 接口,并指定想要拦截的方法签名即可
Interceptor 代码:
public interface Interceptor {
/**
* 拦截要执行的方法, 在这个方法中自定义的逻辑
*/
Object intercept(Invocation invocation) throws Throwable;
/**
* 拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理
* 1 拦截 当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法 -- Plugin.wrap(target, this)
* 2 不拦截 当返回的是当前对象的时候 就不会调用intercept方法
*/
Object plugin(Object target);
/**
* 用于指定属性,注册当前拦截器的时候可以设置一些属性
*/
void setProperties(Properties properties);
}
官方例子:
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
plugin>
plugins>