Mybatis when integrated with Spring have done what?

  Mybatis java developer is very familiar with ORM framework, Spring integration Mybatis is our daily development posture.

  Data in this chapter speak Mybatis and Spring integration do things, allow developers to read the paper on the integration process Mybatis and Spring, have a clear understanding.

  To mybatis-spring-2.0.2 , for example, engineering division of six modules.

A, annotation module

  And it defines @MapperScan @MapperScans, scanned for the annotation mapper interface. And a mapper scan registrar (MapperScannerRegistrar), scanning Register in the ImportBeanDefinitionRegistrar interface will run all the implementation class implements this interface when you start in the Spring container, the internal registrar registers a series of MyBatis related Bean.

Two, batch module

  Relevant batch, batch encapsulate three classes based on relevant outstanding batch framework Spring batch:

    MyBatisBatchItemWriter (batch write)

    MyBatisCursorItemReader (vernier reading)

    MyBatisPagingItemReader (page read)

  In use Mybatis, convenient application Spring batch, see  Spring-batch used .

 

Three, config module

  Parsing, processing, the read configuration information.

 

Four, mapper module

  This is where the process of the mapper:

    ClassPathMapperScanner (depending on the configuration path of the scanning Mapper load all interfaces)

    MapperScannerConfigurer batch scanning mapper interface registration is MapperFactoryBean, behind focuses.

Five, support module

  Support package, SqlSessionDaoSupport  is an abstract class support, to provide you with SqlSession call getSqlSession () method will get a SqlSessionTemplate.

Six, transaction module

  Integration with Spring, Spring handed over to do transaction management.

  There abnormalities including conversion, as well as a very important SqlSessionFactoryBean, scattered out.

 

 

 

  

Here we focus on several core components: 

  First, initialize the correlation

  1)SqlSessionFactoryBean

  On the basis of MyBatis by SqlSessionFactoryBuilder create SqlSessionFactory. After the integration of Spring created by the SqlSessionFactoryBean.   

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>...

  Note SqlSessionFactoryBean implements the Spring FactoryBean interface. This means that ultimately created not by the Spring the SqlSessionFactoryBean itself, but getObject () results. We look at the getObject ()

  @Override
   public a SqlSessionFactory the getObject () throws Exception {
     IF ( the this .sqlSessionFactory == null ) {
       // After the configuration is loaded, create a SqlSessionFactory 
      afterPropertiesSet (); 
    } 
    return  the this .sqlSessionFactory; 
  }

  getObject () finally returns the current class SqlSessionFactory, therefore, Spring will be created when the application starts  SqlSessionFactory , and to  sqlSessionFactory name into the container.

  2) two important attributes:

    1. SqlSessionFactory 有一个唯一的必要属性:用于 JDBC 的 DataSource不能为空,这点在afterPropertisSet()中体现。

    2. configLocation,它用来指定 MyBatis 的 XML 配置文件路径。通常只用来配置 <settings>相关。其他均使用Spring方式配置

 5   public void afterPropertiesSet() throws Exception {
 6     //dataSource不能为空
 7     notNull(dataSource, "Property 'dataSource' is required");
 8     //有默认值,初始化 = new SqlSessionFactoryBuilder()
 9     notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
10     //判断configuration && configLocation有且仅有一个
11     state((configuration == null && configLocation == null) || 
          !(configuration != null && configLocation != null), 12 "Property 'configuration' and 'configLocation' can not specified with together"); 13 //调用build方法创建sqlSessionFactory 14 this.sqlSessionFactory = buildSqlSessionFactory(); 15 }

     buildSqlSessionFactory()方法比较长所以,这里省略了一部分代码,只展示主要过程,看得出在这里进行了Mybatis相关配置的解析,完成了Mybatis核心配置类Configuration的创建和填充,最终返回SqlSessionFactory。

 1 protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
 2 
 3     final Configuration targetConfiguration;
 4 
 5     XMLConfigBuilder xmlConfigBuilder = null;
 6    // 如果自定义了 Configuration,就用自定义的
 7     if (this.configuration != null) {
 8       targetConfiguration = this.configuration;
 9       if (targetConfiguration.getVariables() == null) {
10         targetConfiguration.setVariables(this.configurationProperties);
11       } else if (this.configurationProperties != null) {
12         targetConfiguration.getVariables().putAll(this.configurationProperties);
13       }
14     // 如果配置了原生配置文件路径,则根据路径创建Configuration对象
15     } else if (this.configLocation != null) {
16       xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream()
        , null, this.configurationProperties); 17 targetConfiguration = xmlConfigBuilder.getConfiguration(); 18 } else {21    // 兜底,使用默认的 22 targetConfiguration = new Configuration(); 23    //如果configurationProperties存在,设置属性 24    Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables); } 26 //解析别名,指定包    27 if (hasLength(this.typeAliasesPackage)) { 28 scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream() 29 .filter(clazz -> !clazz.isAnonymousClass())
      .filter(clazz -> !clazz.isInterface()) 30 .filter(clazz -> !clazz.isMemberClass())
      .forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
31 } 32 //解析插件 33 if (!isEmpty(this.plugins)) { 34 Stream.of(this.plugins).forEach(plugin -> { 35 targetConfiguration.addInterceptor(plugin);38 } 39 ... 40 //如果需要解决原生配置文件,此时开始解析(即配置了configLocation) 41 if (xmlConfigBuilder != null) { 42 try { 43 xmlConfigBuilder.parse(); 44    ... //有可能配置多个,所以遍历处理(2.0.0支持可重复注解) 52 if (this.mapperLocations != null) { 53 if (this.mapperLocations.length == 0) {
      for (Resource mapperLocation : this.mapperLocations) { 57 ... //根据mapper路径,加载所以mapper接口 62 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), 63 targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments()); 64 xmlMapperBuilder.parse(); 65  //构造SqlSessionFactory 70 return this.sqlSessionFactoryBuilder.build(targetConfiguration); 71 }

  二、事务管理

  1)事务管理器配置

    MyBatis-Spring 允许 MyBatis 参与到 Spring 的事务管理中。 借助 Spring 的 DataSourceTransactionManager 实现事务管理。  

/** 一、XML方式配置 **/
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="dataSource" />
</bean>
/** 一、注解方式配置 **/
@Bean
public DataSourceTransactionManager transactionManager() {
  return new DataSourceTransactionManager(dataSource());
}
注意:为事务管理器指定的 DataSource 必须和用来创建 SqlSessionFactoryBean 的是同一个数据源,否则事务管理器就无法工作了。

  配置好 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解(声明式事务)和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。无需DAO类中无需任何额外操作,MyBatis-Spring 将透明地管理事务。

  2) 编程式事务:

  推荐TransactionTemplate 方式,简洁,优雅。可省略对 commit 和 rollback 方法的调用。    

1 TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
2 transactionTemplate.execute(txStatus -> {
3   userMapper.insertUser(user);
4   return null;
5 });
注意:这段代码使用了一个映射器,换成SqlSession同理。

  三、SqlSession

    在MyBatis 中,使用 SqlSessionFactory 来创建 SqlSession。通过它执行映射的sql语句,提交或回滚连接,当不再需要它的时候,可以关闭 session。使用 MyBatis-Spring 之后,我们不再需要直接使用 SqlSessionFactory 了,因为我们的bean 可以被注入一个线程安全的 SqlSession,它能基于 Spring 的事务配置来自动提交、回滚、关闭 session。

  SqlSessionTemplate  

    SqlSessionTemplate 是SqlSession的实现,是线程安全的,因此可以被多个DAO或映射器共享使用。也是 MyBatis-Spring 的核心。

  四、映射器

  1) 映射器的注册  

 1 /** 
 2  *@MapperScan注解方式 
3 */ 4 @Configuration 5 @MapperScan("org.mybatis.spring.sample.mapper") 6 public class AppConfig { 8 } 10 /** 11 *@MapperScanS注解 (since 2.0.0新增,java8 支持可重复注解) 12 * 指定多个路径可选用次种方式 13 */ 14 @Configuration 15 @MapperScans({@MapperScan("com.zto.test1"), @MapperScan("com.zto.test2.mapper")}) 16 public class AppConfig { 18 }
<!-- MapperScannerConfigurer方式,批量扫描注册 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="com.zto.test.*" />
  <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>

  无论使用以上哪种方式注册映射器,最终mapper接口都将被注册为MapperFactoryBean。既然是FactoryBean,我们来跟它的getObject()方法看下。

  2) MapperFactoryBean源码解析

    1.查找MapperFactoryBean.getObject()  

1 /**
2    * 通过接口类型,获取mapper
3    * {@inheritDoc}
4    */
5   @Override
6   public T getObject() throws Exception {
7     //getMapper 是一个抽象方法
8     return getSqlSession().getMapper(this.mapperInterface);
9   }

    2.查看实现类,SqlSessionTemplate.getMapper()

    ( 为什么是SqlSessionTemplate,而不是默认的DefaultSqlSession?SqlSessionTemplate是整合包的核心,是线程安全的SqlSession实现,是我们@Autowired mapper接口编程的基础 )

4   @Override
5   public <T> T getMapper(Class<T> type) {
6     return getConfiguration().getMapper(type, this);
7   }

    3.调用Configuration.getMapper()  

1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
2     return mapperRegistry.getMapper(type, sqlSession);
3 }

    4.调用MapperRegistry.getMapper()   

 1 @SuppressWarnings("unchecked")
 2   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 3     final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
 4     if (mapperProxyFactory == null) {
 5       throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 6     }
 7     try {
 8       return mapperProxyFactory.newInstance(sqlSession);
 9     } catch (Exception e) {
10       throw new BindingException("Error getting mapper instance. Cause: " + e, e);
11     }
12 }

    5.调用MapperProxyFactory.newInstance()  

1 @SuppressWarnings("unchecked")
2   protected T newInstance(MapperProxy<T> mapperProxy) {
3     return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
4  }

    最终看到动态代理生成了一个新的代理实例返回了,也就是说,我们使用@Autowired 注解进来一个mapper接口,每次使用时都会由代理生成一个新的实例。

    为什么在Mybatis中SqlSession是方法级的,Mapper是方法级的,在集成Spring后却可以注入到类中使用?

    因为在Mybatis-Spring中所有mapper被注册为FactoryBean,每次调用都会执行getObject(),返回新实例。

  五、总结

    MyBatis集成Spring后,Spring侵入了Mybatis的初始化和mapper绑定,具体就是:

    1)Cofiguration的实例化是读取Spring的配置文件(注解、配置文件),而不是mybatis-config.xml

    2)mapper对象是方法级别的,Spring通过FactoryBean巧妙地解决了这个问题

    3)事务交由Spring管理

    注:如文中有错误或对文中内容有其他疑问,欢迎留下评论。

Guess you like

Origin www.cnblogs.com/xwy6/p/11279518.html