一、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”方法调用,它也是负责低层次 映射语句执行的内部对象。 


插件的作用就是在执行预计前,后都可以自定义需要处理的程序(通过责任链模式 ) ,也就是说可以定义多个需要执行的程序。

二、 实现一个自己的拦截器

  1. package com.elements.user.plugins;

  2. import java.sql.Connection;
  3. import java.util.Properties;

  4. import org.apache.ibatis.executor.statement.StatementHandler;
  5. import org.apache.ibatis.plugin.Interceptor;
  6. import org.apache.ibatis.plugin.Intercepts;
  7. import org.apache.ibatis.plugin.Invocation;
  8. import org.apache.ibatis.plugin.Signature;

  9. @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
  10. public class PageInterceptor implements Interceptor {

  11.     public Object intercept(Invocation invocation) throws Throwable {
  12.         System.out.println("bbbbbbbbbb");

  13.         return null;
  14.     }

  15.     public Object plugin(Object target) {
  16.         System.out.println("target is :"+target.getClass().getName());
  17.         return target;
  18.  
  19.     }

  20.     public void setProperties(Properties properties) {
  21.         System.out.println(properties.toString());

  22.     }

  23. }

测试输出结果

  1. {someProperty=100}
  2. target is :org.apache.ibatis.executor.CachingExecutor
  3. target is :org.apache.ibatis.scripting.defaults.DefaultParameterHandler
  4. target is :org.apache.ibatis.executor.resultset.DefaultResultSetHandler
  5. 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

三、分页实现

mybatis-config.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE configuration
  3.   PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4.   "http://mybatis.org/dtd/mybatis-3-config.dtd">

  5. <configuration>

  6.     <properties resource="jdbc.properties" />

  7.    <!-- 自定义分页拦截器 -->
  8.     <plugins>
  9.         <plugin interceptor="com.elements.user.plugins.PageInterceptor">
  10.             <property name="someProperty" value="100" />
  11.         </plugin>
  12.     </plugins>

  13.     <environments default="dev">
  14.         <environment id="dev">
  15.             <transactionManager type="JDBC" />
  16.             <dataSource type="POOLED">
  17.                 <property name="driver" value="${driver}" />
  18.                 <property name="url" value="${url}" />
  19.                 <property name="username" value="${username}" />
  20.                 <property name="password" value="${password}" />
  21.             </dataSource>
  22.         </environment>
  23.     </environments>



  24.     <mappers>
  25.         <mapper class="com.elements.user.dao.UserMapper" />

  26.     </mappers>

  27. </configuration>

page

  1. package com.elements.user.plugins;

  2. public class Page {

  3.     // 定义分页的默认大小
  4.     private int OnePageSize = 5;

  5.     // 当前页数
  6.     private int nowPage = 1;

  7.     public int getOnePageSize() {
  8.         return OnePageSize;
  9.     }

  10.     public void setOnePageSize(int onePageSize) {
  11.         OnePageSize = onePageSize;
  12.     }

  13.     public int getStartRow() {
  14.         return startRow;
  15.     }

  16.     public void setStartRow(int startRow) {
  17.         this.startRow = startRow;
  18.     }

  19.     public int getEndRow() {
  20.         return endRow;
  21.     }

  22.     public void setEndRow(int endRow) {
  23.         this.endRow = endRow;
  24.     }

  25.     // 开始的行
  26.     private int startRow = 0;
  27.     {
  28.         startRow = (nowPage - 1) * OnePageSize;
  29.     }

  30.     // 结束的行
  31.     private int endRow = 0;
  32.     {
  33.         endRow = nowPage * OnePageSize;
  34.     }

  35.     // 总的页数
  36.     private int AllPages = 0;

  37.     // 总记录数
  38.     private int allRows = 0;

  39.     public int getAllRows() {
  40.         return allRows;
  41.     }

  42.     public void setAllRows(int allRows) {
  43.         this.allRows = allRows;
  44.         this.AllPages = allRows % OnePageSize == 0 ? (int) (allRows / OnePageSize)
  45.                 : (int) (allRows / OnePageSize) + 1;
  46.     }

  47.     public int getNowPage() {
  48.         return nowPage;
  49.     }

  50.     public void setNowPage(int nowPage) {
  51.         this.nowPage = nowPage;
  52.         startRow = (nowPage - 1) * OnePageSize;
  53.         endRow = nowPage * OnePageSize;
  54.     }
  55. }
基础类User

  1. package com.elements.user.model;

  2. import com.elements.user.plugins.Page;

  3.  

  4. public class User extends Page{
  5.     /**
  6.      * This field was generated by MyBatis Generator.
  7.      * This field corresponds to the database column user.userId
  8.      *
  9.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  10.      */
  11.     private Integer userid;

  12.     /**
  13.      * This field was generated by MyBatis Generator.
  14.      * This field corresponds to the database column user.UserName
  15.      *
  16.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  17.      */
  18.     private String username;

  19.     /**
  20.      * This field was generated by MyBatis Generator.
  21.      * This field corresponds to the database column user.UserEmail
  22.      *
  23.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  24.      */
  25.     private String useremail;

  26.     /**
  27.      * This field was generated by MyBatis Generator.
  28.      * This field corresponds to the database column user.createTime
  29.      *
  30.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  31.      */
  32.     private String createtime;

  33.     /**
  34.      * This field was generated by MyBatis Generator.
  35.      * This field corresponds to the database column user.updateTime
  36.      *
  37.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  38.      */
  39.     private String updatetime;

  40.     /**
  41.      * This method was generated by MyBatis Generator.
  42.      * This method returns the value of the database column user.userId
  43.      *
  44.      * @return the value of user.userId
  45.      *
  46.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  47.      */
  48.     public Integer getUserid() {
  49.         return userid;
  50.     }

  51.     /**
  52.      * This method was generated by MyBatis Generator.
  53.      * This method sets the value of the database column user.userId
  54.      *
  55.      * @param userid the value for user.userId
  56.      *
  57.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  58.      */
  59.     public void setUserid(Integer userid) {
  60.         this.userid = userid;
  61.     }

  62.     /**
  63.      * This method was generated by MyBatis Generator.
  64.      * This method returns the value of the database column user.UserName
  65.      *
  66.      * @return the value of user.UserName
  67.      *
  68.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  69.      */
  70.     public String getUsername() {
  71.         return username;
  72.     }

  73.     /**
  74.      * This method was generated by MyBatis Generator.
  75.      * This method sets the value of the database column user.UserName
  76.      *
  77.      * @param username the value for user.UserName
  78.      *
  79.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  80.      */
  81.     public void setUsername(String username) {
  82.         this.username = username == null ? null : username.trim();
  83.     }

  84.     /**
  85.      * This method was generated by MyBatis Generator.
  86.      * This method returns the value of the database column user.UserEmail
  87.      *
  88.      * @return the value of user.UserEmail
  89.      *
  90.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  91.      */
  92.     public String getUseremail() {
  93.         return useremail;
  94.     }

  95.     /**
  96.      * This method was generated by MyBatis Generator.
  97.      * This method sets the value of the database column user.UserEmail
  98.      *
  99.      * @param useremail the value for user.UserEmail
  100.      *
  101.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  102.      */
  103.     public void setUseremail(String useremail) {
  104.         this.useremail = useremail == null ? null : useremail.trim();
  105.     }

  106.     /**
  107.      * This method was generated by MyBatis Generator.
  108.      * This method returns the value of the database column user.createTime
  109.      *
  110.      * @return the value of user.createTime
  111.      *
  112.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  113.      */
  114.     public String getCreatetime() {
  115.         return createtime;
  116.     }

  117.     /**
  118.      * This method was generated by MyBatis Generator.
  119.      * This method sets the value of the database column user.createTime
  120.      *
  121.      * @param createtime the value for user.createTime
  122.      *
  123.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  124.      */
  125.     public void setCreatetime(String createtime) {
  126.         this.createtime = createtime ;
  127.     }

  128.     /**
  129.      * This method was generated by MyBatis Generator.
  130.      * This method returns the value of the database column user.updateTime
  131.      *
  132.      * @return the value of user.updateTime
  133.      *
  134.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  135.      */
  136.     public String getUpdatetime() {
  137.         return updatetime;
  138.     }

  139.     /**
  140.      * This method was generated by MyBatis Generator.
  141.      * This method sets the value of the database column user.updateTime
  142.      *
  143.      * @param updatetime the value for user.updateTime
  144.      *
  145.      * @mbggenerated Wed Apr 27 15:05:09 CST 2016
  146.      */
  147.     public void setUpdatetime(String updatetime) {
  148.         this.updatetime = updatetime;
  149.     }

  150.     @Override
  151.     public String toString() {
  152.         return "User [userid=" + userid + ", username=" + username
  153.                 + ", useremail=" + useremail + ", createtime=" + createtime
  154.                 + ", updatetime=" + updatetime + "]\n";
  155.     }
  156.     
  157.     
  158. }
UserMapper

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
  3. <mapper namespace="com.elements.user.dao.UserMapper" >

  4.  
  5.   <resultMap id="BaseResultMap" type="com.elements.user.model.User" >
  6.     <id column="userId" property="userid" jdbcType="INTEGER" />
  7.     <result column="UserName" property="username" jdbcType="VARCHAR" />
  8.     <result column="UserEmail" property="useremail" jdbcType="VARCHAR" />
  9.     <result column="createTime" property="createtime" jdbcType="VARCHAR" />
  10.     <result column="updateTime" property="updatetime" jdbcType="VARCHAR" />
  11.   </resultMap>
  12.   
  13.   
  14.   <sql id="Base_Column_List" >
  15.     userId, UserName, UserEmail, createTime, updateTime
  16.   </sql>
  17.   
  18.   <select id="selectUserPage" parameterType="com.elements.user.model.User" resultMap="BaseResultMap">
  19.     select
  20.     <include refid="Base_Column_List" />
  21.     from user
  22.      where 1=1
  23.      and UserName like '%%'
  24.   </select>
  25.   
  26. </mapper>
最终要的:PageInterceptor

  1. package com.elements.user.plugins;

  2. import java.sql.Connection;
  3. import java.sql.PreparedStatement;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.util.List;
  7. import java.util.Properties;

  8. import org.apache.ibatis.executor.parameter.ParameterHandler;
  9. import org.apache.ibatis.executor.statement.RoutingStatementHandler;
  10. import org.apache.ibatis.executor.statement.StatementHandler;
  11. import org.apache.ibatis.mapping.BoundSql;
  12. import org.apache.ibatis.mapping.MappedStatement;
  13. import org.apache.ibatis.mapping.ParameterMapping;
  14. import org.apache.ibatis.plugin.Interceptor;
  15. import org.apache.ibatis.plugin.Intercepts;
  16. import org.apache.ibatis.plugin.Invocation;
  17. import org.apache.ibatis.plugin.Plugin;
  18. import org.apache.ibatis.plugin.Signature;
  19. import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;

  20. @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
  21. public class PageInterceptor implements Interceptor {

  22.     public Object plugin(Object target) {
  23.         System.out.println("target is :" + target.getClass().getName());
  24.         if (target instanceof RoutingStatementHandler) {
  25.             return Plugin.wrap(target, this);
  26.         }
  27.         return target;
  28.     }

  29.     public void setProperties(Properties properties) {
  30.         System.out.println(properties.toString());

  31.     }

  32.     public Object intercept(Invocation invocation) throws Throwable {

  33.         if (invocation.getTarget() instanceof RoutingStatementHandler) {

  34.             RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation
  35.                     .getTarget();
  36.             StatementHandler delegate = (StatementHandler) ReflectHelper
  37.                     .getFieldValue(statementHandler, "delegate");
  38.             BoundSql boundSql = delegate.getBoundSql();
  39.             Object obj = boundSql.getParameterObject();

  40.             if (obj instanceof Page) {

  41.                 Page page = (Page) obj;
  42.                 // 通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
  43.                 MappedStatement mappedStatement = (MappedStatement) ReflectHelper
  44.                         .getFieldValue(delegate, "mappedStatement");
  45.                 // 拦截到的prepare方法参数是一个Connection对象
  46.                 Connection connection = (Connection) invocation.getArgs()[0];

  47.                 // 获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句
  48.                 String sql = boundSql.getSql();
  49.                 // 给当前的page参数对象设置总记录数
  50.                 this.setTotalRecord(page, mappedStatement, connection);
  51.                 // 获取分页Sql语句
  52.                 String pageSql = this.getPageSql(page, sql);
  53.                 // 利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
  54.                 ReflectHelper.setFieldValue(boundSql, "sql", pageSql);
  55.             }
  56.         }

  57.         return invocation.proceed();

  58.     }

  59.     private String getPageSql(Page page, String sql) {

  60.         StringBuffer sqlBuffer = new StringBuffer(sql);

  61.         sqlBuffer.insert(0, "select * from (").append(")temp where 1=1 ")
  62.                 .append(" limit ").append(page.getStartRow()).append(",")
  63.                 .append(page.getOnePageSize());

  64.         return sqlBuffer.toString();
  65.     }

  66.     private void setTotalRecord(Page page, MappedStatement mappedStatement,
  67.             Connection connection) {

  68.         // delegate里面的boundSql也是通过mappedStatement.getBoundSql(paramObj)方法获取到的。
  69.         BoundSql boundSql = mappedStatement.getBoundSql(page);
  70.         // 获取到我们自己写在Mapper映射语句中对应的Sql语句
  71.         String sql = boundSql.getSql();
  72.         // 通过查询Sql语句获取到对应的计算总记录数的sql语句
  73.         String countSql = this.getCountSql(sql);

  74.         // 通过BoundSql获取对应的参数映射
  75.         List<ParameterMapping> parameterMappings = boundSql
  76.                 .getParameterMappings();
  77.         // 利用Configuration、查询记录数的Sql语句countSql、参数映射关系parameterMappings和参数对象page建立查询记录数对应的BoundSql对象。
  78.         BoundSql countBoundSql = new BoundSql(
  79.                 mappedStatement.getConfiguration(), countSql,
  80.                 parameterMappings, page);
  81.         // 通过mappedStatement、参数对象page和BoundSql对象countBoundSql建立一个用于设定参数的ParameterHandler对象
  82.         ParameterHandler parameterHandler = new DefaultParameterHandler(
  83.                 mappedStatement, page, countBoundSql);
  84.         // 通过connection建立一个countSql对应的PreparedStatement对象。
  85.         PreparedStatement pstmt = null;
  86.         ResultSet rs = null;
  87.         try {
  88.             pstmt = connection.prepareStatement(countSql);
  89.             // 通过parameterHandler给PreparedStatement对象设置参数
  90.             parameterHandler.setParameters(pstmt);
  91.             // 之后就是执行获取总记录数的Sql语句和获取结果了。
  92.             rs = pstmt.executeQuery();
  93.             if (rs.next()) {
  94.                 int totalRecord = rs.getInt(1);
  95.                 // 给当前的参数page对象设置总记录数
  96.                 page.setAllRows(totalRecord);
  97.             }
  98.         } catch (SQLException e) {
  99.             e.printStackTrace();
  100.         } finally {
  101.             try {
  102.                 if (rs != null)
  103.                     rs.close();
  104.                 if (pstmt != null)
  105.                     pstmt.close();
  106.             } catch (SQLException e) {
  107.                 e.printStackTrace();
  108.             }
  109.         }

  110.     }

  111.     private String getCountSql(String sql) {
  112.         StringBuffer sqlBuffer = new StringBuffer(sql);
  113.         sqlBuffer.insert(0, "select count(*) from (").append(
  114.                 ")temp where 1=1 ");
  115.         return sqlBuffer.toString();
  116.     }

  117. }
测试类:
  1. package com.elements.user;

  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.util.List;

  5. import org.apache.ibatis.io.Resources;
  6. import org.apache.ibatis.session.SqlSession;
  7. import org.apache.ibatis.session.SqlSessionFactory;
  8. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  9. import org.junit.Test;
  10. import com.elements.user.dao.UserMapper;
  11. import com.elements.user.model.User;

  12. public class TestUserPage {

  13.     @Test
  14.     public void TestUserPageSelect() throws IOException {
  15.         String resource = "mybatis-config.xml";
  16.         InputStream inputStream = Resources.getResourceAsStream(resource);
  17.         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
  18.                 .build(inputStream);

  19.         SqlSession session = sqlSessionFactory.openSession();
  20.         try {
  21.  
  22.             
  23.             UserMapper user = (UserMapper) session
  24.                     .getMapper(UserMapper.class);
  25.  
  26.             User u=new User();
  27.             
  28.             List<User> list =user.selectUserPage(u);
  29.             
  30.             System.out.print(list);

  31.         } finally {
  32.             session.close();
  33.         }

  34.     }

  35. }

四、总结

     只要你的查询参数继承Page类,就会自动分页,查询总记录,总页数(在page内  )。方式一要自己一一实现。

项目代码地址:  http://pan.baidu.com/s/1sld2aZV               
                                                                                        本文章参考了:
(1)http://blog.csdn.net/hfmbook/article/details/41985853

快照源:http://blog.chinaunix.net/uid-261569-id-5711315.html