前言
最近在重新看 <<Java EE 联网轻量级框架整合开发>>
,也就是这本书,最最后一章有一个模拟高并发抢红包的案例,其中作者采用了纯 java bean 的配置方式,但是我按照作者的流程走下来,发现总是报如题的错误,但是代码和作者所写的又没有什么差别,很奇怪。在网上搜了很多博客都没有相关的错误,甚至于有些博客写的注解配置和该书作者的配置完全相同,且能够运行,我从网上下载下了博客作者的 demo,在本地运行,发现也是报同样的错误,令人非常沮丧,只好决定自己去探索一下。
探索过程
文件结构和配置文件
文件结构
配置文件 RootConfig
@Configuration
@ComponentScan(value="springMybatisNoXml.*", includeFilters= {@Filter(type = FilterType.ANNOTATION , value ={Repository.class})})
@EnableTransactionManagement
@MapperScan("springMybatisNoXml")
public class RootConfig {
private DataSource dataSource = null ;
/*
* 注册数据库 Bean
*/
@Bean(name = "dataSource")
public DataSource initDataSource() {
if(dataSource != null) {
return dataSource;
}
Properties props = new Properties();
props.setProperty("driverClassName", "com.mysql.jdbc.Driver");
props.setProperty("url", "jdbc:mysql://localhost/exierse?zeroDateTimeBehavior=convertToNull");
props.setProperty("username", "root");
props.setProperty("password","");
props.setProperty("maxActive","200");
props.setProperty("maxIdle", "2");
props.setProperty("maxWait", "3000");
try {
dataSource = BasicDataSourceFactory.createDataSource(props) ;
}catch (Exception e) {
// TODO: handle exception
}
return dataSource;
}
/**
* 注册 sqlSessionFactory,存在问题,无法将 Mapper 注入到 bean 中
* @return
* @throws Exception
*/
@Bean(name="sqlSessionFactory")
public SqlSessionFactory initSqlSessionFactory() throws Exception {
// 新建 SqlSessionFactoryBean
SqlSessionFactoryBean sqlSessionFactory=new SqlSessionFactoryBean();
// 设置数据源
sqlSessionFactory.setDataSource(initDataSource());
// 读取 Mybatis 的配置文件
Resource resource=new ClassPathResource("springMybatisNoXml/mybatis-config.xml");
// 引入配置
sqlSessionFactory.setConfigLocation(resource);
// 注入 sqlSessionFactory
return sqlSessionFactory.getObject();
}
/**
* mapper 扫描
* @return
*/
@Bean
public MapperScannerConfigurer initMapperScannerConfigurer() {
MapperScannerConfigurer msc=new MapperScannerConfigurer();
msc.setBasePackage("springMybatisNoXml");
msc.setSqlSessionFactoryBeanName("sqlSessionFactory");
msc.setAnnotationClass(Repository.class);
return msc;
}
}
测试文件
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = springMybatisNoXml.RootConfig.class)
public class testDao{
@Resource
private RoleMapper roleMapper;
@Test
public void testDecreaseRedPacket(){
Role role = roleMapper.getRole(1L);
System.out.println("role" + role);
}
}
mybatis + spring 运行不报错?
Mybatis 和 spring 结合之后,运行时只会报和 spring 有关的错误,比如找不到 bean,找不到 Mybatis 配置文件。
如果是找不到 Mybatis,只要检查一下文件路径是否正确,但是如果找不到 bean,就无法直接确定问题,此时我想到可以配置 log4j.properties
提高日志标准到 DEBUG 水平来查看 spring 的运行过程,配置运行之后发现 spring 完成 sqlSessionFactory
这个 bean 的构建之后就没有再去寻找并构建 RoleMapper 这些 Bean 了,说明 Mapper 接口对应的 Bean 没有注册成功。
Mapper Bean 没有注册成功
首先考虑可能是 Mapper 接口上没有用 @Repository 来修饰,检查后发现已经标注好了
@Repository
public interface RoleMapper {
public int insertRole(Role role);
public Role getRole(@Param("id") Long id);
public int updateRole(Role role);
public int deleteRole(@Param("id") Long id);
}
这就意味着配置中的 Mapper 自动扫描有问题,也就是下面
/**
* mapper 扫描
* @return
*/
@Bean
public MapperScannerConfigurer initMapperScannerConfigurer() {
MapperScannerConfigurer msc=new MapperScannerConfigurer();
msc.setBasePackage("springMybatisNoXml");
msc.setSqlSessionFactoryBeanName("sqlSessionFactory");
msc.setAnnotationClass(Repository.class);
return msc;
}
在 RootConfig
类中使用最原始的注册方式注册 Mapper,如下所示,发现 Mapper 好用,那就是自动扫描不行。
@Bean
public RoleMapper userMapper() throws Exception {
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(initSqlSessionFactory());
return sqlSessionTemplate.getMapper(RoleMapper.class);
}
为了探寻原因,直接找到了 spring-Mybatis 的官方文档,发现自动扫描应该这样写
@Configuration
@MapperScan("org.mybatis.spring.sample.mapper") // mapper 接口的路径
public class AppConfig {
// ...
}
之后就运行正常了
最后
找到了在纯 java Bean 下实现自动扫描 Mapper 接口的方法之后,我对作者那种写法为什么不正确也就没啥兴趣了。要想知道报错原因,我认为这可能得去了解一下 spring 和 Mybatis 的版本适配问题,但是这个就非常鸡肋了。