(四)MyBatis源码解析之SqlSession

原创 2016年06月02日 17:00:37

上篇博文将完了MyBatis的配置文件解析的过程,从这篇博文开始分析MyBatis的执行流程

解析完配置文件之后,配置文件中的所有信息就存储到了Configuration对象中了,在创建SqlSessionFactory对象时将Configuration做为SqlSessionFactory对象的一个属性,通过调用SqlSessionFactory的openSession(...),openSession()有很多重载方法,创建SqlSession对象只能通过openSession()和这些重载方法

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
下面的代码是实际创建SqlSession的地方,在这里首先要获得解析XML文件时创建的TransactionFacroty对象,其次根据TransactionFactory创建事务对象Transaction,然后通过execType创建Executor,execType的类型决定了创建的Executor对象的类型
  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有三种类型,分别是BATCH,REUSE,SIMPLE,默认的是SIMPLE类型,这三种类型分别对应着三种executor,BatchExecutor专门用于执行批量sql操作,ReuseExecutor会重用statement执行sql操作,SimpleExecutor就是简单的执行sql操作,除了这三种类型之外Mybatis还支持CacheExecutor,这就是Mybatis内建的缓存机制,在SqlSession级别提供对SQL查询结果的缓存,即:如果你调用了相同的select查询,Mybatis会将放在缓存的结果返回,而不会去再查询数据库,后面具体分析查询过程时会分析Mybatis是如何实现缓存的,在Mybatis中真正负责执行数据库操作的就是这些Executor

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
最后executor对象是可以被插件拦截的,关于插件后面会有专门的博文进行分析

通过代理获得Mapper

Mybatis建议通过mapper对象访问mybatis,使用mapper和使用普通的方式的区别主要有以下几点

    public int getUserInfo(long userId){
    	return getSqlSession().selectOne("net.klq.inter.po.TbNsBssUserInfoCn.selectUserInfoCn", userId);
    }

1、普通的方式在dao层查询的id和namespace是手写的,和xml中的配置文件有可能会对不上,实际工作中经常会碰到这种状况,而使用mapper的时候每个接口在哪个包下是固定的不会随意改变,省去了我们写包名时候容易导致的错误

2、普通的方式入参只能有一个,如果sql需要多个参数,我们需要先把多个参数封装成一个对象或者是放到map中,而使用mapper的时候可以使用任意多个参数

知道了通过Mapper编程的好处之后,思考一个问题mapper是一个接口,没有实现类怎么可以调用该接口的方法呢?答案就是动态代理,使用sqlSession的getMapper(...)方法,在调用该方法的时候传我们需要的Mapper接口的class对象进去,mybatis就可以为我们创建该对象的实现类了,具体是怎么创建的呢,一起来看看

    MessageMapperDao messageMapper = sqlSession.getMapper(MessageMapperDao.class);
Mybatis会去knownMappers中查找是否为该接口创建了一个代理工厂,在Mybatis中使用MapperProxyFactory作为代理工厂,找到代理工厂后会通过jdk的动态代理功能为我们创建一个动态代理对象然后返回,在应用中用接口来引用这个动态代理对象,接下来我们所有的增删改查都是通过这个动态代理对象进行的,Mybatis使用MapperProxy作为动态代理的对象,而且Mybatis在创建代理对象时将sqlSession的信息给了MapperProxy,这就

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    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);
    }
  }

到目前为止我们已经找到了Mybatis提供的动态代理类了,但是这个动态代理类帮我们做了什么?怎么就实现了调用接口方法就可以执行xml配置文件里的sql了呢,别着急,让我们来看看Mybatis提供的这个动态代理类,确实到目前为止接口和sql还没有关联,但是仔细一看我们已经为关联做好准备了。methodCache属性具有存储Method和MapperMethod对应关系的功能,而MapperMethod中包含Configuration对象,而Configuration对象包含xml配置文件的所有信息,当然也就包含sql的信息,这就为我们建立接口方法和sql的关联关系做好了准备。再接下来就是具体的增删改查功能了,这是mybatis最最核心的功能,也是最复杂的功能,我们会多篇博文来介绍增删改查

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

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

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}


在介绍增删改查之前,我们先来看一下为什么通过接口查询可以设置多个参数,而通过普通的方法查询不行,通过普通方法查询时Mybatis是不知道我们到底要传进去多少个参数,每个参数之间的关系是什么样的,所以Mybatis规定只能有一个参数,如果我们想传多个参数有一般是有两种方式,将多个参数封装成一个对象或者将多个参数放到一个map中,这对于开发者来说是很不方便的,既然mybatis规定参数只能是一个为什么通过接口查询就可以是多个参数呢,因为mybatis在内部将我们传进去的多个参数转成了一个map省去我们自己转换的这个步骤,下面看看是如何转换的,jdk的动态代理会将方法的参数转成一个Object[]数组,定义多个参数时必须使用@Param注解Mybatis才可以识别,像下面这样声明多个参数

	public List<Message> queryMessageList(@Param(value = "command") String command,@Param(value="description")String description);

在调用增删改查方法时,mybatis通过反射可以知道mybatis有多个参数,如果没有参数下面方法直接返回null,如果有一个参数就返回args[0],如果有多个参数根据@Param的顺序依次放到map中,转换成map之后普通方法执行数据库操作和通过接口操作数据库就没有区别了

public Object convertArgsToSqlCommandParam(Object[] args) {
      final int paramCount = params.size();
      if (args == null || paramCount == 0) {
        return null;
      } else if (!hasNamedParameters && paramCount == 1) {
        return args[params.keySet().iterator().next().intValue()];
      } else {
        final Map<String, Object> param = new ParamMap<Object>();
        int i = 0;
        for (Map.Entry<Integer, String> entry : params.entrySet()) {
          param.put(entry.getValue(), args[entry.getKey().intValue()]);
          // issue #71, add param names as param1, param2...but ensure backward compatibility
          final String genericParamName = "param" + String.valueOf(i + 1);
          if (!param.containsKey(genericParamName)) {
            param.put(genericParamName, args[entry.getKey()]);
          }
          i++;
        }
        return param;
      }
    }







版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

MyBatis源码分析-SQL语句执行的完整流程

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简...

mybatis学习笔记(八)Sqlsession详解

Sqlsession意味着创建数据库会话。mybatis中使用SqlsessionFactory的openSession方法创建。 public class TestMyBatis { ...

sqlSession.getMapper(UserMapper.class)的使用方式

说明: 1,本文采用的Mybatis框架版本为: mybatis-3.0.4.jar 2, 以持久化一个简单的User bean为例 User实体类的代码如下: J...

mybatis 源码学习之getMapper过程分析

mybatis 源码学习之getMapper过程分析

Mybatis使用中遇到的问题-selectOne与返回值类型

Mybatis实操遇到的问题-由selectOne和返回值类型引发的一系列问题先是一个简单的错误写了一个简单的测试项目,分为两层:web层和core层,在写core层的DAO操作时候,使用了selec...
  • santta
  • santta
  • 2016-07-30 21:50
  • 9265

深入浅出MyBatis-Sqlsession

前面的章节主要讲mybatis如何解析配置文件,这些都是一次性的过程。从本章开始讲解动态的过程,它们跟应用程序对mybatis的调用密切相关。本章先从sqlsession开始。 创建 正如其名,S...

Mybatis框架通用Dao中getSqlSession().selectOne()的应用

一、通用DAO中,抽象出BaseDao接口,静态的字符串方法名。 二、实际项目中的应用。 实际类的初始化构造。 三、网络搜索的知识点。

mybatis3常见数据操作

一、一般增删查改操作   数据映射文件User.xml代码如下:

Mybatis3源码分析(一):从sqlSession说起

分析MyBaits3的源码首先得从sqlSessionFactory开始,先来看一段spring配置文件中Mybaits的sqlSessionFactory的配置。 ...

MyBatis源码分析(三)-SqlSession理解

SqlSession理解 一、创建SqlSession 1.创建事务 a.事务工厂根据DataSource创建一个事务对象(Connection对象,事务级别,是否自动提交) b.事务工厂根据...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)