(四)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学习笔记(八)Sqlsession详解

Sqlsession意味着创建数据库会话。mybatis中使用SqlsessionFactory的openSession方法创建。 public class TestMyBatis { ...
  • oh_Mourinho
  • oh_Mourinho
  • 2016年07月08日 11:06
  • 19389

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

一、通用DAO中,抽象出BaseDao接口,静态的字符串方法名。 二、实际项目中的应用。 实际类的初始化构造。 三、网络搜索的知识点。...
  • limuzi13
  • limuzi13
  • 2017年05月23日 16:58
  • 2575

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

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

mybatis入门(一)-mybatis介绍和获取SqlSession

首先什么是MyBatis呢?要找到这个问题的答案,当然最准确的说法肯定在开发出它的人那,谁是开发它的人呢。然后...        我们就要用到学软件必不可少的神器了 https://github.c...
  • u010281209
  • u010281209
  • 2016年11月27日 10:13
  • 2053

MyBatis SqlSessionFactory的几种常见创建方式

MyBatis框架主要是围绕着SqlSessionFactory这个类进行的,这个的创建过程如下: 定义一个Configuration对象,其中包含数据源、事务、mapper文件资源以及影响数据...
  • jimolangge123
  • jimolangge123
  • 2015年10月18日 15:59
  • 14910

深入浅出MyBatis-Sqlsession

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

Mybatis源码分析一(SqlsessionFactory及源码整体结构)

搞java的想提高自己的姿势水平,想拿高工资,对常用开源框架的深入了解是必不可少的,想深入了解源码分析更是必不可少的,今天我开始对mybatis的源码进行分析,并做点记录以备查验。开源框架研究,文档的...
  • wxhlqcl2010
  • wxhlqcl2010
  • 2016年05月15日 15:03
  • 4827

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

分析MyBaits3的源码首先得从sqlSessionFactory开始,先来看一段spring配置文件中Mybaits的sqlSessionFactory的配置。 ...
  • flashflight
  • flashflight
  • 2015年01月23日 01:11
  • 3507

myBatis源码学习之SqlSession

/** * @author Clinton Begin */ public class DefaultSqlSession implements SqlSession { private C...
  • qq924862077
  • qq924862077
  • 2015年12月30日 20:07
  • 2524

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

分析MyBaits3的源码首先得从sqlSessionFactory开始,先来看一段spring配置文件中Mybaits的sqlSessionFactory的配置。 ...
  • flashflight
  • flashflight
  • 2015年01月23日 01:11
  • 3507
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:(四)MyBatis源码解析之SqlSession
举报原因:
原因补充:

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