激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - Mybatis全面分頁插件

Mybatis全面分頁插件

2020-06-06 14:57junehappylove JAVA教程

這篇文章主要為大家詳細介紹了Mybatis全面分頁插件的使用方法,比較適用于在分頁時候進行攔截,感興趣的小伙伴們可以參考一下

根據下面分頁的思想,很容易實現Mybitas的多租戶設計。 

使用Mybatis提供的攔截器。對分頁的SQL語句通過封裝處理,處理成不同的分頁sql。 

本例已經實現了對Mysql和Oracle的分頁功能。注意下面的引用包,不要引用錯了。 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
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配置: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 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類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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();
 }
 }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本成人在线免费 | 92看片淫黄大片一级 | 日本黄色免费片 | 亚洲一区国产视频 | 91福利国产在线观一区二区 | 国产精品看片 | 黄视频网站免费在线观看 | 一级毛片在线观看免费 | 一本色道久久久888 香蕉视频99 | 免费在线观看毛片视频 | 成人午夜视频免费在线观看 | 亚洲国产综合在线观看 | 特级黄一级播放 | 亚洲精品成人久久久 | 国产高清美女一级毛片 | 欧美一级黄色片免费观看 | 素人视频在线观看免费 | 激情夜色 | 亚州精品在线视频 | 欧美视频在线一区二区三区 | 国产精品视频中文字幕 | 精品国产一区在线 | 一级黄色大片在线观看 | av成人在线电影 | 99精品无人区乱码在线观看 | 免费黄色短视频网站 | 成人福利在线看 | 视频在线亚洲 | 羞羞视频免费观看入口 | 视频一区二区三区在线播放 | 色播视频在线播放 | 国产精品9191 | 国产精品久久久久久一区二区三区 | 久久久久久久免费视频 | 亚洲精中文字幕二区三区 | www.91操| 欧美亚洲国产成人 | 久草在线看片 | 国产又粗又爽又深的免费视频 | 久久久www成人免费精品 | 青草av.久久免费一区 |