Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
想必大家都有過這樣的體驗,在使用Mybatis時,最頭痛的就是寫分頁了,需要先寫一個查詢count的select語句,然后再寫一個真正分頁查詢的語句,當查詢條件多了之后,會發現真的不想花雙倍的時間寫 count 和 select,幸好我們有 pagehelper 分頁插件,pagehelper 是一個強大實用的 MyBatis 分頁插件,可以幫助我們快速的實現MyBatis分頁功能,而且pagehelper有個優點是,分頁和Mapper.xml完全解耦,并以插件的形式實現,對Mybatis執行的流程進行了強化,這有效的避免了我們需要直接寫分頁SQL語句來實現分頁功能。那么,接下來我們就來一起體驗下吧。
接下來,我們就通過實際案例來講解如何使用pagehelper來實現MyBatis分頁,為了避免重復篇幅,此篇教程的源碼基于《Spring Boot:整合MyBatis框架》一篇的源碼實現,讀者請先參考并根據教程鏈接先行獲取基礎源碼和數據庫內容。
首先,我們需要在 pom.xml 文件中添加分頁插件依賴包。
pom.xml
<!-- pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
然后在 application.yml 配置文件中添加分頁插件有關的配置。
application.yml
# pagehelper
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
首先,在 DAO 層添加一個分頁查找方法。這個查詢方法跟查詢全部數據的方法除了名稱幾乎一樣。
SysUserMapper.java
package com.louis.springboot.demo.dao;
import java.util.List;
import com.louis.springboot.demo.model.SysUser;
public interface SysUserMapper {
int deleteByPrimaryKey(Long id);
int insert(SysUser record);
int insertSelective(SysUser record);
SysUser selectByPrimaryKey(Long id);
int updateByPrimaryKeySelective(SysUser record);
int updateByPrimaryKey(SysUser record);
/**
* 查詢全部用戶
* @return
*/
List<SysUser> selectAll();
/**
* 分頁查詢用戶
* @return
*/
List<SysUser> selectPage();
}
然后在 SysUserMapper.xml 中加入selectPage的實現,當然你也可以直接用@Select注解將查詢語句直接寫在DAO代碼,但我們這里選擇寫在XML映射文件,這是一個普通的查找全部記錄的查詢語句,并不需要寫分頁SQL,分頁插件會攔截查詢請求,并讀取前臺傳來的分頁查詢參數重新生成分頁查詢語句。
SysUserMapper.xml
<select id="selectPage" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from sys_user
</select>
服務層通過調用DAO層代碼完成分頁查詢,這里統一封裝分頁查詢的請求和結果類,從而避免因為替換ORM框架而導致服務層、控制層的分頁接口也需要變動的情況,替換ORM框架也不會影響服務層以上的分頁接口,起到了解耦的作用。
SysUserService.java
package com.louis.springboot.demo.service;
import java.util.List;
import com.louis.springboot.demo.model.SysUser;
import com.louis.springboot.demo.util.PageRequest;
import com.louis.springboot.demo.util.PageResult;
public interface SysUserService {
/**
* 根據用戶ID查找用戶
* @param userId
* @return
*/
SysUser findByUserId(Long userId);
/**
* 查找所有用戶
* @return
*/
List<SysUser> findAll();
/**
* 分頁查詢接口
* 這里統一封裝了分頁請求和結果,避免直接引入具體框架的分頁對象, 如MyBatis或JPA的分頁對象
* 從而避免因為替換ORM框架而導致服務層、控制層的分頁接口也需要變動的情況,替換ORM框架也不會
* 影響服務層以上的分頁接口,起到了解耦的作用
* @param pageRequest 自定義,統一分頁查詢請求
* @return PageResult 自定義,統一分頁查詢結果
*/
PageResult findPage(PageRequest pageRequest);
}
服務實現類通過調用分頁插件完成最終的分頁查詢,關鍵代碼是 PageHelper.startPage(pageNum, pageSize),將前臺分頁查詢參數傳入并攔截MyBtis執行實現分頁效果。
SysUserServiceImpl.java
package com.louis.springboot.demo.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.louis.springboot.demo.dao.SysUserMapper;
import com.louis.springboot.demo.model.SysUser;
import com.louis.springboot.demo.service.SysUserService;
import com.louis.springboot.demo.util.PageRequest;
import com.louis.springboot.demo.util.PageResult;
import com.louis.springboot.demo.util.PageUtils;
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Override
public SysUser findByUserId(Long userId) {
return sysUserMapper.selectByPrimaryKey(userId);
}
@Override
public List<SysUser> findAll() {
return sysUserMapper.selectAll();
}
@Override
public PageResult findPage(PageRequest pageRequest) {
return PageUtils.getPageResult(pageRequest, getPageInfo(pageRequest));
}
/**
* 調用分頁插件完成分頁
* @param pageQuery
* @return
*/
private PageInfo<SysUser> getPageInfo(PageRequest pageRequest) {
int pageNum=pageRequest.getPageNum();
int pageSize=pageRequest.getPageSize();
PageHelper.startPage(pageNum, pageSize);
List<SysUser> sysMenus=sysUserMapper.selectPage();
return new PageInfo<SysUser>(sysMenus);
}
}
在控制器SysUserController中添加分頁查詢方法,并調用服務層的分頁查詢方法。
SysUserController.java
package com.louis.springboot.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.louis.springboot.demo.service.SysUserService;
import com.louis.springboot.demo.util.PageRequest;
@RestController
@RequestMapping("user")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@GetMapping(value="/findByUserId")
public Object findByUserId(@RequestParam Long userId) {
return sysUserService.findByUserId(userId);
}
@GetMapping(value="/findAll")
public Object findAll() {
return sysUserService.findAll();
}
@PostMapping(value="/findPage")
public Object findPage(@RequestBody PageRequest pageQuery) {
return sysUserService.findPage(pageQuery);
}
}
分頁查詢請求封裝類。
PageRequest.java
package com.louis.springboot.demo.util;
/**
* 分頁請求
*/
public class PageRequest {
/**
* 當前頁碼
*/
private int pageNum;
/**
* 每頁數量
*/
private int pageSize;
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum=pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize=pageSize;
}
}
分頁查詢結果封裝類。
PageResult.java
package com.louis.springboot.demo.util;
import java.util.List;
/**
* 分頁返回結果
*/
public class PageResult {
/**
* 當前頁碼
*/
private int pageNum;
/**
* 每頁數量
*/
private int pageSize;
/**
* 記錄總數
*/
private long totalSize;
/**
* 頁碼總數
*/
private int totalPages;
/**
* 數據模型
*/
private List<?> content;
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum=pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize=pageSize;
}
public long getTotalSize() {
return totalSize;
}
public void setTotalSize(long totalSize) {
this.totalSize=totalSize;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages=totalPages;
}
public List<?> getContent() {
return content;
}
public void setContent(List<?> content) {
this.content=content;
}
}
分頁查詢相關工具類。
PageUtils.java
package com.louis.springboot.demo.util;
import com.github.pagehelper.PageInfo;
public class PageUtils {
/**
* 將分頁信息封裝到統一的接口
* @param pageRequest
* @param page
* @return
*/
public static PageResult getPageResult(PageRequest pageRequest, PageInfo<?> pageInfo) {
PageResult pageResult=new PageResult();
pageResult.setPageNum(pageInfo.getPageNum());
pageResult.setPageSize(pageInfo.getPageSize());
pageResult.setTotalSize(pageInfo.getTotal());
pageResult.setTotalPages(pageInfo.getPages());
pageResult.setContent(pageInfo.getList());
return pageResult;
}
}
啟動應用,訪問:localhost:8088/swagger-ui.html,找到對應接口,模擬測試,結果如下。
參數:pageNum: 1, pageSize: 5
參數:pageNum: 2, pageSize: 4
學習更多JAVA知識與技巧,關注與私信博主(學習)
yBatis開發Dao有兩種方式:原始Dao的開發方式,Mapper動態代理的方式。
兩種開發方式在企業開發中均有運用。都要掌握。
使用myBatis時,需要對其進行一個全局的管理配置。
sqlMappingConfig.xml
[html] view plain copy
其中關于數據庫的一些配置,抽取出來了,這樣可以減少硬編碼:
db.properties:
[plain] view plain copy
1.原始Dao的開發方式
原始Dao的開發,需要程序員自己編寫Dao的接口和Dao接口的實現類。(一下以一個按照id查詢為例說明)
1.映射文件:
[html] view plain copy
2.Dao接口:
[java] view plain copy
3.Dao接口的實現
[java] view plain copy
通過上面的例子,我們可以總結:
原始Dao開發存在以下問題:
Dao的方法存在重復代碼,:通過sqlSessionFactory創建SqlSession,調用sqlSession數據庫的操作方法。
調用sqlSession操作數據庫的方法(第一個參數)時,硬編碼,不利于系統的維護。
SqlSessionFactory的使用范圍:
通過SQLSessionFactoryBuilder創建會話工廠SQLSessionFactory。將SQLSessionFactoryBuilder當做一個工具類來使用即可,不需要使用單例模式去管理。
通過SQLSessionFactory創建sqlSession,使用單例模式管理SQLSessionFactory,(工廠一旦被創建,使用一個實例)。將來mybatis與spring整合后,使用單例模式來管理sqlSessionFactory。
sqlSession是線程不安全的,在sqlsession中不但存在操作數據庫的方法,還有數據域屬性。sqlSession最佳應用場合,是在方法體內,定義成局部變量使用。
2.Mapper代理的方式:
mapper動態代理實現的原理:
mapper接口開發方式,只需要程序員編寫mapper接口(相當于Dao接口),有mybatis框架根據接口定義創建接口的動態代理對象,代理的方法體與上面Dao實現類中的方法體相類似
Mapper接口開發要符合一下規范:
1.Mapper.xml中namespace路徑與mapper接口路徑相同
2.mapper接口中的方法名,與Mapper.xml中對應statement的id相同
3.mapper接口中方法的輸入參數類型和mapper.xml中定義的對應sql的ParameterType的類型相同
4.mapper接口中方法的返回值類型和mapper.xml中定義的對應sql的ResultType的類型相同
1.Mapper.xml寫法:(與上面User.xml不同的是namespace做了定義)
[html] view plain copy
2.mapper.java(接口文件)
[java] view plain copy
3.測試文件:(通過junit)
[java] view plain copy
[java] view plain copy
[java] view plain copy
[java] view plain copy
4.對Mapper動態代理的一個小結:
1.使用mapper代理時,輸入參數類型可以使用pojo的包裝對象,或是mapper對象,保證dao的同性。
2.selectOne和selectList
動態代理對象通過sqlsession.selectOne和sqlsession.selectList,是通過對返回值類型進行判斷而選擇調用哪一個方法。如果返回單個對象則調用selectOne,返回集合對象則調用selectList
積月累,方成大器。
各大網站:
Mybatis官方技術文檔 https://mybatis.org/mybatis-3/zh/index.html
Mybatis源碼包下載:https://github.com/mybatis/mybatis-3/releases
所有代碼及筆記gitee路徑:https://gitee.com/simple-coding/all-demo-code.git
要使用Mybatis開發只需引入mybatis-x.x.x.jar即可,想其它的日志等,如果需要可以另外引入包進行擴展。
本案例使用最新版本:mybatis-3.5.9.jar
如果我們不引入第三方的日志包,那mybatis就不能對日志進行輸出了嗎?
首先,要知道Mybatis不引入第三方日志包,也能進行日志輸出(默認日志功能禁用),只不過只能進行控制臺輸出,不能進行文件輸出;另外,對于第三方日志,mybatis有特定的加載順序,如果引入了多個日志實現也可以在主配置中通過settings進行配置,告訴mybatis我要用哪個進行輸出。
最新版本,第三方加載順序如下、源碼如下圖(不同的mybatis版本可能不一樣):
MyBatis 內置日志工廠基于運行時自省機制選擇合適的日志工具。它會使用第一個查找得到的工具(按上文列舉的順序查找)。如果一個都未找到,日志功能就會被禁用。
Mybatis日志加載順序
Mybatis支持哪些日志的配置,那就要看我們的主配置文件中settings配置了,配置如下
日志實現指定
說白了就是支持常用實現JUL(java util logging)、logback、log4j、log4j2,而SLF4J和COMMONS_LOGGING其實就是日志門面技術,也就是接口。關于日志框架我們后續,會單獨講一下。
STDOUT_LOGGING :是Mybatis內置實現,只做控制臺輸出。配置只需一步,也不需要引入第三方依賴:
<setting name="logImpl" value="STDOUT_LOGGING"/>
運行結果
首先聲明SLF4J是日志門面技術,通過一系列中間轉換操作,可以適配現有的任何日志技術。本節只做簡單的配置實現logback。
<!--這個依賴直接包含了 logback-core實現 以及 slf4j-api日志門面的依賴-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
不配置也行,第三方日志只有一個日志實現的情況下,有默認加載順序,詳見:第二部分、日志配置
<!--使用logback-->
<setting name="logImpl" value="SLF4J"/>
開啟debug級別,用于輸出SQL語句。
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定義日志文件的存儲地址 勿在 LogBack 的配置中使用相對路徑-->
<property name="LOG_HOME" value="logs"/>
<!-- 控制臺輸出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件輸出的文件名-->
<fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/smifc.log.%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>20</maxHistory>
<totalSizeCap>100GB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志輸出級別 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</configuration>
logback
JDK_LOGGING是個啥?其實它就是JDK自帶的日志。也不需要我們引入第三方包。
jdk自帶的日志,配置文件加載有三種方法:1.默認路徑【JRE_HOME/lib/logging.properties】;2.自定義加載配置文件路徑【java.util.logging.config.file】;3.自定義加載配置類【java.util.logging.config.class】需要通過readConfiguration(InputStream) 實現
看下JDK源碼LogManager加載配置實現:
JDK源碼LogManager加載配置實現
自定義加載路徑
handlers=java.util.logging.ConsoleHandler
.level=ALL
#mapper的命名空間
test=ALL
java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tT.%1$tL %4$s %3$s - %5$s%6$s%n
<setting name="logImpl" value="JDK_LOGGING"/>
運行結果
LOG4J(log4j.properties)、LOG4J2(log4j2.xml)這兩個實現和logback實現大同小異,直接引入實現jar包,變更主配置實現即可。
引入jar包
指定日志實現
配置文件
運行結果
COMMONS_LOGGING (commons-logging.properties)是一個日志門面技術,也就是接口,其自身自帶簡單實現org.apache.commons.logging.impl.SimpleLog也支持jdk的日志實現。這里我們引入commons-logging包使用其的簡單日志實現。
另外,支持的實現如下:
//LogFactoryImpl.java
private static final String[] classesToDiscover=new String[]{"org.apache.commons.logging.impl.Log4JLogger", "org.apache.commons.logging.impl.Jdk14Logger", "org.apache.commons.logging.impl.Jdk13LumberjackLogger", "org.apache.commons.logging.impl.SimpleLog"};
<!--commons-logging-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
org.apache.commons.logging.simplelog.defaultlog=all
org.apache.commons.logging.simplelog.log.test=all
org.apache.commons.logging.simplelog.showlogname=true
org.apache.commons.logging.simplelog.showShortLogname=true
org.apache.commons.logging.simplelog.showdatetime=true
org.apache.commons.logging.simplelog.dateTimeFormat=yyyy-MM-dd hh:mm:ss
<!--使用COMMONS_LOGGING簡單日志-->
<setting name="logImpl" value="COMMONS_LOGGING"/>
運行結果
在以上的日志配置中,我引入了所有的日志實現,通過控制主配置文件中配置
<setting name="logImpl" value="XXX"/>
達到,具體要使用哪個的目的。如果整合Mybatis時有多個日志實現,可通過此配置控制。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。