參考來自:
- 官方文檔的說明:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
- 上篇博客地址:2017.12.14 Mybatis物理分頁插件PageHelper的使用
- pageHelper的源碼解讀:http://blog.csdn.net/u014082617/article/details/71215539
1.需求說明
在上篇關於mybatis的pageHelper的使用中,講述了基本使用。
現在的使用場景爲:有一個web頁面,需要用來測試數據庫的連接信息,而數據庫的類型是可選的。
頁面示例如下:
所以原本的使用方式已經不再試用。因爲需要根據不同的數據庫來生成對應的分頁語句。當然很多數據庫對於通用的分頁語句是支持的,但是爲了考慮以後的拓展,以防有特殊語法的數據庫出現(比如自定義的數據庫),還是採用根據數據庫類型來決定分頁語句。
2.實際使用
2.1 舊代碼和舊配置
見2017.12.14 Mybatis物理分頁插件PageHelper的使用。
2.2 新代碼和新配置
(1)pom.xml
注意:mybatis的pageHelper插件5.x和4.x差別較大,這裏使用的是4.x版本。
1 <dependency> 2 <groupId>com.github.pagehelper</groupId> 3 <artifactId>pagehelper</artifactId> 4 <version>4.1.0</version> 5 </dependency>
(2)SqlMapConfig.xml
這裏和之前的區別就是不再指明dialect。
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> 13 <plugins> 14 <!-- com.github.pagehelper爲PageHelper類所在包名 --> 15 <!--<plugin interceptor="com.github.pagehelper.PageHelper">--> 16 <plugin interceptor="com.baosight.ccs.PageHelper.CCSPageHelper"> 17 <property name="autoRuntimeDialect" value="true"/> 18 <!--<property name="dialect" value="postgresql"/>--> 19 25 <!--設置爲true時,使用rowbounds的時候,會將rowbonds的第一個參數offset當做pageNum使用,和startpage中的pageNum效果一樣--> 26 <!--<property name="offsetAsPageNum" value="true"/>-->
27 <!--設置爲true時,使用rowbounds分頁時就會進行count查詢--> 28 <property name="rowBoundsWithCount" value="true"/>
29 <!--設置爲true時,如果pagesize=0或者rowbounds.limit=0就會查詢出所有的結果--> 30 <property name="pageSizeZero" value="true"/>
31 <property name="reasonable" value="true"/> 32 </plugin> 33 </plugins> 34 </configuration>
(3)pageHelper的源碼解析
對pageHelper的源碼學習後發現,在配置文件中不指明dialect,它的確會自動識別數據庫類型,然後生成對應的分頁語句。但是它只會生成一次,即如果第一次是postgresql,第二次是mysql,它會一直沿用postgresql(第一次生成sqlUtil後會放入sqlUtilMap,後面都會從map中取)。
pageHelper的源碼參考來自:http://blog.csdn.net/u014082617/article/details/71215539
1 PageHelper 2 SqlUtil 3 PageSqlSource 4 PostgreSQLParser
這和我的需求不一致。我需要爲每個可能出現的數據庫類型都生成一個sqlUtil,每種類型都只生成一次,並且放入sqlUtilMap中。因此將配置文件中的參數autoRuntimeDialect設爲true。
(4)pageHelper的重寫
這裏有一個奇怪的問題:並沒有生效。因此重寫pageHelper的代碼,在代碼中將變量autoRuntimeDialect設置爲true,而後生效。
1 package com.baosight.ccs.PageHelper; 2 4 import com.baosight.common.utils.StringUtils; 7 import com.github.pagehelper.PageHelper; 12 import org.apache.ibatis.executor.Executor; 13 import org.apache.ibatis.mapping.MappedStatement; 14 import org.apache.ibatis.plugin.Intercepts; 15 import org.apache.ibatis.plugin.Invocation; 16 import org.apache.ibatis.plugin.Signature; 17 import org.apache.ibatis.session.ResultHandler; 18 import org.apache.ibatis.session.RowBounds; 19 import org.slf4j.Logger; 20 import org.slf4j.LoggerFactory; 31 32 /** 33 * 自定義的分頁攔截器,支持通用的數據庫和自定義的sts數據庫 34 */ 35 @SuppressWarnings("rawtypes") 36 @Intercepts(@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})) 37 public class CCSPageHelper extends PageHelper { 38 //sql工具類 39 private SqlUtil sqlUtil; 40 //屬性參數信息 41 private Properties properties; 42 //配置對象方式 43 private SqlUtilConfig sqlUtilConfig; 44 //自動獲取dialect,如果沒有setProperties或setSqlUtilConfig,也可以正常進行 45 private boolean autoDialect = true; 46 //運行時自動獲取dialect 47 private boolean autoRuntimeDialect = true;
但是運行後出現了另一問題:操作幾次頁面之後,連接超出最大數,連接失敗。
1 Caused by: org.apache.ibatis.exceptions.PersistenceException: 2 ### Error querying database. Cause: java.lang.RuntimeException: com.alibaba.druid.pool.GetConnectionTimeoutException:
wait millis 10000, active 15, maxActive 15
查看pageHelper的源碼發現,本來只執行一次的getSqlUtil,因爲authRuntimeDialect=true,將會每次都去執行,如果有則從map中取,否則創建一個。
而爲了拿到url而獲取的連接並沒有及時關閉,導致連接被多次創建,直到超過最大連接數。因此重寫pageHelper。
1 package com.baosight.ccs.PageHelper; 2 3 //import com.github.pagehelper.Dialect; 4 import com.baosight.common.utils.StringUtils; 5 import com.baosight.xinsight.dbs.datasource.MultiDataSource; 6 import com.github.pagehelper.Dialect; 7 import com.github.pagehelper.PageHelper; 8 import com.github.pagehelper.SqlUtil; 9 import com.github.pagehelper.SqlUtilConfig; 10 import com.github.pagehelper.StringUtil; 11 12 import org.apache.ibatis.executor.Executor; 13 import org.apache.ibatis.mapping.MappedStatement; 14 import org.apache.ibatis.plugin.Intercepts; 15 import org.apache.ibatis.plugin.Invocation; 16 import org.apache.ibatis.plugin.Signature; 17 import org.apache.ibatis.session.ResultHandler; 18 import org.apache.ibatis.session.RowBounds; 19 import org.slf4j.Logger; 20 import org.slf4j.LoggerFactory; 21 22 import java.sql.Connection; 23 import java.sql.DatabaseMetaData; 24 import java.sql.SQLException; 25 import java.util.Map; 26 import java.util.Properties; 27 import java.util.concurrent.ConcurrentHashMap; 28 import java.util.concurrent.locks.ReentrantLock; 29 30 import javax.sql.DataSource; 31 32 /** 33 * 自定義的分頁攔截器,支持通用的數據庫和自定義的sts數據庫 34 */ 35 @SuppressWarnings("rawtypes") 36 @Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})) 37 public class CCSPageHelper extends PageHelper { 38 //sql工具類 39 private SqlUtil sqlUtil; 40 //屬性參數信息 41 private Properties properties; 42 //配置對象方式 43 private SqlUtilConfig sqlUtilConfig; 44 //自動獲取dialect,如果沒有setProperties或setSqlUtilConfig,也可以正常進行 45 private boolean autoDialect = true; 46 //運行時自動獲取dialect 47 private boolean autoRuntimeDialect = true; 48 //緩存 49 private Map<String, SqlUtil> urlSqlUtilMap = new ConcurrentHashMap<String, SqlUtil>(); 50 51 private int count = 0; 52 private static final Logger logger = LoggerFactory.getLogger(CCSPageHelper.class); 106 107 @Override 108 public SqlUtil getSqlUtil(Invocation invocation) { 109 MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; 110 111 String url = null; 112 Connection cn = null; 113 try { 114 DataSource dataSource = ms.getConfiguration().getEnvironment().getDataSource(); 115 cn = dataSource.getConnection(); 116 url = cn.getMetaData().getURL(); 117 } catch (SQLException e) { 118 throw new RuntimeException(e1);130 }finally { 131 try { 132 cn.close(); 133 } catch (SQLException e) { 134 throw new RuntimeException(e); 135 } 136 } 137 138 //其餘和pageHelper一樣171 } 172 }
原本代碼是沒有關閉connection的:
1 String url; 2 DataSource dataSource = ms.getConfiguration().getEnvironment().getDataSource(); 3 try { 4 url = dataSource.getConnection().getMetaData().getURL(); 5 } catch (SQLException e) { 6 throw new RuntimeException(e); 7 }
至此需求完成。