Spring(四): Spring整合MyBatis



整合的方法

MyBatis 是以SqlSessionFactory为核心的,Spring是以BeanFactoryApplicationContext为核心的。把两者整合在一起,Mybatis-Spring封装了一个SqlSessionFactoryBean,在这个Bean里可以产生SqlSessionFactory。所以通过Spring的IoC实现SqlSessionFactoryBean的注入即可将二者整合。

使用Mybatis-Spring模块需要mybatis-spring-x.x.x.jar包,如果使用Maven,则需在pom.xml文件中添加下面代码:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>x.x.x</version>
</dependency>

SqlSessionFactoryBean的注入

MyBatis中,session工厂可以使用SqlSessionFactoryBuilder来创建。而在MyBatis-Spring中,则使用SqlSessionFactoryBean来替代。 要想实现对SqlSessionFactoryBean的注入,需要在spring的配置文件中添加这样的一个bean元素:

    <!-- 读取properties文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
       <property name="driverClassName" value="${jdbc.driver}" />
       <property name="url" value="${jdbc.url}" />
       <property name="username" value="${jdbc.username}" />
       <property name="password" value="${jdbc.password}" />
    </bean>
    <!-- SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- dataSource用于指定mybatis的数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- mapperLocations用于指定mybatis中mapper文件所在的位置 -->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
        <!-- 自动重命名 -->
        <property name="typeAliasesPackage" value="com.test.mybatis_spring.model" />
        <!-- 用于指定mybatis配置文件的位置 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

构建SqlSessionFactoryBean的时候,dataSource属性是必须指定的,它表示用于连接数据库的数据源。我们也可以指定一些其他的属性,如:

  • typeAliasesPackage:实体类所在的包,自动取包中不包括包名的简单类名作为别名。 多个package之间可以用逗号或者分号等来进行分隔。
  • typeAliases:数组类型,用来指定别名的。指定了这个属性后,Mybatis会把这个类型的短名称作为这个类型的别名, 前提是该类上没有@Alias注解,否则将使用该注解对应的值作为此种类型的别名。如:
 <property name="typeAliases">  
    <array>  
        <value>com.test.mybatis.model.Blog</value>  
        <value>com.test.mybatis.model.Comment</value>  
    </array>  
</property>
  • plugins:数组类型,用来指定Mybatis的Interceptor
  • typeHandlersPackage:用来指定TypeHandler所在的包,自动把该包下面的类注册为对应的TypeHandler。多个package之间可以用逗号或者分号等来进行分隔。
  • typeHandlers:数组类型,用来指定TypeHandler

MapperFactoryBean

通过SqlSessionFactoryBean可以产生SqlSessionFactory,在MyBatis中,通过sqlSessionFactory.openSession()得到sqlSession,然后通过session.getMapper(xxx.class)得到对应的Mapper。在Spring中,通过MapperFactoryBean可以获取到我们想要的Mapper对象。

MapperFactoryBean实现了Spring的FactoryBean接口,所以MapperFactoryBean是通过FactoryBean接口中定义的getObject方法来获取对应的Mapper对象的。在定义一个MapperFactoryBean的时候有两个属性需要我们注入,一个是Mybatis-Spring用来生成实现了SqlSession接口的SqlSessionTemplate对象的sqlSessionFactory;另一个是我们所要返回的对应的Mapper接口。

比如,有一个UserMapper接口:

//UserMapper.java
public interface UserMapper {
  @Select("SELECT * FROM users WHERE id = #{userId}")
  User getUser(@Param("userId") String userId);
}

使用MapperFactoryBean把接口加入到 Spring 中:

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <!-- 指定的映射器类必须是一个接口,而不是具体的实现类 -->
  <property name="mapperInterface" value="com.test.mybatis_spring.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

定义好Mapper接口对应的MapperFactoryBean之后,就可以把对应的Mapper接口注入到由Spring管理的bean对象中了。当我们需要使用到相应的Mapper接口时,MapperFactoryBean会从它的getObject方法中获取对应的Mapper接口,而getObject内部还是通过我们注入的属性调用SqlSession接口的getMapper()方法来返回对应的Mapper接口。这样就通过把SqlSessionFactory和相应的Mapper接口交给Spring管理实现了Mybatis跟Spring的整合。


使用MapperScannerConfigurer自动注册Mapper

像上面一个Mapper就需要定义一个对应的MapperFactoryBeanMybatis-Spring提供了一个叫MapperScannerConfigurer的类,可以自动注册Mapper对应的MapperFactoryBean对象。我们只需要在配置文件中添加这样的内容:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="com.test.mybatis_spring.mapper" />
</bean>

MapperScannerConfigurer有一个basePackage属性必须指定。basePackage用来指定Mapper接口文件所在的包,在这个包或其子包下面的Mapper接口都将被搜索到。多个包之间可以使用逗号或者分号进行分隔。另外还有两个可以缩小搜索和注册范围的属性,一个是annotationClass,另一个是markerInterface

  • annotationClass:当指定了annotationClass时,MapperScannerConfigurer将只注册使用了annotationClass注解标记的接口。
  • markerInterfacemarkerInterface用于指定一个接口,当指定了markerInterface之后,MapperScannerConfigurer将只注册继承自markerInterface的接口。

如果上述两个属性都指定了的话,那么MapperScannerConfigurer将取它们的并集,而不是交集。


事务管理

MyBatis-Spring利用了存在于Spring中的DataSourceTransactionManager进行事务管理。一旦SpringPlatformTransactionManager配置好了,就可以在Spring中以你通常的做法(@Transactional注解)来配置事务。在事务处理期间,会创建一个单独的SqlSession对象,当事务完成时,这个session会以合适的方式提交或回滚。

要开启Spring的事务处理,需要在Spring的配置文件中创建一个DataSourceTransactionManager对象:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>

下面这段代码展示如何编程式地控制事务:

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = txManager.getTransaction(def);
try {
    userMapper.insertUser(user);
}
catch (MyException ex) {
    txManager.rollback(status);
    throw ex;
}
txManager.commit(status);