Mybatis-Spring包学习

MyBatis-Spring包用来将MyBatis无缝整合到Spring中。使用这个类库中的类, Spring将会加载必要的MyBatis工厂类和Session类。 这个类库也提供一个简单的方式来注入 MyBatis 数据映射器和 SqlSession 到业务层的 bean 中。github: http://github.com/mybatis/spring

首先引入jar包

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

在 Spring 应用上下文中定义一个 SqlSessionFactory

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" />
  <property name=“configLocation” value=“classpath:mybatis-config.xml” />
</bean>

在MyBatis中session工厂可以使用SqlSessionFactoryBuilder来创建。而在MyBatis-Spring中,使用SqlSessionFactoryBean来替代,SqlSessionFactoryBean 是用于创建 SqlSessionFactory的.

SqlSessionFactoryBean 实现了 Spring 的 FactoryBean 接口(参考Spring文档的3.8章)这就说明了由 Spring 最终创建的 bean 不是 SqlSessionFactoryBean 本身, 而是工厂类的getObject()返回的方法的结果。这种情况下,Spring 将会在应用启动时创建 SqlSessionFactory对象,然后将它以 SqlSessionFactory 为名来存储。相当于Java代码:

SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
SqlSessionFactory sessionFactory = factoryBean.getObject();

在一般的 MyBatis-Spring 用法中, 你不需要直接使用 SqlSessionFactoryBean 或和其对应的 SqlSessionFactory。相反session 工厂将会被注入到 MapperFactoryBean 或其它扩展了 SqlSessionDaoSupport的DAO(Data Access Object数据访问对象)中。

SqlSessionFactory 有一个必须的属性DataSource:

    <!-- 指定数据源配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />
    
    <!-- 数据源 -->
    <bean id="dataSource_dev" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
    </bean>

configLocation属性用来指定MyBatis的XML配置文件路径。改变基本的MyBatis配置, 这个配置文件不需要是一个完整的MyBatis配置。环境,数据源和MyBatis的事务管理器都可被忽略,SqlSessionFactoryBean会自动创建。

mapperLocations属性来指定Mapper所在位置, 它的值可以加载一个目录中所有文件, 或者从基路径下递归搜索所有路径。

事务管理

MyBatis-Spring可以通过利用Spring中的DataSourceTransactionManager使MyBatis运用到Spring的事务管理中。

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

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

DataSource必须和创建SqlSessionFactoryBean的是同一个数据源,否则事务管理器无法工作。

配置好后就可以在Spring中以通常的做法来配置事务。@Transactional注解和AOP样式的配置都是支持的。

在事务处理期间,一个单独的SqlSession对象将会被创建和使用。当事务完成时,这个session会以合适的方式提交或回滚。

这样在DAO层中就不需要额外的代码了,MyBatis-Spring将会透明的管理事务。

容器管理的事务

如果你正使用一个 JEE 容器而且想让 Spring 参与到容器管理事务(Container managed transactions)中,那么 Spring 应该使用 JtaTransactionManager 或它的容器指定的子类来配置。

首先使用Spring的事务命名空间:

<tx:jta-transaction-manager />

在这种配置中,MyBatis 将会和其它由 CMT 配置的 Spring 事务资源一样。Spring 会自动使用任意存在的容器事务,在上面附加一个 SqlSession。如果没有开始事务,或者需要基于事务配置,Spring 会开启一个新的容器管理事务。

注意, 如果要使用CMT , 而不使用Spring的事务管理 , 就必须配置SqlSessionFactoryBean使用基本的MyBatis的ManagedTransactionFactory,而不是其它Spring的事务管理器:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="transactionFactory">
    <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
  </property>  
</bean>

使用 SqlSession

MyBatis中, 使用SqlSessionFactory来创建SqlSession。获得一个session之后就可以使用它来执行映射语句,提交或回滚连接,最后不再需要它的时候, 可以关闭session。

使用MyBatis-Spring之后, 不再需要直接使用SqlSessionFactory了,因为你的bean可以通过一个线程安全的SqlSession来注入;基于Spring的事务管理来自动提交,回滚,关闭session。

SqlSessionTemplate

SqlSessionTemplate 是 MyBatis-Spring 的核心。 负责管理 MyBatis 的 SqlSession, 调用 MyBatis 的 SQL 方法, 翻译异常。 SqlSessionTemplate 是线程安全的, 可以被多个 DAO 所共享使用。

当调用 SQL 方法时, 包含从映射器 getMapper()方法返回的方法, SqlSessionTemplate 将会保证使用的 SqlSession 是和当前 Spring 的事务相关的。此外,它管理 session 的生命周期,包含必要的关闭,提交或回滚操作。

SqlSessionTemplate 实现了 SqlSession 接口,这就是说,在代码中无需对 MyBatis 的 SqlSession 进行替换。 SqlSessionTemplate 通常是被用来替代默认的 MyBatis 实现的 DefaultSqlSession , 因为模板可以参与到 Spring 的事务中并且被多个注入的映射器类所使 用时也是线程安全的。相同应用程序中两个类之间的转换可能会引起数据一致性的问题。

SqlSessionTemplate 对象可以使用 SqlSessionFactory 作为构造方法的参数来创建。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

这个 bean 现在可以直接注入到 DAO bean 中。你需要在 bean 中添加一个 SqlSession 属性:

public class UserDaoImpl implements UserDao {
  private SqlSession sqlSession;
  public void setSqlSession(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }
  public User getUser(String userId) {
    return (User) sqlSession.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

如下注入 SqlSessionTemplate:

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
  <property name="sqlSession" ref="sqlSession" />
</bean>

SqlSessionTemplate 有一个使用 ExecutorType 作为参数的构造方法。这允许你用来 创建对象,比如,一个批量 SqlSession,但是使用了下列 Spring 配置的 XML 文件:

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
  <constructor-arg index="1" value="BATCH" />
</bean>

现在你所有的语句可以批量操作了,下面的语句就可以在 DAO 中使用了。

public void insertUsers(User[] users) {
   for (User user : users) {
     sqlSession.insert("org.mybatis.spring.sample.mapper.UserMapper.insertUser", user);
   }
 }

注意,如果所需的执行方法和默认的 SqlSessionFactory 设置不同,这种配置风格才 能使用。

对这种形式需要说明的是当这个方法被调用时,不能有一个存在使用不同 ExecutorType 运行的事务。也要保证在不同的事务中,使用不同执行器来调用 SqlSessionTemplate 时, (比如 PROPAGATION_REQUIRES_NEW)或完全在一个事务外面。

SqlSessionDaoSupport

SqlSessionDaoSupport 是一个抽象的支持类, 用来为你提供 SqlSession。调 用 getSqlSession()方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法:

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
  public User getUser(String userId) {
    return (User) getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

通常 MapperFactoryBean 是这个类的首选,因为它不需要额外的代码。但是,如果你 需要在 DAO 中做其它非 MyBatis 的工作或需要具体的类,那么这个类就很有用了。

SqlSessionDaoSupport 需要一个 sqlSessionFactory 或 sqlSessionTemplate 属性来 设 置 。 这 些 被 明 确 地 设 置 或 由 Spring 来 自 动 装 配 。 如 果 两 者 都 被 设 置 了 , 那 么 SqlSessionFactory 是被忽略的。

假设类 UserMapperImpl 是 SqlSessionDaoSupport 的子类,它可以在 Spring 中进行如 下的配置:

<bean id="userMapper" class="org.mybatis.spring.sample.mapper.UserDaoImpl">
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

注入映射器

为了代替手工使用 SqlSessionDaoSupport 或 SqlSessionTemplate 编写数据访问对象 (DAO)的代码,MyBatis-Spring 提供了一个动态代理的实现:MapperFactoryBean。这个类 可以让你直接注入数据映射器接口到你的 service 层 bean 中。当使用映射器时,你仅仅如调 用你的 DAO 一样调用它们就可以了,但是你不需要编写任何 DAO 实现的代码,因为 MyBatis-Spring 将会为你创建代理。

使用注入的映射器代码,在 MyBatis,Spring 或 MyBatis-Spring 上面不会有直接的依赖。 MapperFactoryBean 创建的代理控制开放和关闭 session,翻译任意的异常到 Spring 的 DataAccessException 异常中。此外,如果需要或参与到一个已经存在活动事务中,代理将 会开启一个新的 Spring 事务。

MapperFactoryBean

数据映射器接口可以按照如下做法加入到 Spring 中:

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

MapperFactoryBean 创建的代理类实现了 UserMapper 接口,并且注入到应用程序中。 因为代理创建在运行时环境中(Runtime,译者注) ,那么指定的映射器必须是一个接口,而 不是一个具体的实现类。

如果 UserMapper 有一个对应的 MyBatis 的 XML 映射器文件, 如果 XML 文件在类路径的 位置和映射器类相同时, 它会被 MapperFactoryBean 自动解析。 没有必要在 MyBatis 配置文 件 中 去 指 定 映 射 器 , 除 非 映 射 器 的 XML 文 件 在 不 同 的 类 路 径 下 。 可 以 参 考 SqlSessionFactoryBean 的 configLocation 属性(第三章)来获取更多信息。

注意,当 MapperFactoryBean 需要 SqlSessionFactory 或 SqlSessionTemplate 时。 这些可以通过各自的 SqlSessionFactory 或 SqlSessionTemplate 属性来设置, 或者可以由 Spring 来自动装配。如果两个属性都设置了,那么 SqlSessionFactory 就会被忽略,因为 SqlSessionTemplate 是需要有一个 session 工厂的设置; 那个工厂会由 MapperFactoryBean. 来使用。

你可以直接在 business/service 对象中以和注入任意 Spring bean 的相同方式直接注入映 射器:

<bean id="fooService" class="org.mybatis.spring.sample.mapper.FooServiceImpl">
  <property name="userMapper" ref="userMapper" />
</bean>

这个 bean 可以直接在应用程序逻辑中使用:

public class FooServiceImpl implements FooService {

  private UserMapper userMapper;

  public void setUserMapper(UserMapper userMapper) {
    this.userMapper = userMapper;
  }

  public User doSomeBusinessStuff(String userId) {
    return this.userMapper.getUser(userId);
  }
}

注意在这段代码中没有 SqlSession 或 MyBatis 的引用。也没有任何需要创建,打开或 关闭 session 的代码,MyBatis-Spring 会来关心它的。

 

MapperScannerConfigurer

没有必要在 Spring 的 XML 配置文件中注册所有的映射器。相反,你可以使用一个 MapperScannerConfigurer , 它 将 会 查 找 类 路 径 下 的 映 射 器 并 自 动 将 它 们 创 建 成 MapperFactoryBean。

要创建 MapperScannerConfigurer,可以在 Spring 的配置中添加如下代码:

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

basePackage 属性为映射器接口文件设置基本的包路径。可以使用分号或逗号作为分隔符设置多个包路径。每个映射器将会在指定的包路径中递归地被搜索到。

MapperScannerConfigurer属性不支持使用了PropertyPlaceholderConfigurer 的属 性替换,因为会在 Spring 其中之前来它加载。但是,你可以使用 PropertiesFactoryBean 和 SpEL 表达式来作为替代。

如果只有一个DataSource,就不用指定SqlSessionFactory或SqlSessionTemplate , 因为MapperScannerConfigurer将会创建 MapperFactoryBean,之后自动装配。

如果有多个DataSource ,那么自动装配可能会失效 。可以使用 sqlSessionFactoryBeanName 或 sqlSessionTemplateBeanName 属性来设置正确的bean名称来使用:

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />

MapperScannerConfigurer支持过滤由指定的创建接口或注解创建映射器。 annotationClass属性指定了要寻找的注解名称。 markerInterface 属性指定了要寻找的父接口。如果两者都被指定了,加入到接口中的映射器会匹配两种标准。默认情况下,这两个 属性都是 null,所以在基包中给定的所有接口可以作为映射器加载。

被发现的映射器将会使用 Spring 对自动侦测组件(参考 Spring 手册的 3.14.4)默认的命名策略来命名。也就是说,如果没有发现注解,它就会使用映射器的非大写的非完全限定类 名。但是如果发现了@Component 或 JSR-330 的@Named 注解,它会获取名称。注意你可以 配 置 到 org.springframework.stereotype.Component , javax.inject.Named(如果你使用 JSE 6 的话)或你自己的注解(肯定是自我注解)中,这 样注解将会用作生成器和名称提供器。

使用 MyBatis API

使用了MyBatis-Spring, 还可以继续使用MyBatis的API,比如Spring中的SqlSessionFactoryBean来创建一个SqlSessionFactory:

public class UserMapperSqlSessionImpl implements UserMapper {
  // SqlSessionFactory would normally be set by SqlSessionDaoSupport
  private SqlSessionFactory sqlSessionFactory;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
  }

  public User getUser(String userId) {
    // note standard MyBatis API usage - opening and closing the session manually
    SqlSession session = sqlSessionFactory.openSession();

    try {
      return (User) session.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
    } finally {
      session.close();
    }
  }
}

但是需要注意的是:

1. 这样不会参与到 Spring 的事务之中。

2. 如果 SqlSession 使用 DataSource,它也会被 Spring 事务管理器使用,而且当前有事务在进行时,这段代码会抛出异常。

3. MyBatis的DefaultSqlSession是线程不安全的。如果在 bean中注入了它,就会发生错误。

4. 使用 DefaultSqlSession创建的映射器也不是线程安全的。如果你将它们注入到 bean中就会发生错误的。

5. 必须保证在 finally 块中来关闭 SqlSession。

所以还是不要这样写。

end

原文地址:https://www.cnblogs.com/luangeng/p/6151479.html