springboot整合Mybatis项目源码分析

我们先来看看Mybatis的几个核心类SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession之间的关系;

springboot整合Mybatis项目源码分析_第1张图片

 

 

大致流程是这个样子的,mybatis通过SqlSessionFactoryBuilder类的build方法和配置文件生成SqlSessionFactory对象,而SqlSession对象是通过SqlSessionFactory的open方法获取到的,这里我们说下这三个对象的生命周期,对于SqlSessionFactoryBuilder来说,他是为了创建SqlSessionFactory而存在的,SqlSessionFactory创建完成后就可以丢弃不要了,而SqlSessionFactory是创建SqlSession的基础,只要程序在运行,则SqlSessionFactory必须存在,所以SqlSessionFactory可以做成单例存在程序运行期间,而SqlSession由于是线程不安全的,所以存在方法体内或者一次请求的对象更合适;

生成SqlSession的代码如下:

 public void getSqlSession(){
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader("com/xxx/config.xml");
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
            SqlSession sqlSession =sessionFactory.openSession();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

 

而springboot整合mybatis时做了一些改变,SqlSessionFactoryBean代替了SqlSessionFactoryBuilder的用,SqlSessionTemplate代替了Sqlsession,看下面的代码我们通过注解的方式自定义了SqlSessionFactory和SqlSessionTemplate,接下来我们从注入看起,看看spring是如何整合mybatis的;

package com.spring.mybatis.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

@Configuration
@EnableTransactionManagement //开启注解事物管理
@MapperScan(basePackages = "com.spring.mybatis.mapper" ,sqlSessionFactoryRef = "sqlSessionFactory")
public class MybatisConfig {

    @Autowired
    @Qualifier("dataSource")
    private DataSource dataSource;


    //配置sqlSessionFactory
    //自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
    @Bean(name = "sqlSessionFactory")
    @Primary
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        SqlSessionFactory sessionFactory = null;
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
       /*
        Resource[] resources = new Resource[1];
        Resource方式只能一个一个列出mapper文件
       Resource resource = new ClassPathResource("mapping/DemoMapper.xml");
        resources[0] = resource;*/
       //PathMatchingResourcePatternResolver方式可以使用正则匹配
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        //设置mapper文件的路径Resource
        sessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapping/*.xml"));
        //给entity取别名,这样在mapper文件中就可以使用缩写,默认别名是类名,不区分大小写
        sessionFactoryBean.setTypeAliasesPackage("com.spring.mybatis.model");
        //设置数据源DataSource
        sessionFactoryBean.setDataSource(this.dataSource);
        Properties mybatisProperties = new Properties();
        mybatisProperties.setProperty("dialect", "mysql");
        //设置Properties属性
        sessionFactoryBean.setConfigurationProperties(mybatisProperties);
        try {
           /* 通过sessionFactoryBean获取sessionFactory配置文件,其实底层就是通过解析配置文件生成Configuration对象
            再通过SqlSessionFactoryBuild的build方法生成sessionFactory,return this.sqlSessionFactoryBuilder.build(configuration);
            有兴趣的可以自己通过debug跟一下,最后生成的是DefaultSqlSessionFactory对象*/
            sessionFactory = sessionFactoryBean.getObject();
            sessionFactory.getConfiguration().setCacheEnabled(true);
            sessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
            return  sessionFactory;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sessionFactory;
    }

}

分析万SqlSessionFactory对象的生成,我们再来看一下SqlSessionTemplate对象;

//配置sqlSessionTemplate
    @Bean(name = "sqlSessionTemplate")
    @Primary
    public SqlSessionTemplate getSqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }
 public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
    }

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
        this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
    }

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        Assert.notNull(executorType, "Property 'executorType' is required");
        //设置sqlSessionFactory对象
        this.sqlSessionFactory = sqlSessionFactory;
        //executorType=SIMPLE 
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        //sqlSessionProxy是一个SqlSession动态代理对象
        //newProxyInstance方法有三个参数,第一个参数是生成代理对象的类加载器,第二个参数是
        //生成那个对象的代理对象,接口显示,第三个参数是是指明生成代理对象要做的事情
        this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
    }

可能很多读者可能已经猜出来了这里生成SqlSession的动态代理的用处,我们前面说过,SqlSession是线程不安全的,我们之前都是要用的时候通过SqlSessionFactory open一个SqlSession,用完就关闭,但是我们现在用的SqlSessionTemplate是一个单例的,他里面也只要只有一个SqlSession对象,那么我们怎么保证的线程安全呢,这就是要用到SqlSession动态代理的用处了,我们可以看看SqlSessionInterceptor做了什么:

 private class SqlSessionInterceptor implements InvocationHandler {
        private SqlSessionInterceptor() {
        }

        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;
        }
    }

我们主要看的是生成SqlSession这一行,这个方法可以从spring事物的上下文获取事物范围内的SqlSession:

SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        Assert.notNull(executorType, "No ExecutorType specified");
        //从当前线程的threadLocal 中获取sqlSessionHolder
        //根据SqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder 
        //当SqlSessionFactory创建一个SqlSession时,就会在事物管理器中添加一对映射,
        //key=SqlSessionFactory,value=SqlSessionHolder
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
            return session;
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Creating a new SqlSession");
            }
           //获取新的sqlSession 对象。这里由sessionFacory产生的defaultSqlSession
            session = sessionFactory.openSession(executorType);
          //判断当前是否存在事务,将sqlSession 绑定到sqlSessionHolder 中,并放到threadLoacl 当中
            registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
            return session;
        }
    }

 这里可以看出SqlSession是视情况是否创建新的SqlSession然后执行,结果很清晰了,通过代理SqlSession的好处就是每次请求时都能保证SqlSession是线程安全的;

在mybatis中,是用MapperProxy动态代理我们的dao的,也就是说当我们调用dao的方法时,其实是调用的MapperProxy代理对象,接下来我们看看怎么获取MapperProxy对象吧:

先看看SqlSessionTemplate的getMapper方法:

springboot整合Mybatis项目源码分析_第2张图片

springboot整合Mybatis项目源码分析_第3张图片

通过debug很容易看出来,生成了一个mapperProxy对象代理我们的dao,我这里dao是DemoMapper,当我们调用DemoMapper接口的方法时,会调用的invoke方法,下面是mapperProxy类的一部分:

public class MapperProxy implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class mapperInterface;
  private final Map methodCache;

  public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

未完待续。。。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



 

你可能感兴趣的:(spring,mybatis)