文档章节

Mybatis之执行SQL过程

cregu
 cregu
发布于 03/31 12:36
字数 1175
阅读 28
收藏 1

概述

MyBatis执行Sql的一个过程当中是怎么样的呢,涉及到了那些类呢,让我们不妨来一一过一遍。MyBatis的用法可以采用纯MyBatis、或者使用Spring集成的用法。

纯MyBatis

原生MyBatis对外暴露的接口层是SqlSession,所有的Sql操作都是靠它来引导完成的,我们先看下SqlSession是怎么创建出来的,看下面这段伪代码:

// mybatis 配置文件
String resource = "mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
// 创建SqlSessionFactory,SqlSession的工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();

1、通过创建SqlSessionFactoryBuilder来生成SqlSessionFactory;

2、通过SqlSessionFactory工厂创建SqlSession;

执行Sql

从开发者的角度来看下怎么使用MyBatis来执行一次Sql:

  1. 获取某个Mapper接口的实现示例
  2. 调用这个Mapper接口的方法

就只要两步和普通方法的调用一样,MyBatis将Sql的执行隐藏了起来。

BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
Book book = bookMapper.findByName("Learn Java");

我们看下底层MyBatis是怎么实现的吧:

1、获取的是Mapper接口的代理实现,基于JDK动态代理

// SqlSession

public <T> T getMapper(Class<T> type) {
	return configuration.<T>getMapper(type, this);
}

// Configuration

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

// MapperRegistry

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 {
      // 每次get都重新创建
	  return mapperProxyFactory.newInstance(sqlSession);
	} catch (Exception e) {
	  throw new BindingException("Error getting mapper instance. Cause: " + e, e);
	}
}

// MapperProxyFactory 使用JDK动态代理,生成代理对象

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);
}
  • Mapper接口和MapperProxyFactory是一一映射的关系,维护在MapperRegistry对象中
  • MapperRegistry对象在Configuration对象的属性
  • Mapper接口和MapperProxyFactory的关系,在addMapper()方法中添加
public <T> void addMapper(Class<T> type) {
	if (type.isInterface()) {
	  if (hasMapper(type)) {
	    throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
	  }
	  boolean loadCompleted = false;
	  try {
	    knownMappers.put(type, new MapperProxyFactory<>(type));
	    // It's important that the type is added before the parser is run
	    // otherwise the binding may automatically be attempted by the
	    // mapper parser. If the type is already known, it won't try.
	    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
	    parser.parse();
	    loadCompleted = true;
	  } finally {
	    if (!loadCompleted) {
	      knownMappers.remove(type);
	    }
	  }
	}
}
  • MapperProxyFactory的作用是生成Mapper接口的代理实现

2、MapperProxy的invoker方法

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

private MapperMethod cachedMapperMethod(Method method) {
	return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
  • 关键点是MapperMethod对象的execute,MapperMethod和Method是一一映射的关系

3、MapperMethod的execute方法,来路由,调用SqlSession的相关方法

public Object execute(SqlSession sqlSession, Object[] args) {
	Object result;
	switch (command.getType()) {
	  case INSERT: {
	  	...
        sqlSession.insert(command.getName(), param)
	    break;
	  }
	  case UPDATE: {
	    ...
        sqlSession.update(command.getName(), param)
	    break;
	  }
	  case DELETE: {
	    ...
        sqlSession.delete(command.getName(), param)
	    break;
	  }
	  case SELECT:
	    ...
        sqlSession.selectXXX(command.getName(), param)
	    break;
	  case FLUSH:
	    result = sqlSession.flushStatements();
	    break;
	  default:
	    throw new BindingException("Unknown execution method for: " + command.getName());
	}
	if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
	  throw new BindingException("Mapper method '" + command.getName()
	      + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
	}
	return result;
}

4、SqlSession的增删改查方法

  • SqlSession的这些方法,我们可以直接拿来使用的
  • MyBatis对Mapper接口的处理,兜兜转转又回到了SqlSession的这些方法上

PS:Mapper接口的方法都会形成一个ID,来和MapperStatement一一映射

String statementId = mapperInterface.getName() + "." + methodName;

集成Spring

@Autowired
private BookMapper bookMapper;

@Test
public void test(){
    Assert.notNull(bookMapper);

    System.out.println(bookMapper.findByName("Learn Java"));
}

执行Sql

从开发者的角度来看下怎么使用MyBatis来执行一次Sql:

  1. 获取某个Mapper接口的实现示例,是Spring的bean
  2. 调用这个Mapper接口的方法

这里和原生MyBatis的使用方式的区别是,这里的Mapper接口的实现是Spring实现的,是Spring的bean,其他的用法不变

让我们看Spring是如何实现的?

MyBatis之Spring集成

总结

原生MyBatis和Spring集成MyBatis,在执行Sql的过程中,唯一的区别是Mapper接口实现的获取方式不同。

原生的需要自己从SqlSession中获取Mapper接口,并且需要管理这个Mapper接口(为了避免每次都重试获取生成)。Spring集成的方式,由Spring的IoC容器负责管理Mapper接口,在项目启动的时候,Spring已经将所有的Mapper接口的代理实现都注册到了容器里,并且都是单例的,使用到了MapperFactoryBean。

共同点是,获取的都是Mapper接口的代理实现,都用到了JDK动态代理。

© 著作权归作者所有

cregu
粉丝 0
博文 45
码字总数 36831
作品 0
杭州
私信 提问
Mybaits深入了解(一)----带你入门

Mybatis简介 Mybatis架构 Mybatis简介 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对iba...

令仔很忙
2016/07/01
0
0
存储过程-数据库

一、存储过程 存储过程主要包括以下元素:1.变量的声明和初始化;2.SQL语句;3.流程控制关键字(if.....else....);4.关键字。 一个简单的例子: @num,为int类型,需要用户在执行该存储过程时...

小车车
2016/08/27
34
0
综合技术 --myBatis理解

myBatis是一个基于java的持久层框架,它提供的持久层框架包括 SQL Maps和Data Access Objescts(DAO)。 myBatis是支持普通的SQL查询、存储过程和高级映射的持久层框架。myBatis使用简单的XML...

求是科技
2015/04/11
110
0
mybatisdomo 最新免费版

MyBatis 本是一个开源项目iBatis,MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old...

相心的泪
2016/08/26
20
0
终结篇:MyBatis原理深入解析(一)

1 引言# 本文主要讲解JDBC怎么演变到Mybatis的渐变过程,重点讲解了为什么要将JDBC封装成Mybaits这样一个持久层框架。再而论述Mybatis作为一个数据持久层框架本身有待改进之处。 2 JDBC实现查...

猿码道
2016/08/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

XXL-JOB使用命令行的方式启动python时,日志过多导致阻塞的解决方式

一、Runtime.getRuntime().exec()的阻塞问题 这个问题也不能算是XXL-JOB的问题,而是Java的Runtime.getRuntime().exec()造成的,BufferedReader的缓冲区大小有限,当不能及时从缓冲区中把输出...

codeobj
19分钟前
4
0
java后端获取字符串标签里面的具体值

1、如下:怎么获取value值,使用Jsoup解决 <select id='department' name='department' class='select' tabindex='6' onchange='changeDept()'><option value=''>院系</optio......

木九天
26分钟前
4
0
Xamarin图表开发基础教程(10)OxyPlot框架支持的图表类型

Xamarin图表开发基础教程(10)OxyPlot框架支持的图表类型 OxyPlot组件支持26种图表,这些图表按照功能和样式可以分为4大类,分别为线型图表、条型图表、金融图表和其它图表。 线型图表 OxyP...

大学霸
29分钟前
4
0
移动端input“输入框”常见问题及解决方法

移动端input“输入框”常见问题及解决方法 1. ios中,输入框获得焦点时,页面输入框被遮盖,定位的元素位置错乱: 当页input存在于吸顶或者吸底元素中时,用户点击输入框,输入法弹出后,fie...

tyou
31分钟前
4
0
初探Android线程池

前言 最近在看OkHttp的源码,看的时候发现有关线程池的运用,自己就仔细想了一下,这个块知识好像不是很牢固。没办法,再研究一下有关线程池的相关知识吧。学习就是一个查漏补缺的过程,最终...

二营长的意大利炮手
38分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部