zoukankan      html  css  js  c++  java
  • Mybatis

    核心对象

    1. SqlSessionFactoryBean: 实现InitializingBean接口,在初始化过程中读取配置文件mybatis-config.xml并创建SqlSessionFactory
    2. MapperScannerConfigurer: 实现BeanDefinitionRegistryPostProcessor接口,实现自定义的Bean对象注册 - MapperFactoryBean<*Mapper>对象
    3. SqlSessionTemplate: 持有SqlSessionProxy对象,对SqlSessionProxy对象的操作都会经过SqlSessionInterceptor.invoke()方法,在invoke()内部获取sqlSession进行SQL语句执行
    4. MapperFactoryBean对象: getObject()方法返回Mapper的代理对象,底层实现依赖MapperProxyFactory对象:MapperProxyFactory.newInstance(SqlSessionTemplate);
      mybatis-spring整合

    Spring配置文件

    <beans>
        <!-- Spring包扫描路径 -->
        <context:component-scan base-package="top.kiqi.spring"/>
    
        <bean id="jdbc" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations" value="classpath*:db.properties"/>
        </bean>
    
        <!-- c3p0连接池 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${dataSource.driver}" />
            <property name="jdbcUrl" value="${dataSource.url}" />
            <property name="user" value="${dataSource.username}" />
            <property name="password" value="${dataSource.password}" />
        </bean>
    
        <!-- 在Spring启动时创建 sqlSessionFactory -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="configLocation" value="classpath:spring-mybatis-config.xml"></property>
            <property name="mapperLocations" value="classpath:mapping/*iMapper.xml"></property>
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!-- 配置扫描器,将mybatis的接口实现加入到 IOC容器中  -->
        <mybatis-spring:scan base-package="top.kiqi.mybatis.dao" />
    
        <!--开启事务控制-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    </beans>
    

    SqlSessionFactoryBean

    // 注册到IOC容器中,用于获取SqlSessionFactory对象
    public class SqlSessionFactoryBean
        implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
      // (init)生命周期切入点
      @Override
      public void afterPropertiesSet() throws Exception {
        notNull(dataSource, "Property 'dataSource' is required");
        notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    
        state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
            "Property 'configuration' and 'configLocation' can not specified with together");
    
        // MS1 : 通过SqlSessionFactoryBean对象的生命周期切入点,实现配置读取和SqlSessionFactory对象创建
        this.sqlSessionFactory = buildSqlSessionFactory();
      }
    
      // MS : 实现配置文件解析,并返回sqlSessionFactory对象。
      protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
         Configuration targetConfiguration  = xmlConfigBuilder.getConfiguration();
         xmlConfigBuilder.parse();
         xmlMapperBuilder.parse();
         return this.sqlSessionFactoryBuilder.build(targetConfiguration);
      }
    
      // IOC容器调用FactoryBean<T>对象的getObject()方法获取真实bean
      public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
          afterPropertiesSet();
        }
    
        return this.sqlSessionFactory;
      }
    

    MapperScannerConfigurer

    // 用于扫描mybatis mapper接口,并注册到IOC容器中,其BeanDefinition中的class为MapperFactoryBeanClass,通过getObject()获取Mapper代理对象
    public class MapperScannerConfigurer
        implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
     
      // 通过该后置处理器,可以实现对未实例化的BeanDefinition进行修改
      @Override
      public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
          processPropertyPlaceHolders();
        }
    
        // 配置扫描参数
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(lazyInitialization)) {
          scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
        }
        scanner.registerFilters();
    
        // MS : 快进到 ClassPathMapperScanner.scan方法,读取到beanDefinitions定义后将class替换为MapperFactoryBean<T>对象
        //      最后注册到容器中的为MapperFactoryBean<T>对象,通过getObject方法获得mapper
        scanner.scan(
            StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
    }
    
    public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
      @Override
      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    
        if (beanDefinitions.isEmpty()) {
          LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
              + "' package. Please check your configuration.");
        } else {
          // 修改BeanDefinition的定义,将class对象替换为mapperFactoryBeanClass。
          processBeanDefinitions(beanDefinitions);
        }
    
        return beanDefinitions;
      }
    
    // MapperScannerConfigurer#postProcessBeanDefinitionRegistry()
    //   definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
    //   definition.setBeanClass(this.mapperFactoryBeanClass);
    }
    

    getMapper()

    // IOC容器调用 MapperFactoryBean.getObject() 获取beanname为*Mapper的对象
    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
      // 实际调用sqlSessionTemplate.sqlSessionFactory.configuration.mapperRegistry.getMapper(type, sqlSession);
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    
      public SqlSession getSqlSession() {
        return this.sqlSessionTemplate;
      }
    }
    
    // MapperRegistry 中存放了`Type-MapperProxyFactory`的映射,由MapperProxyFactory创建代理对象和invocationHandler对象(MapperProxy)
    // 由于传入参数为sqlSessionTemplate,因此由sqlSessionTemplate实现线程安全控制
    public class MapperRegistry {
        private final Configuration config;
        private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
    
        public MapperRegistry(Configuration config) {
            this.config = config;
        }
    
        public <T> T getMapper(Class<T> type, SqlSession sqlSessionTemplate) {
            MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
            if (mapperProxyFactory == null) {
                throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
            } else {
                try {
                    return mapperProxyFactory.newInstance(sqlSessionTemplate);
                } catch (Exception var5) {
                    throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
                }
            }
        }
    

    SqlSessionTemplate

    -> mapper代理对象方法 -> SqlSessionTemplate方法 -> sqlSessionProxy方法 -> SqlSessionInterceptor.invoke()
    -> SqlSessionUtils.getSqlSession() -> TransactionSynchronizationManager事务资源管理

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
          PersistenceExceptionTranslator exceptionTranslator) {
    
        notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        notNull(executorType, "Property 'executorType' is required");
    
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
    
        // SqlSessionTemplate持有一个SqlSessionProxy对象,对sqlSessionProxy的操作都会经过SqlSessionInterceptor.invoke()方法
        this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
            new Class[] { SqlSession.class }, new SqlSessionInterceptor());
    }
    
    // SqlSessionProxy - InvocationHandler对象
      private class SqlSessionInterceptor implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          // MS : 此处获取SqlSession(--- 具体逻辑与Spring的TransactionSynchronizationManager有关,通过ThreadLocal获取当前线程已有sqlSession)
          SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
              SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
          try {
            Object result = method.invoke(sqlSession, args);
            if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
              // force commit even on non-dirty sessions because some databases require
              // a commit/rollback before calling close()
              sqlSession.commit(true);
            }
            return result;
          } catch (Throwable t) {
            Throwable unwrapped = unwrapThrowable(t);
            if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
              // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
              closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
              sqlSession = null;
              Throwable translated = SqlSessionTemplate.this.exceptionTranslator
                  .translateExceptionIfPossible((PersistenceException) unwrapped);
              if (translated != null) {
                unwrapped = translated;
              }
            }
            throw unwrapped;
          } finally {
            if (sqlSession != null) {
              closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
            }
          }
        }
      }
    }
    

    由Spring TransactionSynchronizationManager对象 - 实现SqlSession线程相关

    1. 从TransactionSynchronizationManager中获取SqlSessionHolder对象(底层为ThreadLocal数据结构,线程相关)
    • 如果存在,则返回SqlSessionHolder对象持有的SqlSession
    • 如果不存在,则session = sessionFactory.openSession(executorType),包装成SqlSessionHolder并注册
    SqlSessionUtils#registerSessionHolder()
            // 将session包装成SqlSessionHolder对象
            holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
    
            // 将SqlSessionHolder对象注册到TransactionSynchronizationManager的ThreadLocal中
            TransactionSynchronizationManager.bindResource(sessionFactory, holder);
    
            // 将SqlSessionSynchronization对象注册到TransactionSynchronizationManager的ThreadLocal中,该对象可以实现Spring事务管理前后置代码切入
            TransactionSynchronizationManager
                .registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
            holder.setSynchronizedWithTransaction(true);
    
    1. TransactionSynchronizationAdapter对象 - Spring提供的事务管理前后置代码切入点
      private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter{}
    public abstract class TransactionSynchronizationAdapter implements TransactionSynchronization, Ordered {
        public TransactionSynchronizationAdapter() {}
    
        public int getOrder() {
            return 2147483647;}
    
        public void suspend() {}
    
        public void resume() {}
    
        public void flush() {}
    
        public void beforeCommit(boolean readOnly) {}
    
        public void beforeCompletion() {}
    
        public void afterCommit() {}
    
        public void afterCompletion(int status) {}
    }
    

    Spring实现MyBatis事务管理

    1. SqlSessionFactoryBean在解析配置文件时,会将SpringManagedTransactionFactory配置为Mybatis的事务管理工厂
    2. sqlSessionFactory.openSession()获取链接时,会与SpringManagedTransactionFactory中创建的SpringManagedTransaction绑定
    3. Connection conn = springManagedTransaction.getConnection()
    4. 实际上,Spring通过 DataSourceUtils.getConnection(this.dataSource);获得链接,如此,链接由Spring进行控制
        public static Connection doGetConnection(DataSource dataSource) throws SQLException {
            Assert.notNull(dataSource, "No DataSource specified");
            ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
            if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) {
                logger.debug("Fetching JDBC Connection from DataSource");
                Connection con = fetchConnection(dataSource);
                if (TransactionSynchronizationManager.isSynchronizationActive()) {
                    try {
                        ConnectionHolder holderToUse = conHolder;
                        if (conHolder == null) {
                            holderToUse = new ConnectionHolder(con);
                        } else {
                            conHolder.setConnection(con);
                        }
    
                        holderToUse.requested();
                        TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource));
                        holderToUse.setSynchronizedWithTransaction(true);
                        if (holderToUse != conHolder) {
                            TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
                        }
                    } catch (RuntimeException var4) {
                        releaseConnection(con, dataSource);
                        throw var4;
                    }
                }
    
                return con;
            } else {
                conHolder.requested();
                if (!conHolder.hasConnection()) {
                    logger.debug("Fetching resumed JDBC Connection from DataSource");
                    conHolder.setConnection(fetchConnection(dataSource));
                }
    
                return conHolder.getConnection();
            }
        }
    

    欢迎疑问、期待评论、感谢指点 -- kiqi,愿同您为友

    -- 星河有灿灿,愿与之辉

  • 相关阅读:
    C基础02天——笔记
    C基础-指针和数组等价转换
    C基础04天—选择排序笔记
    C基础-标准C语言头文件
    C基础-C的关键字
    Ubuntu linux 环境基本使用
    C基础01天——常量变量笔记
    根据评分,用js输出评价星星的样式
    横向滚动条展示 css
    ajax 城市区域选择三级联动
  • 原文地址:https://www.cnblogs.com/kiqi/p/14368108.html
Copyright © 2011-2022 走看看