一、Mybatis插件介绍
摘自官方文档:
很晦涩,简单阅读一下即可
MyBatis 允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis 允许使用插件来拦截方法调用:
-
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
-
ParameterHandler (getParameterObject, setParameters)
-
ResultSetHandler (handleResultSets, handleOutputParameters)
-
StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的详情可以通过查看每个方法的签名来发现 ,而且它们的源代码在 MyBatis 的发行包中有。你应该理解你覆盖方法的行为,假设你所做的要比监视调用要多。 如果你尝试修改或覆盖一个给定的方法, 你可能会打破 MyBatis 的核心。 这是低层次的类和方法,要谨慎使用插件。
使用插件是它们提供的非常简单的力量。
简单实现拦截器接口, 要确定你想拦截的指定签名。
上面的插件将会拦截在 Executor 实例中所有的“update”方法调用,它也是负责低层次 映射语句执行的内部对象。
插件的作用就是在执行预计前,后都可以自定义需要处理的程序(通过责任链模式 ) ,也就是说可以定义多个需要执行的程序。
二、 实现一个自己的拦截器
-
package com.elements.user.plugins;
-
-
import java.sql.Connection;
-
import java.util.Properties;
-
-
import org.apache.ibatis.executor.statement.StatementHandler;
-
import org.apache.ibatis.plugin.Interceptor;
-
import org.apache.ibatis.plugin.Intercepts;
-
import org.apache.ibatis.plugin.Invocation;
-
import org.apache.ibatis.plugin.Signature;
-
-
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
-
public class PageInterceptor implements Interceptor {
-
-
public Object intercept(Invocation invocation) throws Throwable {
-
System.out.println("bbbbbbbbbb");
-
-
return null;
-
}
-
-
public Object plugin(Object target) {
-
System.out.println("target is :"+target.getClass().getName());
-
return target;
-
-
}
-
-
public void setProperties(Properties properties) {
-
System.out.println(properties.toString());
-
-
}
-
-
}
测试输出结果
-
{someProperty=100}
-
target is :org.apache.ibatis.executor.CachingExecutor
-
target is :org.apache.ibatis.scripting.defaults.DefaultParameterHandler
-
target is :org.apache.ibatis.executor.resultset.DefaultResultSetHandler
-
target is :org.apache.ibatis.executor.statement.RoutingStatementHandler
intercept没有被调用
第一步 我们的属性,从配置文件里面获取了,这里是 someProperty 值是 someProperty
第二步 我们的plugin ,被调用了4次,分别是 CachingExecutor, DefaultParameterHandler ,DefaultResultSetHandler,RoutingStatementHandler
CachingExecutor :二级缓存执行器 实现 Executor
DefaultParameterHandler :参数处理 实现ParameterHandler
DefaultResultSetHandler :实现 ResultSetHandler
ResultSetHandler只负责两件事:
1.处理Statement执行后产生的结果集,生成结果列表
2.处理存储过程执行后的输出参数
RoutingStatementHandler:实现了StatementHandler
三、分页实现
最终要的:PageInterceptor
-
package com.elements.user.plugins;
-
-
import java.sql.Connection;
-
import java.sql.PreparedStatement;
-
import java.sql.ResultSet;
-
import java.sql.SQLException;
-
import java.util.List;
-
import java.util.Properties;
-
-
import org.apache.ibatis.executor.parameter.ParameterHandler;
-
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
-
import org.apache.ibatis.executor.statement.StatementHandler;
-
import org.apache.ibatis.mapping.BoundSql;
-
import org.apache.ibatis.mapping.MappedStatement;
-
import org.apache.ibatis.mapping.ParameterMapping;
-
import org.apache.ibatis.plugin.Interceptor;
-
import org.apache.ibatis.plugin.Intercepts;
-
import org.apache.ibatis.plugin.Invocation;
-
import org.apache.ibatis.plugin.Plugin;
-
import org.apache.ibatis.plugin.Signature;
-
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
-
-
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
-
public class PageInterceptor implements Interceptor {
-
-
public Object plugin(Object target) {
-
System.out.println("target is :" + target.getClass().getName());
-
if (target instanceof RoutingStatementHandler) {
-
return Plugin.wrap(target, this);
-
}
-
return target;
-
}
-
-
public void setProperties(Properties properties) {
-
System.out.println(properties.toString());
-
-
}
-
-
public Object intercept(Invocation invocation) throws Throwable {
-
-
if (invocation.getTarget() instanceof RoutingStatementHandler) {
-
-
RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation
-
.getTarget();
-
StatementHandler delegate = (StatementHandler) ReflectHelper
-
.getFieldValue(statementHandler, "delegate");
-
BoundSql boundSql = delegate.getBoundSql();
-
Object obj = boundSql.getParameterObject();
-
-
if (obj instanceof Page) {
-
-
Page page = (Page) obj;
-
// 通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
-
MappedStatement mappedStatement = (MappedStatement) ReflectHelper
-
.getFieldValue(delegate, "mappedStatement");
-
// 拦截到的prepare方法参数是一个Connection对象
-
Connection connection = (Connection) invocation.getArgs()[0];
-
-
// 获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句
-
String sql = boundSql.getSql();
-
// 给当前的page参数对象设置总记录数
-
this.setTotalRecord(page, mappedStatement, connection);
-
// 获取分页Sql语句
-
String pageSql = this.getPageSql(page, sql);
-
// 利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
-
ReflectHelper.setFieldValue(boundSql, "sql", pageSql);
-
}
-
}
-
-
return invocation.proceed();
-
-
}
-
-
private String getPageSql(Page page, String sql) {
-
-
StringBuffer sqlBuffer = new StringBuffer(sql);
-
-
sqlBuffer.insert(0, "select * from (").append(")temp where 1=1 ")
-
.append(" limit ").append(page.getStartRow()).append(",")
-
.append(page.getOnePageSize());
-
-
return sqlBuffer.toString();
-
}
-
-
private void setTotalRecord(Page page, MappedStatement mappedStatement,
-
Connection connection) {
-
-
// delegate里面的boundSql也是通过mappedStatement.getBoundSql(paramObj)方法获取到的。
-
BoundSql boundSql = mappedStatement.getBoundSql(page);
-
// 获取到我们自己写在Mapper映射语句中对应的Sql语句
-
String sql = boundSql.getSql();
-
// 通过查询Sql语句获取到对应的计算总记录数的sql语句
-
String countSql = this.getCountSql(sql);
-
-
// 通过BoundSql获取对应的参数映射
-
List<ParameterMapping> parameterMappings = boundSql
-
.getParameterMappings();
-
// 利用Configuration、查询记录数的Sql语句countSql、参数映射关系parameterMappings和参数对象page建立查询记录数对应的BoundSql对象。
-
BoundSql countBoundSql = new BoundSql(
-
mappedStatement.getConfiguration(), countSql,
-
parameterMappings, page);
-
// 通过mappedStatement、参数对象page和BoundSql对象countBoundSql建立一个用于设定参数的ParameterHandler对象
-
ParameterHandler parameterHandler = new DefaultParameterHandler(
-
mappedStatement, page, countBoundSql);
-
// 通过connection建立一个countSql对应的PreparedStatement对象。
-
PreparedStatement pstmt = null;
-
ResultSet rs = null;
-
try {
-
pstmt = connection.prepareStatement(countSql);
-
// 通过parameterHandler给PreparedStatement对象设置参数
-
parameterHandler.setParameters(pstmt);
-
// 之后就是执行获取总记录数的Sql语句和获取结果了。
-
rs = pstmt.executeQuery();
-
if (rs.next()) {
-
int totalRecord = rs.getInt(1);
-
// 给当前的参数page对象设置总记录数
-
page.setAllRows(totalRecord);
-
}
-
} catch (SQLException e) {
-
e.printStackTrace();
-
} finally {
-
try {
-
if (rs != null)
-
rs.close();
-
if (pstmt != null)
-
pstmt.close();
-
} catch (SQLException e) {
-
e.printStackTrace();
-
}
-
}
-
-
}
-
-
private String getCountSql(String sql) {
-
StringBuffer sqlBuffer = new StringBuffer(sql);
-
sqlBuffer.insert(0, "select count(*) from (").append(
-
")temp where 1=1 ");
-
return sqlBuffer.toString();
-
}
-
-
}
测试类:
-
package com.elements.user;
-
-
import java.io.IOException;
-
import java.io.InputStream;
-
import java.util.List;
-
-
import org.apache.ibatis.io.Resources;
-
import org.apache.ibatis.session.SqlSession;
-
import org.apache.ibatis.session.SqlSessionFactory;
-
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
-
import org.junit.Test;
-
import com.elements.user.dao.UserMapper;
-
import com.elements.user.model.User;
-
-
public class TestUserPage {
-
-
@Test
-
public void TestUserPageSelect() throws IOException {
-
String resource = "mybatis-config.xml";
-
InputStream inputStream = Resources.getResourceAsStream(resource);
-
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
-
.build(inputStream);
-
-
SqlSession session = sqlSessionFactory.openSession();
-
try {
-
-
-
UserMapper user = (UserMapper) session
-
.getMapper(UserMapper.class);
-
-
User u=new User();
-
-
List<User> list =user.selectUserPage(u);
-
-
System.out.print(list);
-
-
} finally {
-
session.close();
-
}
-
-
}
-
-
}
四、总结
只要你的查询参数继承Page类,就会自动分页,查询总记录,总页数(在page内 )。方式一要自己一一实现。
项目代码地址:
本文章参考了:
(1)http://blog.csdn.net/hfmbook/article/details/41985853
阅读(4258) | 评论(0) | 转发(0) |