Mybatis的解析和运行原理:SqlSession运行过程

3552 篇文章 117 订阅

使用Mybatis时,最常看见的语句之一:

XXXMapper mapper = sqlSession.getMapper(XXXMapper.class);
复制代码

通过getMapper()方法得到了一个XXXMapper的对象。

要知道XXXMapper本身是一个接口,而我们代码是没有写实现类的。所以该对象是由Mybatis通过:反射和动态代理,这两项技术创建了一个代理对象,现在我们来分析一下这个工程。

SqlSession是一个接口,有一个实现类DefaultSqlSession,实现了getMapper方法:

@Override
public <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}
复制代码

我们会发现它调用了configuration.getMapper方法,回想一下这个configuration怎么来的。

回顾:

build方式创建SqlSessionFactory时,SqlSessionFactory也是一个接口,它的默认实现类是DefaultSqlSessionFactory

sqlSessionFactory = new SqlSessionFactoryBuilder().build(xml);
复制代码

configuration通过parser.parse()返回

return build(parser.parse());
复制代码

并且调用build(Configuration config)方法,创建了SqlSessionFactory,拿到它后,我们要调用其openSession()方法。

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}
复制代码

拿到SqlSession

sqlSessionFactory通过openSession()方法,获得SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession();
复制代码

我们知道sqlSessionFactory实质上是DefaultSqlSessionFactory,所以其底层是调用DefaultSqlSessionFactory的方法,而配置configuration已经作为参数被传入其中了:

@Override
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
复制代码

我们会发现DefaultSqlSessionFactory又调用了openSessionFromDataSource,并且将configuration.getDefaultExecutorType()作为参数传入。

先看openSessionFromDataSource定义:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
复制代码

我们注意到它是一个ExecutorType类,我们去看

configuration.getDefaultExecutorType()
复制代码

会发现它就是配置了mybatis的默认执行器:SIMPLE。 最后通过调用构造函数返回SqlSession,该构造器就是我们开头看见的代码:

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  this.configuration = configuration;
  this.executor = executor;
  this.dirty = false;
  this.autoCommit = autoCommit;
}
复制代码
  1. SqlSession是一个接口,有一个实现类DefaultSqlSession,实现了getMapper方法。
  2. SqlSessionFactory是一个接口,它的默认实现类是DefaultSqlSessionFactory,它调用openSession方法,执行openSessionFromDataSource,去生成DefaultSqlSession。:
@Override
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
复制代码
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    ...
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    ...
  } finally {
    ...
  }
}
复制代码

拿到代理

回过头来,它是调用configuration.getMapper(type, this);生成对象的。

@Override
public <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}
复制代码

该方法如下,将DefaultSqlSession类作为参数传递,又调用了mapperRegistry.getMapper(type, sqlSession)

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}
复制代码

这里先不展开mapperRegistry,它是一个映射器的注册器。我们就看调用的这个方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 #1 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
 #2 if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    return mapperProxyFactory.newInstance(sqlSession); // 看这里
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}
复制代码

#1#2,一看抛出的异常,我们就理解了,我们的映射器如果没有注册在配置文件中的话,就会抛出"Type " + type + " is not known to the MapperRegistry."异常。所以这里我们又可以倒回去去仔细研究配置文件中哪里去配置了这个knownMappers


(待补充)

这里贴出核心代码,在XMLConfigBuilder中有这样一行代码,其中mapperClass是字符串,我们通常会注册时写,写入的是mapper接口的全类路径名:

<mapper class="top.chengyunlai.mapper.UserAnnotationMapper"/>
复制代码
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
复制代码

如果注册在配置文件中的话,我们就要执行mapperProxyFactory.newInstance(sqlSession)

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

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
复制代码

这里就明了,它是使用动态代理的方式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值