<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


        Mybatis是java開(kāi)發(fā)者非常熟悉的ORM框架,Spring集成Mybatis更是我們的日常開(kāi)發(fā)姿勢(shì)。

        本篇主要講Mybatis與Spring集成所做的事情,讓讀過(guò)本文的開(kāi)發(fā)者對(duì)Mybatis和Spring的集成過(guò)程,有清晰的理解。

        注:若文中有錯(cuò)誤或其他疑問(wèn),歡迎留下評(píng)論。

        以mybatis-spring-2.0.2
      <https://github.com/mybatis/spring/tree/mybatis-spring-2.0.2>為例,工程劃分六個(gè)模塊。

      1、annotation 模塊

        

      ?

      ?


        定義了@MapperScan和@MapperScans,用于掃描mapper接口。以及mapper掃描注冊(cè)器(MapperScannerRegistrar),掃描注冊(cè)器實(shí)現(xiàn)了
      ImportBeanDefinitionRegistrar接口, 在Spring容器啟動(dòng)時(shí)會(huì)運(yùn)行所有實(shí)現(xiàn)了這個(gè)接口的實(shí)現(xiàn)類(lèi),
      注冊(cè)器內(nèi)部會(huì)注冊(cè)一系列MyBatis相關(guān)Bean。

      2、batch 模塊



        

      ?

      ?

      ?

      ?

      ?

        批處理相關(guān),基于優(yōu)秀的批處理框架Spring batch 封裝了三個(gè)批處理相關(guān)類(lèi):

      * MyBatisBatchItemWriter(批量寫(xiě))
      * MyBatisCursorItemReader(游標(biāo)讀)
      * MyBatisPagingItemReader(分頁(yè)讀)
        在使用Mybatis時(shí),方便的應(yīng)用Spring? batch,詳見(jiàn)?Spring-batch使用
      <http://www.mybatis.org/spring/zh/batch.html>。

      3、config模塊

        

      ?

        解析、處理讀取到的配置信息。

      4、mapper模塊

        

      ?

      ?

        這里是處理mapper的地方:
      ClassPathMapperScanner(根據(jù)路徑掃描Mapper接口)與MapperScannerConfigurer
      配合,完成批量掃描mapper接口并注冊(cè)為MapperFactoryBean。

      5、support 模塊

        

      ?

        支持包,SqlSessionDaoSupport
      ?是一個(gè)抽象的支持類(lèi),用來(lái)為你提供?SqlSession調(diào)用getSqlSession()方法會(huì)得到一個(gè)SqlSessionTemplate。

      6、transaction 模塊,以及凌亂類(lèi)

        

      ?

      ?

      ?

      ?

      ?

        與Spring集成后,事務(wù)管理交由Spring來(lái)做。

        還有包括異常轉(zhuǎn)換,以及非常重要的SqlSessionFactoryBean,在外散落著。

      下面重點(diǎn)講述幾個(gè)核心部分:?

        一、初始化相關(guān)

        1)SqlSessionFactoryBean

        在基礎(chǔ)的MyBatis中,通過(guò)SqlSessionFactoryBuilder創(chuàng)建SqlSessionFactory。
      集成Spring后由SqlSessionFactoryBean來(lái)創(chuàng)建?!  ?br>public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>...

        需要注意SqlSessionFactoryBean實(shí)現(xiàn)了Spring的FactoryBean接口。這意味著由Spring最終創(chuàng)建不是
      SqlSessionFactoryBean本身,而是 getObject()的結(jié)果。我們來(lái)看下getObject()
      @Override public SqlSessionFactory getObject() throws Exception { if (this
      .sqlSessionFactory ==null) { //配置加載完畢后,創(chuàng)建SqlSessionFactory
      afterPropertiesSet(); }return this.sqlSessionFactory; }
        getObject()最終返回了當(dāng)前類(lèi)的 SqlSessionFactory,因此,Spring 會(huì)在應(yīng)用啟動(dòng)時(shí)創(chuàng)建?SqlSessionFactory
      ,并以?sqlSessionFactory名稱放進(jìn)容器。

        2)??兩個(gè)重要屬性:

          1.?SqlSessionFactory?有一個(gè)唯一的必要屬性:用于 JDBC 的?
      DataSource不能為空,這點(diǎn)在afterPropertisSet()中體現(xiàn)。

          2.?configLocation,它用來(lái)指定 MyBatis 的 XML 配置文件路徑。通常只用來(lái)配置?
      <settings>相關(guān)。其他均使用Spring方式配置
      5 public void afterPropertiesSet() throws Exception { 6 //dataSource不能為空 7
      notNull(dataSource, "Property 'dataSource' is required"); 8 //有默認(rèn)值,初始化 = new
      SqlSessionFactoryBuilder() 9 notNull(sqlSessionFactoryBuilder, "Property
      'sqlSessionFactoryBuilder' is required"); 10 //判斷configuration &&
      configLocation有且僅有一個(gè) 11 state((configuration == null && configLocation == null)
      ||
                !(configuration != null && configLocation != null), 12 "Property
      'configuration' and 'configLocation' can not specified with together"); 13 //
      調(diào)用build方法創(chuàng)建sqlSessionFactory 14 this.sqlSessionFactory =
      buildSqlSessionFactory();15 }
          
      buildSqlSessionFactory()方法比較長(zhǎng)所以,這里省略了一部分代碼,只展示主要過(guò)程,看得出在這里進(jìn)行了Mybatis相關(guān)配置的解析,完成了Mybatis核心配置類(lèi)Configuration的創(chuàng)建和填充,最終返回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    // 如果配置了原生配置文件路徑,則根據(jù)路徑創(chuàng)建Configuration對(duì)象 15 } else if (this.configLocation
      !=null) { 16 xmlConfigBuilder = new XMLConfigBuilder(this
      .configLocation.getInputStream()
              , null, this.configurationProperties); 17 targetConfiguration =
      xmlConfigBuilder.getConfiguration();18 } else {21    // 兜底,使用默認(rèn)的 22
      targetConfiguration =new Configuration(); 23    //
      如果configurationProperties存在,設(shè)置屬性 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 //如果需要解決原生配置文件,此時(shí)開(kāi)始解析(即配置了configLocation) 41 if (xmlConfigBuilder !=
      null) { 42 try { 43 xmlConfigBuilder.parse(); 44    ... //
      有可能配置多個(gè),所以遍歷處理(2.0.0支持可重復(fù)注解) 52 if (this.mapperLocations != null) { 53 if (this
      .mapperLocations.length == 0) {
            for (Resource mapperLocation : this.mapperLocations) { 57 ... //
      根據(jù)mapper路徑,加載所以mapper接口 62 XMLMapperBuilder xmlMapperBuilder = new
      XMLMapperBuilder(mapperLocation.getInputStream(),63 targetConfiguration,
      mapperLocation.toString(), targetConfiguration.getSqlFragments());64
      xmlMapperBuilder.parse();65  //構(gòu)造SqlSessionFactory 70 return this
      .sqlSessionFactoryBuilder.build(targetConfiguration);71 }
        二、事務(wù)管理


        1)事務(wù)管理器配置

          MyBatis-Spring 允許 MyBatis 參與到 Spring 的事務(wù)管理中。 借助 Spring 的
      DataSourceTransactionManager 實(shí)現(xiàn)事務(wù)管理?! ?br>/** 一、XML方式配置 **/ <bean id="transactionManager" class
      ="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <
      constructor-argref="dataSource" /> </bean> /** 一、注解方式配置 **/ @Bean public
      DataSourceTransactionManager transactionManager() { return new
      DataSourceTransactionManager(dataSource()); }
      注意:為事務(wù)管理器指定的?DataSource?必須和用來(lái)創(chuàng)建?SqlSessionFactoryBean?的是同一個(gè)數(shù)據(jù)源,否則事務(wù)管理器就無(wú)法工作了。

        配置好 Spring 的事務(wù)管理器,你就可以在 Spring 中按你平時(shí)的方式來(lái)配置事務(wù)。并且支持 @Transactional 注解(聲明式事務(wù))和
      AOP 風(fēng)格的配置。在事務(wù)處理期間,一個(gè)單獨(dú)的?SqlSession?對(duì)象將會(huì)被創(chuàng)建和使用。當(dāng)事務(wù)完成時(shí),這個(gè) session
      會(huì)以合適的方式提交或回滾。無(wú)需DAO類(lèi)中無(wú)需任何額外操作,MyBatis-Spring 將透明地管理事務(wù)。

        2)?編程式事務(wù):

        推薦TransactionTemplate?方式,簡(jiǎn)潔,優(yōu)雅??墒÷詫?duì)?commit?和?rollback?方法的調(diào)用?!   ?br>1 TransactionTemplate transactionTemplate = new
      TransactionTemplate(transactionManager);2 transactionTemplate.execute(txStatus
      -> { 3 userMapper.insertUser(user); 4 return null; 5 });
      注意:這段代碼使用了一個(gè)映射器,換成SqlSession同理。
        三、SqlSession

        在MyBatis 中,使用?SqlSessionFactory?來(lái)創(chuàng)建?SqlSession
      。通過(guò)它執(zhí)行映射的sql語(yǔ)句,提交或回滾連接,當(dāng)不再需要它的時(shí)候,可以關(guān)閉 session。使用 MyBatis-Spring 之后,我們不再需要直接使用?
      SqlSessionFactory?了,因?yàn)槲覀兊腷ean 可以被注入一個(gè)線程安全的?SqlSession,它能基于 Spring
      的事務(wù)配置來(lái)自動(dòng)提交、回滾、關(guān)閉 session。

        SqlSessionTemplate? 

        SqlSessionTemplate 是SqlSession的實(shí)現(xiàn),是線程安全的,因此可以被多個(gè)DAO或映射器共享使用。也是
      MyBatis-Spring 的核心。

        四、映射器

        1) 映射器的注冊(cè)  
      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
      支持可重復(fù)注解)12 * 指定多個(gè)路徑可選用此種方式 13 */ 14 @Configuration 15
      @MapperScans({@MapperScan("com.zto.test1"), @MapperScan("com.zto.test2.mapper"
      )})16 public class AppConfig { 18 } <!-- MapperScannerConfigurer方式,批量掃描注冊(cè) --> <
      beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name
      ="basePackage" value="com.zto.test.*" /> <property name
      ="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean>
        無(wú)論使用以上哪種方式注冊(cè)映射器,最終mapper接口都將被注冊(cè)為
      MapperFactoryBean。既然是FactoryBean,我們來(lái)跟它的getObject()方法看下。

        2) MapperFactoryBean源碼解析

          1.查找MapperFactoryBean.getObject()  
      1 /** 2 * 通過(guò)接口類(lèi)型,獲取mapper 3 * {@inheritDoc} 4 */ 5 @Override 6 public T
      getObject()throws Exception { 7 //getMapper 是一個(gè)抽象方法 8 return
      getSqlSession().getMapper(this.mapperInterface); 9 }
          2.查看實(shí)現(xiàn)類(lèi),SqlSessionTemplate.getMapper()

          (
      為什么是SqlSessionTemplate,而不是默認(rèn)的DefaultSqlSession?SqlSessionTemplate是整合包的核心,是線程安全的SqlSession實(shí)現(xiàn),是我們@Autowired
      mapper接口編程的基礎(chǔ) )
      4 @Override 5 public <T> T getMapper(Class<T> type) { 6 return
      getConfiguration().getMapper(type,this); 7 }
          3.調(diào)用Configuration.getMapper()  
      1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 2 return
      mapperRegistry.getMapper(type, sqlSession);3 }
          4.調(diào)用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.調(diào)用MapperProxyFactory.newInstance()  
      1 @SuppressWarnings("unchecked") 2 protected T newInstance(MapperProxy<T>
      mapperProxy) {3 return (T)
      Proxy.newProxyInstance(mapperInterface.getClassLoader(),new Class[] {
      mapperInterface }, mapperProxy);4 }
          最終看到動(dòng)態(tài)代理生成了一個(gè)新的代理實(shí)例返回了,也就是說(shuō),我們使用@Autowired
      注解進(jìn)來(lái)一個(gè)mapper接口,每次使用時(shí)都會(huì)由代理生成一個(gè)新的實(shí)例。

          為什么在Mybatis中SqlSession是方法級(jí)的,Mapper是方法級(jí)的,在集成Spring后卻可以注入到類(lèi)中使用?

          因?yàn)樵贛ybatis-Spring中所有mapper被注冊(cè)為FactoryBean,每次調(diào)用都會(huì)執(zhí)行g(shù)etObject(),返回新實(shí)例。

        五、總結(jié)

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

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

          2)mapper對(duì)象是方法級(jí)別的,Spring通過(guò)FactoryBean巧妙地解決了這個(gè)問(wèn)題

          3)事務(wù)交由Spring管理

          注:如對(duì)文中內(nèi)容有疑問(wèn),歡迎留下評(píng)論共同探討。

      ?

      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問(wèn)題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          av性爱在线观看 国内精品久久久久久久久久 | 国产精品揄拍100视频 | 都市激情综合 | 色综合88| 亚洲精品一区二区三 | ass日本另类肉体图pics | 成人做爱视频在线观看免费版网站 | 男人添女人高潮免费观看视频 | 美女搞黄免费 | 久久免费视屏 |