1.簡介
MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄
2. 背景介紹
MyBatis是支持普通SQL查詢,存儲過程和高級映射的優秀持久層框架。MyBatis消除了幾乎所有的JDBC代碼和參數的手共設置以及結果集的檢索,MyBatis使用簡單的XML或者註解用於配置和原始映射,將接口和Java的POJOs(Plain Ordinary Java Objects,普通的 Java對象)映射成數據庫中的記錄。
每個MyBatis應用程序主要利用SqlSessionFactory實例操作數據庫,而SqlSessionFactory實例可以通過SqlSessionFactoryBuilder獲得。SqlSessionFactoryBuilder可以從一個xml配置文件或者一個預定義的配置類實例獲得。
用xml文件構建SqlSessionFactory實例是非常簡單的事情,推薦在這個配置中使用類路徑資源(classpath resource),但你可以使用任何Reader實例,包括用文件路徑或者f://開頭的url創建實例。MyBatis有一個實用類(Resources),它有很多方法,可以方便地從類路徑以及其他位置加載資源。
3.框架特點
- 簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar文件+配置幾個sql映射文件易於學習,易於使用,通過文檔和源代碼,可以比較完全的掌握它的設計思路和實現。
- 靈活:mybatis不會對應用程序或者數據庫的現有設計強加任何影響。 sql寫在xml裏,便於統一管理和優化。通過sql基本上可以實現我們不使用數據訪問框架可以實現的所有功能,或許更多。
- 解除sql與程序代碼的耦合:通過提供DAL層,將業務邏輯和數據訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和代碼的分離,提高了可維護性。
- 提供映射標籤,支持對象與數據庫的orm字段關係映射
- 提供對象關係映射標籤,支持對象關係組建維護
- 提供xml標籤,支持編寫動態sql
4. 功能架構
Mybatis的功能架構分爲三層:
-
API接口層:提供給外部使用的接口API,開發人員通過這些本地API來操縱數據庫。接口層一接收到調用請求就會調用數據處理層來完成具體的數據處理。
-
數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次數據庫操作。
-
基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來作爲最基礎的組件。爲上層的數據處理層提供最基礎的支撐。
5. 體系結構
6. 使用傳統的MyBatis API
通過調用MyBatis中SqlSession對象的方法從而達到與數據庫交互的方式,有一些類似DBUtils的操作!
<!-- namespace="dao接口路徑"
還可以連dao接口都不定義,只定義此mapper文件,則此處的namespace可以隨意寫一個“zhj”,
執行方式sqlSession.selectList("zhj.queryAll")
sqlSession.delete("zhj.deleteUser",1);
基於DAO接口的開發中,底層依然是如上api
-->
<mapper namespace="zhj">
...
<select id="queryAll" resultType="User">
select id,name,gender,create_time as createTime
from tt2
</select>
<!-- 刪除用戶 -->
<delete id="deleteUser" parameterType="int">
delete from tt2
where id=#{id}
</delete>
...
7.MyBatis類型解析
第一:mybatis自動處理java.util.Date
第二:mybatis自動識別1:0爲true/false。數據庫中存1/0,java中直接收true/false
第三:parameterType 和 resultType 解析
<-- parameterType類型:jdk8中基本類型+String :double,float,int,short,boolean,long,date,string --> <select id="queryOne" parameterType="int" resultType="User"> select id,name,gender,create_time as createTime from tt2 where id=#{id} </select>
原理: 1>會類型字符轉小寫,TypeAliasRegistry初始化構造方法中註冊了很多類型,先從中檢查
2>如果如上沒有,則用Resources.classForName(string)通過反射得到類型(核心原理是 Class.forName()類加載)
8.MyBatis事務控制
8.1 起點 :獲取SqlSession對象
// ================ 起點 ===============
sqlSessionFactory.openSession();
8.2 DefaultSqlSessionFactory
// 類:DefaultSqlSessionFactory = SqlSessionFactory的實際類型
public SqlSession openSession() {
// ================ openSession()方法實現 ================
// Executor 隔離級別 自動提交
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 獲得數據庫相關配置
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// ============== 獲得事務對象,(此處需要重點關注,繼續跟進 ================
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 執行器
final Executor executor = configuration.newExecutor(tx, execType);
// 返回構建好的SqlSession,實際類型爲DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
8.3 JdbcTransactionFactory
//類:JdbcTransactionFactory
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
// =============== 構建事務對象,實際類型:JdbcTransaction ==============
return new JdbcTransaction(ds, level, autoCommit);
}
8.4 JdbcTransaction
//類:JdbcTransaction,事務對象
// 構造方法:以上傳來的各項參數:【數據源+事務隔離級別+是否自動提交事務】
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
// 在自己的屬性中 保持瞭如上3項參數
dataSource = ds;
level = desiredLevel;
autoCommmit = desiredAutoCommit;
}
// 事務對象中,會打開一個 Connection,其中設置瞭如上的參數
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
// 1.通過數據源,獲得一個Connection對象
connection = dataSource.getConnection();
if (level != null) {
// 2.設置事務隔離級別
connection.setTransactionIsolation(level.getLevel());
}
// 3.設置事務的自動提交屬性,默認爲false,即mybatis中默認是不自動提交事務的
setDesiredAutoCommit(autoCommmit);
}
// 事務對象中設置 自動提交屬性
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
....
connection.setAutoCommit(desiredAutoCommit);
....
}
// 事務對象中 提交事務
public void commit() throws SQLException {
....
connection.commit();
....
}
//事務對象中 回滾事務
public void rollback() throws SQLException {
...
connection.rollback();
....
}
//事務對象中 關閉資源
public void close() throws SQLException {
...
connection.close();
}
綜上,即,在創建sqlSession時,便會創建一個Transaction對象。
Transaction對象負責創建Connection對象,並借之實現事務管理:事務屬性設置(隔離級別,自動提交)
事務行爲控制(提交,回滾)
9.開發規範
- mapper接口的全限定名要和mapper映射文件的namespace的值相同。
- mapper接口的方法名稱要和mapper映射文件中的statement的id相同;
- mapper接口的方法參數只能有一個,且類型要和mapper映射文件中statement的parameterType的值保持一致。
- mapper接口的返回值類型要和mapper映射文件中statement的resultType值或resultMap中的type值保持一致;
10. 方法參數細節
如果DAO方法中只有一個參數,則在mapper文件中【#{隨意}】:如下的
#{abc},#{def},#{hig}
如是。
public List<User> xx(Integer id);
public List<User> xx(String name);
public List<User> xx(Date createTime);
<select id="xx" parameterType="int" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from tt2
where id=#{abc}
</select>
<select id="xx" parameterType="string" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from tt2
where name=#{def}
</select>
<select id="xx" parameterType="date" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from tt2
where create_time=#{hig}
</select>
當方法中有多個參數時,不能再向如上那樣隨意,且也不能如常規課程中直接使用參數名;有兩種解決方案:
1> 使用
#{arg0} #{arg1} #{arg2}
2> 使用
@Param
註解
public List<User> test1(@Param("id")Integer id, @Param("name") String name,@Param("gender") Boolean gender);
public List<User> test2(Integer id, String name,Boolean gender);
<select id="test1" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from tt2
where id=#{id} and name=#{name} and gender=#{gender}
</select>
<select id="test2" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from tt2
where id=#{arg0} and name=#{arg1} and gender=#{arg2}
</select>
11. # 和 $ 區分
//如果用$,則必須用 @Param註解,否則${name}會認爲是要從參數中取名爲name的屬性
public List<User> test3(@Param("name") String a);
<!-- 注意${} 就是在做字符拼接,所以此處用了【‘${name}’】而不是【${name}】
類比jdbc的sql語句的拼接:
String name="zhj";
String sql = "select ... from tt2 where name='"+name+"'";//此處是要加單引號的
注意:sql拼接時,有sql注入的風險
-->
<select id="test3" parameterType="string" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from tt2
where name = '${name}'
</select>
<!-- 注意#{} 就是在做佔位符,所以此處用了【#{name}】而不是【’#{name}‘】
類比jdbc的sql語句的拼接:
String name="zhj";
String sql = "select ... from tt2 where name=?“;//此處是不加單引號的
...
prepareStatement.executeQuery(sql,name);//佔位符賦值
-->
<select id="test3" parameterType="string" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from tt2
where name = #{name}
</select>
必須使用$場景:
<select id="test3" parameterType="string" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from tt2
order by id ${name}
</select>
<select id="test4" parameterType="string" resultType="com.zhj.domain.User">
select id,name,gender,create_time as createTime
from ${tn}
where name = #{name}
</select>
<!-- 用在列名上亦可 -->
userDAO.test3("desc");
userDAO.test3("asc");
userDAO.test4("t_user");
userDAO.test4("t_admin");
12. Statement 選型
發生在初始加載過程中,每個MappedStatement中存儲自己的 StatementType
MyBatis中,除非明確設置
statementType="STATEMENT"
否則默認都是用 PreparedStatement可參見 類:
org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode()
{ //在mapper標籤中獲取’statementType‘屬性值,如果沒有設置則默認取’StatementType.PREPARED.toString()‘ StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); }
<select id="test3" parameterType="string" resultType="com.zhj.domain.User" statementType="STATEMENT">
select id,name,gender,create_time as createTime
from tt2
where name = '${name}'
</select>
真正執行一個MappedStatement時:
SimpleExecutor中:
//doQuery() 或 doUpdate() 方法中
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler
resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 通過當前MappedStatement=ms 中的StatementType屬性,創建一個StatementHandler
// 會創建一個RoutingStatementHandler,其構造方法中會根據ms的StatementType構建:
// SimpleStatementHandler 或 PreparedStatementHandler.他們各自負責創建一種Statement
// 如下hanlder屬性中存儲的是一個RoutingStatementHandler對象,可實際工作時,是其內部的
// SimpleStatementHandler 或 PreparedStatementHandler完成執行。
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds,
resultHandler, boundSql);
// 此方法中的handler會負責創建Statement,即,決定用哪種Statement
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
}....
13. 關聯關係映射多種方式
//namespace = com.zhj.dao.UserDAO
<resultMap id="person_passport" type="Person">
<id property="id" column="pid"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<!-- 常規配置
<association property="passport" javaType="Passport">
<id property="id" column="passid"/>
<result property="note" column="note"/>
<result property="createTime" column="create_time"/>
</association>-->
<!-- 通過column+select,column作爲select的輸入參數,select中使用的查詢可以來自其他ns
<association property="passport" column="passid" select="com.zhj.dao.UserDAO2.queryOne"/> -->
<!-- 嵌套resultMap,和常規配置類似,不過此種abc可以複用 -->
<association property="passport" resultMap="abc"/>
</resultMap>
<resultMap id="abc" type="Passport">
<id property="id" column="passid"/>
<result property="note" column="note"/>
<result property="createTime" column="create_time"/>
</resultMap>
<!-- 自動映射 -->
<resultMap id="person_passport" type="Person" autoMapping="true">
<id property="id" column="pid"/>
<!-- 自動映射 -->
<association property="passport" javaType="Passport" autoMapping="true">
<id property="id" column="passid"/>
</association>
</resultMap>
14. 細節參數
1. 連接池
連接池 |
---|
<!-- 默認是如上所示開啓的,可以通過以下配置關閉二級緩存 --> <settings> <setting name="cacheEnabled" value="false"/> </settings>
<dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> <property name="poolMaximumIdleConnections" value="9"/> 增設參數 </dataSource>
2. 延遲加載細節
延遲加載_Mapper映射 |
---|
15. 緩存細節
緩存必須要序列化pojo對象
1. 一級緩存
一級緩存 |
---|
2. 二級緩存開關
二級緩存開關 和 存儲位置 |
---|
3. 二級緩存存儲位置
二級緩存,存於SqlSessionFactory中 |
---|
4.二級緩存存儲結構
二級緩存存儲結構 ,以mapper(dao).xml文件爲一個大map,對應的value是一個緩存,緩存中還有小map,存儲的key是sql語句,存儲的value是數據 |
---|
5. 二級緩存生效時間
二級緩存需要提交事務,纔會生效。
原因:用於全局共享的數據,還是在操作數據的事務結束後再生效爲好。
實現:見下圖:
二級緩存細節 |
---|
注意:
查詢時,數據存入一級緩存中。
如果sqlsession在commit或close後,數據存入進入二級緩存。
如果sqlsession執行rollback,則數據不會進入二級緩存。
6. 二級緩存-cache-ref
和關係屬性相關
注意如果
<collection>
中沒有使用select
關聯查詢,則不存在此問題。
<mapper namespace="com.zhj.dao.UserDAO">
<cache/>
<resultMap id="user_orders" type="User">
<id property="id" column="uid"/>
<result property="name" column="name"/>
<result property="gender" column="gender"/>
<result property="registTime" column="registTime"/>
<collection property="orders" select="com.zhj.dao.OrderDAO.queryOrderOfUser" column="id" fetchType="eager"/>
</resultMap>
<select id="queryOne" parameterType="int" resultMap="user_orders">
select id,name,gender,regist_time as registTime
from t_user
where id=#{id}
</select>
</mapper>
<mapper namespace="com.zhj.dao.OrderDAO">
<!-- 使用cache-ref 則OrderDAO的緩存數據,會存放於com.zhj.dao.UserDAO分支下,
與UserDAO的緩存數據存儲於同一個Perpetual對象中
-->
<cache-ref namespace="com.zhj.dao.UserDAO"/>
<select id="queryOrderOfUser" parameterType="int" resultType="Order">
select id as oid,note,price,create_time as createTime
from t_order
where user_id = #{userId}
</select>
</mapper>
UserDAO userDAO = sqlSession1.getMapper(UserDao.class);
userDAO.queryOne(1);//User 和 關係屬性Order 都被緩存
sqlSession.commit();
OrderDAO orderDAO = sqlSession2.getMapper(OrderDAO.lass);
orderDAO.queryOrderOfUser(1); //有二級緩存數據可用 (OrderDAO中必須有 <cache>或<cache-ref>)
UserDAO userDAO = sqlSession1.getMapper(UserDao.class);
userDAO.queryOne(1);
//數據改動,此時會清空User緩存,並清空Order緩存(因爲order中是 <cache-ref>,如果是<cache>則此處只會清空User緩存)
//除非使用的是連表查詢,比如inner join,詳細可以參考《MyBatis高級應用》
userDAO.insertUser(new User(null,"new_user",true,new Date()));
sqlSession.commit();
OrderDAO orderDAO = sqlSession2.getMapper(OrderDAO.lass);
orderDAO.queryOrderOfUser(1); //重新查詢數據庫,<cache-ref namespace="A"/> 會由於A的改動而被清空緩存
16. idea設置sql方言
設置方言後,mapper文件中的sql語句就不會報黃啦
17. bind
<bind name="name_pattern" value="'%'+name+'%'"/>
創建一個變量,並綁定在當前上下文
<select id="queryUsers" parameterType="User" resultType="User">
<bind name="name_pattern" value="'%'+name+'%'"/>
SELECT id,name,gender,regist_time
FROM t_user2
<trim prefix="where" prefixOverrides="and|or">
<if test="name != null">
name like #{name_pattern}
</if>
<if test="id>=10">
OR id>#{id}
</if>
<if test="gender == false">
AND gender=#{gender}
</if>
</trim>
</select>
18. Foreach-Map(瞭解)
public int insertUsers(Map<String,List<User>> users);
<insert id="insertUsers">
insert into t_user2 (name,gender,regist_time) values
<foreach collection="userMaps" item="user" index="ind" close="" open="" separator=",">
(#{user.name},#{user.gender},#{user.registTime,jdbcType=DATE})
</foreach>
</insert>
HashMap<String, List<User>> userMaps = new HashMap<>();
userMaps.put("users",users);
userDAO.insertUsers(userMaps);
19. 配置文件加載
SqlSessionFactoryBuilder().
build(reader);
|-- XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);//封裝解析器
return build(parser.
parse()
);//解析配置,返回一個SqlSessionFactory.則SSF是由mybatis的全部配置支撐的。 |-- XMLConfigBuilder .
parse()
//解析配置文件 |-- XMLConfigBuilder .
parseConfiguration
(parser.evalNode("/configuration")); //解析配置,從configuration標籤開始,解析整個配置文件
//其中加載配置個各個節點都是用了單獨的方法
//加載properties標籤,加載typealias標籤等,可以在每個加載方法中看到標籤解析的細節。
//比如properties標籤加載時,會解析 resource屬性 或 url屬性,以及會如何判斷用戶用的是哪個。
20. 映射文件加載
XMLConfigBuilder .
parseConfiguration
(parser.evalNode("/configuration"));//其中有一項是加載mapper文件 |-- XMLConfigBuilder .
mapperElement
(root.evalNode(“mappers”)); //加載mapper文件 |-- XMLMapperBuilder.
buildStatementFromContext(context.evalNodes("select|insert|update|delete"))
//其中會加載所有的 select,update,delete,insert標籤, 返回
List<XNode>
,然後遍歷該List,執行下一步 |-- XMLStatementBuilder.
parseStatementNode()
//每遍歷一個XNode執行一次該方法,解析出一個MappedStatement //其中會解析上一步加載的每個標籤(
XNode
),和其中的屬性(resultTeype,id,paremeterType…), //其中還有很重要的一步,是決定當前mapstatement使用的Statement類型。
//解析完畢後,有一步:
|-- parseStatementNode()#
builderAssistant.addMappedStatement(XNode中的所有屬性 id,resultTpe,...)
|-- MapperBuilderAssistant.
addMappedStatement()
|-- 其中會根據傳入的(id,resultType,…)構建一個
MappedStatement.Builder statementBuilder
然後: |--
statementBuilder.build()
返回一個MappedStatement |--
configuration.addMappedStatement(statement);
//將MappedStatement 存入Configuration對象
21. 動態sql解析
在映射文件加載過程中,還會有sql語句的解析,主要是針對,動態sql的解析
|-- XMLStatementBuilder.
parseStatementNode()
中: |--
langDriver.createSqlSource(configuration, context, parameterTypeClass);
|-- 如上方法實現中:
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
|-- XMLScriptBuilder.
parseScriptNode()
|-- 如上方法中:
List contents = parseDynamicTags(context);//解析動態sql
if (isDynamic) {// 如果有 $ 或 動態標籤
sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); //其中解析 #{ } }
//得到一個SqlSource,其中存儲了 : sql語句;sql中的參數名(#{xx})
//還持有當前SqlSessionFactory中的configuration對象( 緩存信息,延遲加載信息等配置都可以在其中獲)
【parseDynamicTags方法中調用nodeHandlers()//獲取所有的動態sql標籤處理的 handler,每種動態標籤都對應一種handler
handler會解析動態標籤】
22. SqlSession執行
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
|-- userDAO.queryOne(1); //mapper類型:MapperProxy,基於jdk動態代理構建
|-- MapperProxy.
invoke
//其中獲得的MapperMethod對象( 其中封裝了:當前方法接口,當前方法對象, Configuration)
|-
MapperMethod.execute()
//其中會真正執行對應方法,執行的真正實現者是sqlSession.xxxx();[insert,udate,delete,selectOne,…]
|- 在傳統API中調用
Excutor(CachingExcutor=>SimpleExcutor)
|-
PrepareStatementHandler
=> 獲取PrepareStatement.execute()
23. PageHelper
1. 使用過程
<!--如果jar包無法導入依賴,則下載3.4.2版本,然後在dos命令下輸入,在idea更新倉庫-->
<!--mvn install:install-file -DgroupId=com.github -DartifactId=pagehelper -Dversion=5.1.9 -Dpackaging=jar -Dfile=pagehelper-5.1.9.jar-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>RELEASE</version>
</dependency>
<!--pageHelper-->
<!--4-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.2.1</version>
</dependency>
<!--5-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.6</version>
</dependency>
<!--
plugins在配置文件中的位置必須符合要求,否則會報錯,順序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper爲PageHelper類所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 頁號自動迴歸到合理數值,可以不寫 -->
<!--<property name="reasonable" value="true"/>-->
</plugin>
</plugins>
<plugins>
<!--5.0之前版本-->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!--方言 -->
<property name="dialect" value="mysql"/>
<!--也大小爲0,查詢所有的數據 -->
<property name="pageSizeZero" value="true"/>
<!--查詢合理化 index<1 查詢第一頁 index>pageCount 查詢最後一頁 -->
<property name="reasonable" value="true"/>
</plugin>
<!--5.0之前版本 方言去掉,可以自動識別,不然報錯-->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--也大小爲0,查詢所有的數據 -->
<property name="pageSizeZero" value="true"/>
<!--查詢合理化 index<1 查詢第一頁 index>pageCount 查詢最後一頁 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
<!-- spring等價配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注意其他配置 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor"></bean>
</array>
</property>
</bean>
//使用:
PageHelper.startPage(2,3);// 第2頁,每頁3條數據
PageHelper.orderBy("regist_time");//可以選擇設置排序
List<User> users = mapper.queryAllUsers();//PageHelper後的第一個查詢語句,會被PageHelp增強處理(可觀測mysql日誌)
for (User user : users) {// users中已經是分頁數據
System.out.println(user);
}
//包裝一個PageInfo,其中會持有所有分頁會用到的信息:當前頁號,每頁多少條,共多少頁,是否爲第一頁/最後一頁,是否有下一頁等。
PageInfo<User> pageInfo=new PageInfo<User>(users);
PageInfo對象 概覽 |
---|
注意:如果是多表查詢,則會有如下效果:
select t_user.id,name,gender,regist_time,
t_order.id orderId,price,note,create_time
from t_user JOIN t_order
ON t_user.id = t_order.user_id LIMIT 2, 2
#是對大表做了分頁,此時數據可能不完整,比如用戶有10個訂單,卻只能查到2個,或部分。
2. 重要提示
PageHelper.startPage
方法重要提示只有緊跟在
PageHelper.startPage
方法後的第一個Mybatis的**查詢(Select)**方法會被分頁,查詢語句和設置分頁調節語句順序不能反。請不要配置多個分頁插件
請不要在系統中配置多個分頁插件(使用Spring時,
mybatis-config.xml
和Spring<bean>
配置方式,請選擇其中一種,不要同時配置多個分頁插件)!分頁插件不支持帶有
for update
語句的分頁對於帶有
for update
的sql,會拋出運行時異常,對於這樣的sql建議手動分頁,畢竟這樣的sql需要重視。分頁插件不支持嵌套結果映射
由於嵌套結果方式會導致結果集被摺疊,因此分頁查詢的結果在摺疊後總數會減少,所以無法保證分頁結果數量正確。