标题盛不下了,把报错写到这
Bean named 'sqlSessionFactory' is expected to be of type 'org.mybatis.spring.SqlSessionFactoryBean' but was actually of type 'org.apache.ibatis.session.defaults.DefaultSqlSessionFactory'
代码背景
只是在做spring整合mybatis 的练习的时候出现的一个报错,技术版本
spiring: 5.3.1
mybatis: 3.5.3
mybatis-spring(sping整合mybtis的jar): 2.0.6
aspectjweaver: 1.9.4
junit: 4.13.1
c3p0: 0.9.5.5
核心代码如下:
applicationContext.xml
<!-- 加载配置文件 --> <context:property-placeholder location="jdbcConfig.properties"/> <!-- 配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 配置 Mybatis 的 Session 工厂 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据库连接池 --> <property name="dataSource" ref="dataSource"/> <!-- 文件映射器 --> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- 自动扫描 mapper.xml 文件 --> <property name="mapperLocations" value="classpath:com/krlin/mybatis/dao/*.xml"/> </bean>
按照spring 的理解,这样就是在 spring 的 IOC 容器中 存了两个JAVA对象,一个是存放数据库配置数据的 dataSource ,一个是 mybatis 的 核心对象 sqlSessionFactory
按照常理,放在IOC 容器中的对象可以获取并使用,于是测试类中:
@Test public void dataSourceTest() { /** 使用 ApplicationContext 接口, 获取 spring 容器 */ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); /** 根据 bean 的 id 获取对象 */ ComboPooledDataSource dataSource = ac.getBean("dataSource", ComboPooledDataSource.class); System.out.println(dataSource.getJdbcUrl()); System.out.println(dataSource.getUser()); System.out.println(dataSource.getPassword()); } @Test public void SqlSessionFactoryTest() { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); SqlSessionFactoryBean sqlSessionFactory = ac.getBean("sqlSessionFactory", SqlSessionFactoryBean.class); System.out.println(sqlSessionFactory);
代码分析
第一个 dataSource 的测试没有问题,第二个就报错了 Bean named 'sqlSessionFactory' is expected to be of type 'org.mybatis.spring.SqlSessionFactoryBean' but was actually of type 'org.apache.ibatis.session.defaults.DefaultSqlSessionFactory'
翻译一下也很简单, sqlSessionFactory 应该是 org.mybatis.spring.SqlSessionFactoryBean类型的,但是实际上却是 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory 类型的。
心想不对啊,我在配置文件中提供的类就是 SqlSessionFactoryBean 没错啊,怎么会出现一个其他的类,进入 SqlSessionfactoryBean 发现
他实现了 FactoryBean<T> 接口,原来是当实现了这个接口的 Bean 放到 IOC容器的时候,那么容器中实际存放的其实就是 这个泛型T 的类型,这也就不难理解,我们需要的 SqlSessionFactory 是 SqlSessionFactoryBean 来提供的,因为 SqlSessionFactoryBean 实现的接口的泛型就是 sqlSessionFactory, 所以看似我们 放置了 SqlSessionFactoryBean, 但实际上在 IOC 中是一个 SqlSessionFactory罢了。
查看SqlSessionFactory 发现他是一个接口
他有两个实现类,而第一个实现类,就是放入到 IOC 里边的类型,所以我们在测试的时候,就会出现类型不匹配了。
解决办法也很简单,我们在测试类中,用 DefaultSqlSessionFactory 来接收就行了
@Test public void SqlSessionFactoryTest() { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); DefaultSqlSessionFactory sqlSessionFactory = ac.getBean("sqlSessionFactory", DefaultSqlSessionFactory.class); SysUserMapper mapper = sqlSessionFactory.openSession(true).getMapper(SysUserMapper.class); List<SysUserEntity> userList = mapper.getUserList(); userList.forEach(System.out::println); }
打印数据也没有问题(虽然我们平时也不这么用,但是逻辑上是可以同的)
代码本身没问题,就是自己的理解没有到位,没有看透 SqlSessionFactoryBean 背后的本质。