spring-mybatis集成源码分析

丁_b6cdIP属地: 上海
字数 536

简述

1.在IOC容器的注册过程中,将mapper->MapperFactoryBean注册到容器的beanDefinitionMap中
2.配置SqlSessionFactoryBean,完成mybatis的Configuration的配置以及生成SqlSessionFactory
3.然后通过MapperFactoryBean(工厂bean)的getObject创建Mapper的代理对象MapperProxy
4.查询时openSession,执行查询

源码分析

SqlSessionFactory如何构建

SqlSessionFactoryBean实现InitializingBean,因此在bean初始化完成后会调用afterPropertiesSet方法
由以下代码可知,SqlSessionFactoryBean完成SqlSessionFactory的构建并且将它作为自己的成员变量

public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.dataSource, "Property 'dataSource' is required");
        Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

buildSqlSessionFactory方法解析mybatis的configLocation 和mapperLocations文件

//以下代码为删减后的代码
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
        XMLConfigBuilder xmlConfigBuilder = null;
        Configuration targetConfiguration;
//配置解析
        if (this.configLocation != null) {
            xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);
            targetConfiguration = xmlConfigBuilder.getConfiguration();
        } 
//解析mapper.xml
        if (!ObjectUtils.isEmpty(this.mapperLocations)) {
            Resource[] var24 = this.mapperLocations;
            int var4 = var24.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Resource mapperLocation = var24[var5];
                if (mapperLocation != null) {
                    try {
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
                        xmlMapperBuilder.parse();
                    } catch (Exception var19) {
                        throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var19);
                    } finally {
                        ErrorContext.instance().reset();
                    }

                    LOGGER.debug(() -> {
                        return "Parsed mapper file: '" + mapperLocation + "'";
                    });
                }
            }
          }
        return this.sqlSessionFactoryBuilder.build(targetConfiguration);
    }
mapper的代理

mapper的代理由getBean方法作为入口,由于mapper对应的MapperFactoryBean为factoryBean,所有调用它的getObject方法

public T getObject() throws Exception {
        return this.getSqlSession().getMapper(this.mapperInterface);
    }

追踪发现,最终还是调用了mybatis的MapperRegisty的getMapper方法,生成代理对象。最后执行this.factoryBeanObjectCache.put(beanName, object)将生成的代理对象放入容器。

sql执行

mapper被MapperProxy代理,调试发现,最终执行SqlSessionTemplate的selectOne方法

    public <T> T selectOne(String statement, Object parameter) {
        return this.sqlSessionProxy.selectOne(statement, parameter);
    }

sqlSessionProxy是在SqlSessionTemplate构造时创建的一个动态代理对象,所以调用SqlSessionInterceptor的invoke方法
由代码可知,首先会获取一个sqlSession,然后执行mybatis的语句执行流程,紧接着进行sqlSession的提交或释放

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

            Object unwrapped;
            try {
                Object result = method.invoke(sqlSession, args);
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }

                unwrapped = result;
            } catch (Throwable var11) {
                unwrapped = ExceptionUtil.unwrapThrowable(var11);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }

                throw (Throwable)unwrapped;
            } finally {
                if (sqlSession != null) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }

            }

            return unwrapped;
        }

获取session的时候,如果没有开启事务,那么直接调用openSession开启一个新的session,如果是开启事务的情况,那么将当前事务存储在当前的线程变量中,以便事务中其他执行时获取

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        Assert.notNull(executorType, "No ExecutorType specified");
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
            return session;
        } else {
            LOGGER.debug(() -> {
                return "Creating a new SqlSession";
            });
            session = sessionFactory.openSession(executorType);
            registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
            return session;
        }
    }
对比mybatis

1.session的创建
mybatis中session的创建和sql的执行是分开的,但是spring-mybatis中是在sql的执行调用时,通过代理的方式在sql执行前获取session。因此在不存在事务的情况,spring-mybatis的一级缓存是没有作用的
2.mapper的作用域
mybatis每次获取mapper都会生成它的代理对象,因此mybatis的mapper是session级别的;spring-mybatis在第一次获取后放入factoryBeanObjectCache,以后都是从factoryBeanObjectCache中获取,因此它是全局单例的。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
0人点赞
总资产0.059共写了3923字获得2个赞共1个粉丝