spring + mybatis 进行拦截分页、分库

来源:互联网 发布:云计算管理系统 编辑:程序博客网 时间:2024/05/16 13:42

http://zxlaiye.iteye.com/blog/1344703

悲催的Bug 

与spring结合时,org.mybatis.spring.mapper.MapperScannerConfigurer不支持 <context:property-placeholder /> 
(mybatis 3.1.1, mybatis-spring 1.1.1) 

●结合Spring的配置 
Xml代码  收藏代码
  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
  2.     <property name="dataSource" ref="dataSource"/>  
  3.     <property name="typeAliasesPackage" value="xx.xx.xx.entity" />  
  4.     <property name="plugins">  
  5.        <list>  
  6.           <!-- 配置自己实现的分页插件 -->  
  7.           <bean class="xx.xx.xx.mybatis.PagingPlugin">  
  8.             <property name="dialect" value="mysql"/>  
  9.           </bean>  
  10.         </list>  
  11.     </property>  
  12. </bean>  
  13. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
  14.     <property name="basePackage" value="xx.xx.xx.dao" />  
  15. </bean>  


●传递多个参数 
Java代码  收藏代码
  1. //使用@Param来注解,例如:  
  2. List queryXX(@Param("arg1"int arg1, @Param("arg2") String arg2);  


●分页插件。 有以下缺点: 
  • 暂时只实现mysql和oracle
  • oracle未测试
  • 未考虑取总数的性能
  • 未考虑排序
  • 查找的结果集未能自动放到分页对象(Page)中

Page.java 
Java代码  收藏代码
  1. public class Page{  
  2.     private int limit = 20//每页显示条数  
  3.     private int start = 0;  //起始行号  
  4.     private long total = -1//总数  
  5.     private List result = new ArrayList(); //结果集  
  6.     //---- 省略get set ----//  
  7. }  

PagingPlugin.java 
Java代码  收藏代码
  1. /** 
  2.  * Mybatis的分页查询插件,通过拦截StatementHandler的prepare方法来实现。 
  3.  * 只有在参数列表中包括Page类型的参数时才进行分页查询。 
  4.  * 在多参数的情况下,只对第一个Page类型的参数生效。 
  5.  * 另外,在参数列表中,Page类型的参数无需用@Param来标注 
  6.  * @author linzongxue 2012-1-16(修改) 
  7.  * 
  8.  */  
  9. @Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})   
  10. public class PagingPlugin implements Interceptor {  
  11.     private String dialect;  
  12.   
  13.     @SuppressWarnings("unchecked")  
  14.     @Override  
  15.     public Object intercept(Invocation invocation) throws Throwable {  
  16.         if(!(invocation.getTarget() instanceof RoutingStatementHandler))   
  17.             return invocation.proceed();  
  18.           
  19.         RoutingStatementHandler statementHandler = (RoutingStatementHandler)invocation.getTarget();  
  20.         BoundSql boundSql = statementHandler.getBoundSql();  
  21.         //分析是否含有分页参数,如果没有则不是分页查询  
  22.         //注意:在多参数的情况下,只处理第一个分页参数  
  23.         Page page = null;  
  24.         Object paramObj = boundSql.getParameterObject();  
  25.         if (paramObj instanceof Page){ //只有一个参数的情况  
  26.             page = (Page)paramObj;  
  27.         }  
  28.         else if (paramObj instanceof Map){ //多参数的情况,找到第一个Page的参数  
  29.             for (Map.Entry<String, Object> e : ((Map<String, Object>)paramObj).entrySet()){  
  30.                 if (e.getValue() instanceof Page){  
  31.                     page = (Page)e.getValue();  
  32.                     break;  
  33.                 }  
  34.             }  
  35.         }  
  36.           
  37.         if (page == nullreturn invocation.proceed();  
  38.           
  39.         //查找总记录数,并设置Page的相关参数  
  40.         long total = this.getTotal(invocation);  
  41.         page.setTotal(total);  
  42.         //生成分页SQL  
  43.         String pageSql = generatePageSql(boundSql.getSql(), page);  
  44.         //强制修改最终要执行的SQL  
  45.         setFieldValue(boundSql, "sql", pageSql);  
  46.         return invocation.proceed();  
  47.     }  
  48.       
  49.     /** 
  50.      * 获取记录总数 
  51.      */  
  52.     @SuppressWarnings("unchecked")  
  53.     private long getTotal(Invocation invocation) throws Exception{  
  54.         RoutingStatementHandler statementHandler = (RoutingStatementHandler)invocation.getTarget();  
  55.         BoundSql boundSql = statementHandler.getBoundSql();  
  56.         /* 
  57.          * 为了设置查找总数SQL的参数,必须借助MappedStatement、Configuration等这些类, 
  58.          * 但statementHandler并没有开放相应的API,所以只好用反射来强行获取。 
  59.          */  
  60.         BaseStatementHandler delegate = (BaseStatementHandler)getFieldValue(statementHandler, "delegate");  
  61.         MappedStatement mappedStatement = (MappedStatement)getFieldValue(delegate, "mappedStatement");  
  62.         Configuration configuration = mappedStatement.getConfiguration();  
  63.         TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();  
  64.         Object param = boundSql.getParameterObject();  
  65.         MetaObject metaObject = configuration.newMetaObject(param);  
  66.           
  67.         long total = 0;  
  68.         String sql = boundSql.getSql();  
  69.         String countSql = "select count(1) from (" + sql+ ") as t"//记录统计  (mysql要求必须添加 最后的as t)  
  70.         try{  
  71.             Connection conn = (Connection)invocation.getArgs()[0];  
  72.             PreparedStatement ps = conn.prepareStatement(countSql);  
  73.             int i = 1;  
  74.             for (ParameterMapping pm : boundSql.getParameterMappings()) {  
  75.                 Object value = null;  
  76.                 String propertyName = pm.getProperty();  
  77.                 PropertyTokenizer prop = new PropertyTokenizer(propertyName);  
  78.                 if (typeHandlerRegistry.hasTypeHandler(param.getClass())) {  
  79.                     value = param;    
  80.                 }  
  81.                 else if (boundSql.hasAdditionalParameter(propertyName)) {  
  82.                     value = boundSql.getAdditionalParameter(propertyName);    
  83.                 }  
  84.                 else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&& boundSql.hasAdditionalParameter(prop.getName())) {    
  85.                     value = boundSql.getAdditionalParameter(prop.getName());  
  86.                     if (value != null) {    
  87.                         value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));    
  88.                     }  
  89.                 } else {    
  90.                     value = metaObject.getValue(propertyName);  
  91.                 }  
  92.   
  93.                 pm.getTypeHandler().setParameter(ps, i++, value, pm.getJdbcType());  
  94.             }  
  95.             ResultSet rs = ps.executeQuery();  
  96.             rs.next();  
  97.             total = rs.getLong(1);  
  98.             rs.close();    
  99.             ps.close();  
  100.         }  
  101.         catch (Exception e){  
  102.             throw new RuntimeException("分页查询无法获取总记录数", e);  
  103.         }  
  104.         return total;  
  105.     }  
  106.       
  107.     /** 
  108.      * 生成分页SQL 
  109.      */  
  110.     private String generatePageSql(String sql, Page page){  
  111.         StringBuilder pageSql = new StringBuilder();  
  112.         if("mysql".equals(dialect)){  
  113.             pageSql.append(sql);  
  114.             pageSql.append(" limit ").append(page.getStart()).append(",").append(page.getLimit());    
  115.         }  
  116.         else if("oracle".equals(dialect)){  
  117.             pageSql.append("select * from (select t.*, ROWNUM num from (")  
  118.                 .append(sql).append(") as t where ROWNUM <= ")  
  119.                 .append(page.getStart() + page.getLimit())  
  120.                 .append(") where num > ").append(page.getStart());  
  121.         }  
  122.         else{  
  123.             throw new RuntimeException("分页插件还不支持数据库类型:" + dialect);  
  124.         }  
  125.         return pageSql.toString();  
  126.     }  
  127.       
  128.     /** 
  129.      * 用反射取对象的属性值 
  130.      */  
  131.     private Object getFieldValue(Object obj, String fieldName) throws Exception{          
  132.         for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {  
  133.             try{  
  134.                 Field field = superClass.getDeclaredField(fieldName);  
  135.                 field.setAccessible(true);  
  136.                 return field.get(obj);  
  137.             }  
  138.             catch(Exception e){}  
  139.         }  
  140.         return null;  
  141.     }  
  142.   
  143.     /** 
  144.      * 用反射设置对象的属性值 
  145.      */  
  146.     private void setFieldValue(Object obj, String fieldName, Object fieldValue) throws Exception{  
  147.         Field field = obj.getClass().getDeclaredField(fieldName);  
  148.         field.setAccessible(true);  
  149.         field.set(obj, fieldValue);  
  150.     }  
  151.       
  152.     @Override  
  153.     public Object plugin(Object target) {  
  154.         return Plugin.wrap(target, this);  
  155.     }  
  156.       
  157.     @Override  
  158.     public void setProperties(Properties props) {  
  159.           
  160.     }  
  161.       
  162.     public void setDialect(String dialect){  
  163.         this.dialect = dialect.toLowerCase();  
  164.     }  
  165.       
  166.     public String getDialect(){  
  167.         return this.dialect;  
  168.     }  
  169. }  
分享到:  
maven笔记 | 夜半闲聊
  • 2012-01-13 13:53
  • 浏览 5686
  • 评论(2)
  • 收藏
  • 分类:编程语言
  • 相关推荐

0 0
原创粉丝点击