MyBatis分頁插件版本問題
Cause: java.lang.ClassCastException: com.github.pagehelper.PageHelper cannot be cast to org.apache.ibatis.plugin.Interceptor
問題背景:
在某商城的學習過程中用到的分頁插件的版本是4.0以下的版本,基本不支持多邊查詢的分頁,學習資料中是經過高手重新編寫的,后來查資料發現最新的5.0及以上的版本已經有了解決方案,便直接用了5.0的版本,除了引用這個pagehelper-x.x.x.jar 和其依賴包 jsqlparser-0.9.5.jar,開始的配置就照着原來的配置在SqlMapConfig.xml進行了配置
一、5.0以下版本分頁插件的xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<!-- com.github.pagehelper 為 PageHelper 類所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 設置數據庫類型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL 六種數據庫-->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
</configuration>
在5.0版本以下xml配置文件中需要配置數據庫名
二 、5.0以上版本分頁插件的xml配置文件
<configuration>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
在5.0版本以上xml配置文件中是不需要配置數據庫名
三、配置錯誤時的運行錯誤日志
一月 09, 2021 10:31:01 下午 org.apache.catalina.core.StandardContext listenerStart
嚴重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in URL [jar:file:/E:/apache-maven-3.6.3-bin/m2/com/pinyougou/pinyougou-dao/1.0-SNAPSHOT/pinyougou-dao-1.0-SNAPSHOT.jar!/spring/applicationContext-dao.xml]: Invocation of init method failed; nested exception is org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [mybatis/SqlMapConfig.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.ClassCastException: com.github.pagehelper.PageHelper cannot be cast to org.apache.ibatis.plugin.Interceptor
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
四、分析
合后的mybaties和spring都是通過配置文件聯系起來的,如果配置文件有問題一些映射就會出錯,也就會導致一些class或者bean無法成功生成或找到,經過多次查找,發現是分頁的插件配置的問題,如果把mybatis的分頁插件的配置注釋掉就能運行。
異常信息
java.lang.ClassCastException: com.github.pagehelper.PageHelper cannot be cast to
org.apache.ibatis.plugin.Interceptor
mybatis的配置文件,SqlMapConfig.xml配置可能有誤,核查了一遍依然報錯,考慮版本的問題呢,一搜看到了這篇博客,他用的是PageInterceptor,我用的是pagehelper。
五、直接原因:
PageHelper5.0版本pagehelper是繼承了PageMethod和實現了Dialect,PageInterceptor是實現了Interceptor接口


pageHelper是如何在mybatis中工作呢,是通過mybatis的pulgin實現了Interceptor接口,從而獲得要執行的sql語句實現分頁技術。所以最開始報錯能解釋了,可改成了PageInterceptor又報錯是怎么回事呢,這這篇博客中,才注意到了錯誤日志里的這句話
Error parsing SQL Mapper Configuration. Cause: com.github.pagehelper.PageException:
java.lang.ClassNotFoundException: mysql
給出的解釋是PageHelper插件4.0.0以后的版本支持自動識別使用的數據庫,可以不用配置 <property
name=“dialect” value=“mysql”/> 這,抱着僥幸的心理,我再一次試了,然后就成功了,真是一波三折,后來打開這個類發現了這么幾句代碼


通過讀取properties里面的配置文件來自動識別數據庫的,只需要在分頁插件配置文件中配置這個類即可,無需指定數據庫
六、感悟
主要還是要學習其工作的原理,一些源碼的簡單理解
分頁展示數據的服務端代碼

服務端
a) 創建PageBean對象
public class PageBean<T> {
private int totalCount;//總記錄數
private int totalPage;//總頁數
private int currentPage;//當前頁碼
private int pageSize;//每頁顯示的條數
private List<T> list;//每頁顯示的數據集合
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
b) RouteServlet
@WebServlet("/route/*")
public class RouteServlet extends BaseServlet {
private RouteService routeService = new RouteServiceImpl();
/** * 分頁查詢 * @param request * @param response * @throws ServletException * @throws IOException */
public void pageQuery(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.接受參數
String currentPageStr = request.getParameter("currentPage");
String pageSizeStr = request.getParameter("pageSize");
String cidStr = request.getParameter("cid");
int cid = 0;//類別id
//2.處理參數
if(cidStr != null && cidStr.length() > 0){
cid = Integer.parseInt(cidStr);
}
int currentPage = 0;//當前頁碼,如果不傳遞,則默認為第一頁
if(currentPageStr != null && currentPageStr.length() > 0){
currentPage = Integer.parseInt(currentPageStr);
}else{
currentPage = 1;
}
int pageSize = 0;//每頁顯示條數,如果不傳遞,默認每頁顯示5條記錄
if(pageSizeStr != null && pageSizeStr.length() > 0){
pageSize = Integer.parseInt(pageSizeStr);
}else{
pageSize = 5;
}
//3. 調用service查詢PageBean對象
PageBean<Route> pb = routeService.pageQuery(cid, currentPage, pageSize);
//4. 將pageBean對象序列化為json,返回
writeValue(pb,response);
}
}
c) RouteService
public class RouteServiceImpl implements RouteService {
private RouteDao routeDao = new RouteDaoImpl();
@Override
public PageBean<Route> pageQuery(int cid, int currentPage, int pageSize) {
//封裝PageBean
PageBean<Route> pb = new PageBean<Route>();
//設置當前頁碼
pb.setCurrentPage(currentPage);
//設置每頁顯示條數
pb.setPageSize(pageSize);
//設置總記錄數
int totalCount = routeDao.findTotalCount(cid);
pb.setTotalCount(totalCount);
//設置當前頁顯示的數據集合
int start = (currentPage - 1) * pageSize;//開始的記錄數
List<Route> list = routeDao.findByPage(cid,start,pageSize);
pb.setList(list);
//設置總頁數 = 總記錄數/每頁顯示條數
int totalPage = totalCount % pageSize == 0 ? totalCount / pageSize :(totalCount / pageSize) + 1 ;
pb.setTotalPage(totalPage);
return pb;
}
}
d) RouteDao
public class RouteDaoImpl implements RouteDao {
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public int findTotalCount(int cid) {
String sql = "select count(*) from tab_route where cid = ?";
return template.queryForObject(sql,Integer.class,cid);
}
@Override
public List<Route> findByPage(int cid, int start, int pageSize) {
String sql = "select * from tab_route where cid = ? limit ? , ?";
return template.query(sql,new BeanPropertyRowMapper<Route>(Route.class),cid,start,pageSize);
}
}
