Spring整合Mybatis到底整合了啥?(一、Mybatis跑了个神)

@Service
public class DemoServiceImpl implements DemoService {   
 
    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional
    public String sayHello(String name) {
        List<User> list= userMapper.selectUser();
        return "Hello, " + name + " (from Spring Boot)"+ JSONObject.toJSONString(list);
    }
}

这是一段使用查询用户列表的代码;UserMapper 是Dao层的一个接口。为什么注入了这个接口,就可以直接查询到了数据?这背后到底发生了什么?今天辉哥就来一层层的扒开她的面纱,喜欢的朋友点个赞哦;读此文需了解JDBC运行及Mybatis使用

首先我们来了解几个常用的概念,了解一下Mybatis基本运行原理

一、SqlSessionFactory

简单的理解,SqlSessionFactory是一个工厂类,专门生成SqlSession;下面是Mybatis简单使用Demo

图1

    @Test
    public void test() throws IOException {
        String resources = "mybatis-config.xml";
        InputStream in = Resources.getResourceAsStream(resources);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sessionFactory.openSession(true);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.selectUser();
        for (User user : users) {
            System.out.println(user);
        }
    }

从这一段代码中,可以看出;SqlSessionFactory由SqlSessionFactoryBuilder从配置文件中构建;

来深入一下SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);多余的代码先去掉,直接进入到核心的代码如下:

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

从这里可以看出他只是读取了配置文件;parser.parse()主要是构建Configuration ,Configuration简单的理解就是解析Mybatis配置文件,转成一个对象类,由XmlConfigBuilder生成;SqlSessionFactory最终由build(parser.parse())方法 生成默认的SqlSessionFactory;源码如下:

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

二、SqlSession

图1中:执行语句 SqlSession sqlSession = sessionFactory.openSession(true); 生成了Session

openSession(true)方法最终调用了openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)方法;来看一下这个源码

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

ExecutorType execType执行器默认使用Simple版本;TransactionIsolationLevel level事务隔离级别为默认;boolean autoCommit自动提交为true

图1中执行  UserMapper mapper = sqlSession.getMapper(UserMapper.class),生成代理类。

最终调用的核心源码是

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

MapperProxy是一个InvocationHandler类,里面有代理的逻辑

  @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 (method.isDefault()) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

执行MapperMethod类中的execute方法,来执行相应的sql

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      ... ...
}

Sqlsession中封装了数据源;Sql的预编译语句,等参数,可以通地SqlSession直接执行语句;

内部也封装了一级缓存,当重复sql在同 一个sqlsession中执行时,可以直接从缓存中获取;这类源码就不一一展示了,有兴趣的伙伴可以自行进入看看。

通过以上神操作,整个操作数据的流程就可以走通啦!

三、SqlSessionTemplate

SqlsessionTemplate本质是对SqlSession的一次封装;复用当前线程中存在的SqlSession;并生成代理对象;上源码一目了然

 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;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

sqlSessionProxy是一个代理类,他代理了什么呢?来看一下SqlSessionInterceptor();

  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
        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;
    }

  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    LOGGER.debug(() -> "Creating a new SqlSession");
    session = sessionFactory.openSession(executorType);

    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
 }

从getSqlSession方法中可以看到,从当前线程的ThreadLocal中获取了SqlSessionHolder 对象;  (不了解TransactionSynchronizationManager.getResource(sessionFactory);方法的,可以了解一下事务同步器获取ThreadLocal中的对象。)

并从SqlSessionHolder中获取SqlSession对象。因此本质上是从当前线程中复用SqlSession;

当然,如果当前线程中的SqlSession为空,则会执行session=sessionFactory.openSession(executorType);从新获取一个SqlSession;

至此三大重要概念都讲解完毕!下一期讲解Spring整合!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值