MyBatis-Plus分页插件使用避坑:cannot be cast to com.baomidou.mybatisplus.core.metadata.IPage

2年前 (2022) 程序员胖胖胖虎阿
489 0 0

1.bug背景

1.1.Spring配置文件

<!--配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <!--连接池中保持最大的连接数-->
        <property name="maxActive" value="10"/>
        <!--连接池初始化的时候创建几个-->
        <property name="initialSize" value="5"/>
    </bean>

    <!--把MyBatis交给Sring容器管理-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--引入MyBatis的配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--注册映射文件-->
        <property name="mapperLocations">
            <array>
                <value>classpath:mappers/*.xml</value>
            </array>
        </property>
    </bean>

    <!--扫描MyBatis需要动态代理的接口所在的包-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="pers.lintao.dao"/>
    </bean>

1.2.MyBatis配置文件

<?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>
    
    <settings>
        <!--下划线转驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <plugins>
        <!--配置MyBatis-plus分页插件拦截器-->
        <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
            <property name="overflow" value="true"/>
            <property name="dbType" value="MYSQL"/>
        </plugin>
    </plugins>

</configuration>

1.3.dao层方法

/**
     * mybatis-plus分页查询
     * @param page
     * @param queryWrapper
     * @return
     */
    IPage<DepartmentListResponseDTO> queryPageList(Page<DepartmentListRequestDTO> page, @Param("ew") Wrapper<DepartmentListRequestDTO> queryWrapper);

1.4.mapper映射文件

    <select id="queryPageList" resultType="pers.lintao.dto.responseDTO.DepartmentListResponseDTO">
        SELECT dep_id,name,address,status
        FROM department ${ew.customSqlSegment}
    </select>

1.5.相关实体

DepartmentListRequestDTO和DepartmentListResponseDTO都存在

1.6.Junit测试单元

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
@WebAppConfiguration
public class DepartmentDaoTest {

    @Autowired
    private DepartmentDao departmentDao;

    @Test
    public void queryPageList() {
        QueryWrapper<DepartmentListRequestDTO> wrapper = new QueryWrapper<>();
        wrapper.like("name", "学习");
        wrapper.eq("status",1);
        Page<DepartmentListRequestDTO> page = new Page<>(1, 10);
        IPage<DepartmentListResponseDTO> iPage = departmentDao.queryPageList(page, wrapper);
        System.out.println(iPage);
    }
}

2.Bug详情

08:52:54.536 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@61ecbee9] will not be managed by Spring
08:52:54.550 [main] DEBUG com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize - JsqlParserCountOptimize sql=SELECT dep_id,name,address,status
        FROM department WHERE (name LIKE ? AND status = ?)
08:52:54.587 [main] DEBUG pers.lintao.dao.DepartmentDao.queryPageList - ==>  Preparing: SELECT COUNT(1) FROM department WHERE (name LIKE ? AND status = ?) 
08:52:54.613 [main] DEBUG pers.lintao.dao.DepartmentDao.queryPageList - ==> Parameters: %学习%(String), 1(Integer)
08:52:54.673 [main] DEBUG pers.lintao.dao.DepartmentDao.queryPageList - ==>  Preparing: SELECT dep_id,name,address,status FROM department WHERE (name LIKE ? AND status = ?) LIMIT ?,? 
08:52:54.674 [main] DEBUG pers.lintao.dao.DepartmentDao.queryPageList - ==> Parameters: %学习%(String), 1(Integer), 0(Long), 10(Long)
08:52:54.681 [main] DEBUG pers.lintao.dao.DepartmentDao.queryPageList - <==      Total: 1
08:52:54.682 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@767191b1]
08:52:54.683 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [385337537] from cache with key [[WebMergedContextConfiguration@6950e31 testClass = DepartmentDaoTest, locations = '{classpath:applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.test.context.web.WebDelegatingSmartContextLoader', parent = [null]]]
08:52:54.683 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@18da4dd size = 1, maxSize = 32, parentContextCount = 0, hitCount = 5, missCount = 1]
08:52:54.685 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [385337537] from cache with key [[WebMergedContextConfiguration@6950e31 testClass = DepartmentDaoTest, locations = '{classpath:applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.test.context.web.WebDelegatingSmartContextLoader', parent = [null]]]
08:52:54.685 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@18da4dd size = 1, maxSize = 32, parentContextCount = 0, hitCount = 6, missCount = 1]
08:52:54.685 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext@2db7a79b testClass = DepartmentDaoTest, testInstance = pers.lintao.dao.DepartmentDaoTest@4195105b, testMethod = queryPageList@DepartmentDaoTest, testException = java.lang.ClassCastException: pers.lintao.dto.responseDTO.DepartmentListResponseDTO cannot be cast to com.baomidou.mybatisplus.core.metadata.IPage, mergedContextConfiguration = [WebMergedContextConfiguration@6950e31 testClass = DepartmentDaoTest, locations = '{classpath:applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.test.context.web.WebDelegatingSmartContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]], class annotated with @DirtiesContext [false] with mode [null], method annotated with @DirtiesContext [false] with mode [null].
08:52:54.685 [main] DEBUG org.springframework.test.context.web.ServletTestExecutionListener - Resetting RequestContextHolder for test context [DefaultTestContext@2db7a79b testClass = DepartmentDaoTest, testInstance = pers.lintao.dao.DepartmentDaoTest@4195105b, testMethod = queryPageList@DepartmentDaoTest, testException = java.lang.ClassCastException: pers.lintao.dto.responseDTO.DepartmentListResponseDTO cannot be cast to com.baomidou.mybatisplus.core.metadata.IPage, mergedContextConfiguration = [WebMergedContextConfiguration@6950e31 testClass = DepartmentDaoTest, locations = '{classpath:applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.test.context.web.WebDelegatingSmartContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]].

java.lang.ClassCastException: pers.lintao.dto.responseDTO.DepartmentListResponseDTO cannot be cast to com.baomidou.mybatisplus.core.metadata.IPage

	at com.sun.proxy.$Proxy41.queryPageList(Unknown Source)
	at pers.lintao.dao.DepartmentDaoTest.queryPageList(DepartmentDaoTest.java:34)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

3.分析Bug的可能

3.1.可能一:Page对象的位置

<!--MyBatis-Plus官方文档明确说明:使用他的分页插件是Page对象在声明方法是必须位于第一个参数的位置-->
IPage<DepartmentListResponseDTO> queryPageList(Page<DepartmentListRequestDTO> page, @Param("ew") Wrapper<DepartmentListRequestDTO> queryWrapper);

IPage<DepartmentListResponseDTO> iPage = departmentDao.queryPageList(page, wrapper);

3.2.可能二:MyBatis-Plus分页插件配置有问题

MyBatis配置文件中<configuration>的子元素的配置有严格的顺序要求,结合spring配置的话,可能也需要注意他的先后顺序:The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".

4.解决Bug

可能一不成立,我的方法声明和使用Page对象都是在首位

可能二不成立,在Mybatis配置文件中子元素顺序不对会直接报错;而配合Spring配置文件配置时不需要考虑属性的先后顺序

出错位置:在Spring配置文件中配置SqlSessionFactory时,使用的是mybatis-spring包中的,应该使用MyBatis-Plus中的,因为在引入MyBatis-Plus时,它会顺带引入mybatis和mybatis-spring的依赖;为了避免jar冲突我们一般就不自己引入mybatis和mybatis-spring的依赖

<!--    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">-->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--注册映射文件-->
        <property name="mapperLocations">
            <array>
                <value>classpath:mappers/*.xml</value>
            </array>
        </property>
        <!--引入MyBatis的配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

相关文章

暂无评论

暂无评论...