Mybatis周全分頁插件。本站提示廣大學習愛好者:(Mybatis周全分頁插件)文章只能為提供參考,不一定能成為您想要的結果。以下是Mybatis周全分頁插件正文
依據上面分頁的思惟,很輕易完成Mybitas的多租戶設計。
應用Mybatis供給的攔阻器。對分頁的SQL語句經由過程封裝處置,處置成分歧的分頁sql。
本例曾經完成了對Mysql和Oracle的分頁功效。留意上面的援用包,不要援用錯了。
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;
import com.yidao.utils.Page;
import com.yidao.utils.ReflectHelper;
/**
*
* 分頁攔阻器,用於攔阻須要停止分頁查詢的操作,然後對其停止分頁處置。
* 應用攔阻器完成Mybatis分頁的道理:
* 要應用JDBC對數據庫停止操作就必需要有一個對應的Statement對象,Mybatis在履行Sql語句前就會發生一個包括Sql語句的Statement對象,並且對應的Sql語句
* 是在Statement之前發生的,所以我們便可以在它生成Statement之前對用來生成Statement的Sql語句下手。在Mybatis中Statement語句是經由過程RoutingStatementHandler對象的
* prepare辦法生成的。所以應用攔阻器完成Mybatis分頁的一個思緒就是攔阻StatementHandler接口的prepare辦法,然後在攔阻器辦法中把Sql語句改成對應的分頁查詢Sql語句,以後再挪用
* StatementHandler對象的prepare辦法,即挪用invocation.proceed()。
* 關於分頁而言,在攔阻器外面我們還須要做的一個操作就是統計知足以後前提的記載一共有若干,這是經由過程獲得到了原始的Sql語句後,把它改成對應的統計語句再應用Mybatis封裝好的參數和設
* 置參數的功效把Sql語句中的參數停止調換,以後再履行查詢記載數的Sql語句停止總記載數的統計。
*
*/
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PageInterceptor implements Interceptor {
private String dialect = ""; //數據庫方言
private String pageSqlId = ""; //mapper.xml中須要攔阻的ID(正則婚配)
public Object intercept(Invocation invocation) throws Throwable {
//關於StatementHandler其實只要兩個完成類,一個是RoutingStatementHandler,另外一個是籠統類BaseStatementHandler,
//BaseStatementHandler有三個子類,分離是SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler,
//SimpleStatementHandler是用於處置Statement的,PreparedStatementHandler是處置PreparedStatement的,而CallableStatementHandler是
//處置CallableStatement的。Mybatis在停止Sql語句處置的時刻都是樹立的RoutingStatementHandler,而在RoutingStatementHandler外面具有一個
//StatementHandler類型的delegate屬性,RoutingStatementHandler會根據Statement的分歧樹立對應的BaseStatementHandler,即SimpleStatementHandler、
//PreparedStatementHandler或CallableStatementHandler,在RoutingStatementHandler外面一切StatementHandler接口辦法的完成都是挪用的delegate對應的辦法。
//我們在PageInterceptor類上曾經用@Signature標志了該Interceptor只攔阻StatementHandler接口的prepare辦法,又由於Mybatis只要在樹立RoutingStatementHandler的時刻
//是經由過程Interceptor的plugin辦法停止包裹的,所以我們這裡攔阻到的目的對象確定是RoutingStatementHandler對象。
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();
}
/**
* 給以後的參數對象page設置總記載數
*
* @param page Mapper映照語句對應的參數對象
* @param mappedStatement Mapper映照語句
* @param connection 以後的數據庫銜接
*/
private void setTotalRecord(Page<?> page,
MappedStatement mappedStatement, Connection connection) {
//獲得對應的BoundSql,這個BoundSql其實跟我們應用StatementHandler獲得到的BoundSql是統一個對象。
//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.setTotalRecord(totalRecord);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 依據原Sql語句獲得對應的查詢總記載數的Sql語句
* @param sql
* @return
*/
private String getCountSql(String sql) {
int index = sql.indexOf("from");
return "select count(*) " + sql.substring(index);
}
/**
* 依據page對象獲得對應的分頁查詢Sql語句,這裡只做了兩種數據庫類型,Mysql和Oracle
* 其它的數據庫都 沒有停止分頁
*
* @param page 分頁對象
* @param sql 原sql語句
* @return
*/
private String getPageSql(Page<?> page, String sql) {
StringBuffer sqlBuffer = new StringBuffer(sql);
if ("mysql".equalsIgnoreCase(dialect)) {
return getMysqlPageSql(page, sqlBuffer);
} else if ("oracle".equalsIgnoreCase(dialect)) {
return getOraclePageSql(page, sqlBuffer);
}
return sqlBuffer.toString();
}
/**
* 獲得Mysql數據庫的分頁查詢語句
* @param page 分頁對象
* @param sqlBuffer 包括原sql語句的StringBuffer對象
* @return Mysql數據庫分頁語句
*/
private String getMysqlPageSql(Page<?> page, StringBuffer sqlBuffer) {
//盤算第一筆記錄的地位,Mysql中記載的地位是從0開端的。
// System.out.println("page:"+page.getPage()+"-------"+page.getRows());
int offset = (page.getPage() - 1) * page.getRows();
sqlBuffer.append(" limit ").append(offset).append(",").append(page.getRows());
return sqlBuffer.toString();
}
/**
* 獲得Oracle數據庫的分頁查詢語句
* @param page 分頁對象
* @param sqlBuffer 包括原sql語句的StringBuffer對象
* @return Oracle數據庫的分頁查詢語句
*/
private String getOraclePageSql(Page<?> page, StringBuffer sqlBuffer) {
//盤算第一筆記錄的地位,Oracle分頁是經由過程rownum停止的,而rownum是從1開端的
int offset = (page.getPage() - 1) * page.getRows() + 1;
sqlBuffer.insert(0, "select u.*, rownum r from (").append(") u where rownum < ").append(offset + page.getRows());
sqlBuffer.insert(0, "select * from (").append(") where r >= ").append(offset);
//下面的Sql語句拼接以後年夜概是這個模樣:
//select * from (select u.*, rownum r from (select * from t_user) u where rownum < 31) where r >= 16
return sqlBuffer.toString();
}
/**
* 攔阻器對應的封裝原始對象的辦法
*/
public Object plugin(Object arg0) {
// TODO Auto-generated method stub
if (arg0 instanceof StatementHandler) {
return Plugin.wrap(arg0, this);
} else {
return arg0;
}
}
/**
* 設置注冊攔阻器時設定的屬性
*/
public void setProperties(Properties p) {
}
public String getDialect() {
return dialect;
}
public void setDialect(String dialect) {
this.dialect = dialect;
}
public String getPageSqlId() {
return pageSqlId;
}
public void setPageSqlId(String pageSqlId) {
this.pageSqlId = pageSqlId;
}
}
xml設置裝備擺設:
<!-- MyBatis 接口編程設置裝備擺設 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- basePackage指定要掃描的包,在此包之下的映照器都邑被搜刮到,可指定多個包,包與包之間用逗號或分號分隔-->
<property name="basePackage" value="com.yidao.mybatis.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- MyBatis 分頁攔阻器-->
<bean id="paginationInterceptor" class="com.mybatis.interceptor.PageInterceptor">
<property name="dialect" value="mysql"/>
<!-- 攔阻Mapper.xml文件中,id包括query字符的語句 -->
<property name="pageSqlId" value=".*query$"/>
</bean>
Page類
package com.yidao.utils;
/**本身看看,須要甚麼字段加甚麼字段吧*/
public class Page {
private Integer rows;
private Integer page = 1;
private Integer totalRecord;
public Integer getRows() {
return rows;
}
public void setRows(Integer rows) {
this.rows = rows;
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getTotalRecord() {
return totalRecord;
}
public void setTotalRecord(Integer totalRecord) {
this.totalRecord = totalRecord;
}
}
ReflectHelper類
package com.yidao.utils;
import java.lang.reflect.Field;
import org.apache.commons.lang3.reflect.FieldUtils;
public class ReflectHelper {
public static Object getFieldValue(Object obj , String fieldName ){
if(obj == null){
return null ;
}
Field targetField = getTargetField(obj.getClass(), fieldName);
try {
return FieldUtils.readField(targetField, obj, true ) ;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null ;
}
public static Field getTargetField(Class<?> targetClass, String fieldName) {
Field field = null;
try {
if (targetClass == null) {
return field;
}
if (Object.class.equals(targetClass)) {
return field;
}
field = FieldUtils.getDeclaredField(targetClass, fieldName, true);
if (field == null) {
field = getTargetField(targetClass.getSuperclass(), fieldName);
}
} catch (Exception e) {
}
return field;
}
public static void setFieldValue(Object obj , String fieldName , Object value ){
if(null == obj){return;}
Field targetField = getTargetField(obj.getClass(), fieldName);
try {
FieldUtils.writeField(targetField, obj, value) ;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
以上就是本文的全體內容,願望對年夜家的進修有所贊助,也願望年夜家多多支撐。