MyBatis-Spring 实现 MyBatis 和 Spring 框架集成。
问题现象
在配置中碰到不能加载 MySQL JDBC 驱动的问题,报错如下(部分截取):
09:59:06.595 [C3P0PooledConnectionPoolManager[identityToken->z8kfltb71qnbl7e1cco0kz|23833818]-HelperThread-#2] WARN c.m.v2.c3p0.DriverManagerDataSource - Could not load driverClass ${jdbc.driverClass}java.lang.ClassNotFoundException: ${jdbc.driverClass}at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1332)at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1144)at java.base/java.lang.Class.forName0(Native Method)at java.base/java.lang.Class.forName(Class.java:534)at java.base/java.lang.Class.forName(Class.java:513)at com.mchange.v2.c3p0.DriverManagerDataSource.loadDriverClass(DriverManagerDataSource.java:129)at com.mchange.v2.c3p0.DriverManagerDataSource.ensureIfPossibleDriverClassLoaded(DriverManagerDataSource.java:108)at com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:157)at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:167)at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:153)at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:499)at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1112)at com.mchange.v2.resourcepool.BasicResourcePool.doAcquireAndDecrementPendingAcquiresWithinLockOnSuccess(BasicResourcePool.java:1099)at com.mchange.v2.resourcepool.BasicResourcePool.access$700(BasicResourcePool.java:9)at com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask.run(BasicResourcePool.java:1846)at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696)
Caused by: java.sql.SQLException: No suitable driverat java.sql/java.sql.DriverManager.getDriver(DriverManager.java:300)at com.mchange.v2.c3p0.DriverManagerDataSource.driver(DriverManagerDataSource.java:273)at com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:159)at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:167)at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:153)at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:499)at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1112)at com.mchange.v2.resourcepool.BasicResourcePool.doAcquireAndDecrementPendingAcquiresWithinLockOnSuccess(BasicResourcePool.java:1099)at com.mchange.v2.resourcepool.BasicResourcePool.access$700(BasicResourcePool.java:9)at com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask.run(BasicResourcePool.java:1846)at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696)
环境配置
SSM 框架集成的主要配置如下:
依赖:
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.17</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.4</version></dependency><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.10.1</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>9.1.0</version><scope>runtime</scope></dependency>
db.properties:
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://127.0.0.1/java
jdbc.user=root
jdbc.password=root
spring.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="cn.oraenv.javaweb.dao"/><aop:aspectj-autoproxy/><context:property-placeholder location="classpath:db.properties"/><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driverClass}"/><property name="jdbcUrl" value="${jdbc.jdbcUrl}"/><property name="user" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/></bean><bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><tx:annotation-driven/><bean name="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="configLocation" value="classpath:mybatis-config.xml"/></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="sqlSessionFactory" ref="sqlSessionFactoryBean"/><property name="basePackage" value="cn.oraenv.javaweb.dao"/></bean>
</beans>
问题解析
根据异常报错信息可以看出 JDBC 驱动的类全路经名没有替换为 db.properties 文件中的值,从而造成不能加载 MySQL 的 JDBC 驱动。
经过排查后发现是 MapperScannerConfigurer
的配置出错,使用 sqlSessionFactory
属性名以 ref 方式进行注入,会造成 org.mybatis.spring.SqlSessionFactoryBean
提前初始化,但是此时 ${jdbc.driverClass}
等参数的替换还未开始,无法读取到正确的数据库连接配置。
官方文档中对 MapperScannerConfigurer
的配置方法描述如下:
下面总结 MapperScannerConfigurer
的配置方法:
- 如果 Spring 上下文中只有一个 sqlSessionFactory,那么不用手动注入
sqlSessionFactory
属性; - 如果 Spring 上下文中有多个 sqlSessionFactory, 那么使用
sqlSessionFactoryBeanName
属性以 value 方式注入字符串的 Bean 名称。
修改后的配置段如下:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/><property name="basePackage" value="cn.oraenv.javaweb.dao"/></bean>
至此数据库连接正常。
刚开始配置的时候以为加了这项配置也不要紧,不以为然,谁成想出现这样的错误,最后通过排查和查资料,定位解决了这个问题。
问题还涉及 Bean 的生命周期和后处理的相关知识,现在水平还不到,放到以后学习。
参考链接
- https://mybatis.org/spring/mappers.html#mapperscannerconfigurer