mybatis-spring将mybatis无缝集成到spring框架中,使得mybatis能够参与spring的事务,将mybatis mapper和sqlsession注入到其它bean中,将mybatis的异常转换成spring的DataAccessException等。本文将在代码层面,从spring容器启动到一次数据库访问结束期间,mybatis-spring的运行流程,来解析mybatis的原理及其核心技术。
配置mybatis
使用mybatis-spring,我们有多种配置bean的方式。当然无论如何配置,数据源是必不可少的:
<bean id = “dataSource” class = “org.springframework.jdbc.datasource.DriverManagerDatasource”>
<property name = “driverClassName” value = “com.mysql.jdbc.Driver”/>
<property name = “url” value = “jdbc:mysql://localhost:3306/test”/>
<property name = “username” value = “xxx”/>
<property name = “password” value = “xxx”/>
</bean>
另外,SqlSessionFactoryBean作为mybtis-spring的核心组件,同样不可缺少:
<bean id = “sqlSessionFactory” class = “org.mybatis.spring.SqlSessionFactoryBean”>
<property name = “dataSource” ref = “dataSource”/>
<property name = “configLocation” value = “classpath:mybatis.cfg.xml”/>
</bean>
其中,configLocation属性表示mybatis配置文件的路径。mybatis配置文件可以用于指定mapper文件的路径和设置类别名。
方式一:SqlSessionTemplate会话模板
<bean id = “sqlSessionTemplate” class = “org.mybatis.spring.SqlSessionTemplate”>
<constructor-arg index = “0” ref = “sqlSessionFactory”/>
</bean>
然后我们可以在DAO bean中注入SqlSessionTemplate bean进行dao操作
@Service
public class UserDaoImpl implements UserDao {
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
return sqlSession.selectList(“com.example.mapper.selectAll”);
}
}
方式二:dao类继承SqlSessionDaoSupport
@Service
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
@Autowired
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate){
super.setSqlSessionTemplate(sqlSessionTemplate);
}
@Override
public List<User> selectUser() {
return this.getSqlSession().selectList(“com.example.mapper.selectAll”);
}
}
由于mybatis-spring在1.2版本之后,将SqlSessionDaoSupport 中setSqlSessionTemplate 和 setSqlSessionFactory方法上的@Autowired注解移除了,因此我们需要重写其中的一个方法,自行注入SqlSessionFactory或者SqlSessionTemplate bean。方式二与方式一本质上无差别。
方式三:基于mapper接口和注解的配置
以上是使用mybatis的传统方式:在mapper文件中编写sql,将sql的id作为方法参数传入SqlSessionTemplate进行dao操作,这种方式并不符合面向对象的思想,因此mybatis引入了Mapper接口,在Mapper接口方法与Mapper xml文件sql id之间建立映射,因此可以直接通过Mapper接口来进行dao操作,原理后面讲。甚至,我们可以在接口方法上使用@Select等注解来编写sql,省去Mapper xml文件。
public interface UserMapper {
@Select(“select * from USER”)
List<user> selectUser();
}
然后创建一个对应的MapperFactoryBean
<bean id = “userMapper” class = “org.mybatis.spring.mapper.MapperFactoryBean”>
<property name = “mapperInterface” value = “com.example.mapper.UserMapper”/>
<property name = “sqlSessionFactory” ref = “sqlSessionFactory”/>
</bean>
然后我们可以在任何地方注入UserMapper进行dao操作了。事实上,在底层依然是调用SqlSessionTemplate。
手动为每一个Mapper接口创建一个MapperFactoryBean显然是低效的,Mybatis-Spring提供了一个MapperScannerConfigurer,可以自动扫描Mapper接口并创建MapperFactoryBean。只需要设置其basePackage属性即可
<bean class = “org.mybatis.spring.mapper.MapperScannerConfigurer”>
<property name = “basePackage” value = “com.example.mapper”/>
</bean>
mybatis-spring还提供了另外一种注解的方式,实现跟MapperScannerConfigurer同样的效果:
@Configuration
@MapperScan(basePackages = "org.mybatis.spring.sample.mapper")
public class MybatisConfig {
}
如果mapper xml文件和mapper接口在相同的路径下,那么MapperFactoryBean会自动去寻找和绑定对应的Mapper,此时就没有必要在mybatis配置文件中指明mapper xml文件的路径了。事实上还有另外一种方式来指定mapper xml文件的路径,即在SqlSessionFactoryBean中配置
<bean id = “sqlSessionFactory” class = “org.mybatis.spring.SqlSessionFactoryBean”>
<property name = “dataSource” ref = “dataSource”/>
<property name = “mapperLocation” value = “classpath:com/example/mapper/*.xml”/>
</bean>
这样,我们可以将mybatis配置文件也去除了,除非你需要配置类别名。
更多关于mybatis-spring的使用信息,可参考官网http://www.mybatis.org/spring/getting-started.html
原理分析
1. SqlSessionFactoryBean
SqlSessionFactoryBean实现了FactoryBean<SqlSessionFactory>接口,其getObject()方法返回的是一个DefaultSqlSessionFactory实例。这意味着我们可以从Spring容器中获取两个类型的bean:SqlSessionFactoryBean和SqlSessionFactory。关于Spring的BeanFactory在获取bean时如何处理FactoryBean,可以参考https://blog.csdn.net/zknxx/article/details/79572387和https://blog.csdn.net/zknxx/article/details/79588391。使用FactoryBean的好处是,我们可以定制bean实例的创建过程;如,实例化SqlSessionFactory是较为复杂的,需要配置大量的信息,但是如果采取编码的方式,在SqlSessionFactoryBean中封装其实例化细节,问题就变得简单多了。