01.ORM 比较JDBC 和mybatis的区别
02.Mybatis 结构
Mybatis 整体执行流程
03.Mybatis 使用到的设计模式
1.Builder 模式 : SQLSessionFactoryBuilder、Environment
在Mybatis 环境的初始化过程中,
SqlSessionFactoryBuilder 会调用XMLConfigBuilder 读取 mybatis-config.xml 和所有的 *Mapper.xml 文件,
构建Mybatis 核心对象Configuration ,然后将Configuration 对象作为参数构建一个 SqlSessionFactory。
在这个过程中,Builder 会读取文件或配置,然后做XpathParser 解析,配置或语法解析,反射生成对象,存入结果缓存等步骤,这么多的工作不是一个构造函数所能包括的,因此大量采用了Builder 模式来解决。
2.工厂模式 :SqlSessionFactory、TransactionFactory、LogFactory
在Mybatis 中使用了简单工厂模式,因为没有什么复杂的业务逻辑。
Mybatis 中执行sql 语句、获取Mappers、管理事务的核心接口SqlSession 的创建过程使用到了工厂模式,有一个SqlSessionFactory 来负责 SqlSession 的创建。
3.单例模式 : ErrorContext、LogFactory
4.代理模式 :Mybatis 实现核心,比如MapperProxy、ConnectionLogger
代理模式可以认为是Mybatis 核心使用模式,正式由于这个模式,我们只需要编写Mapper 接口,不需要实现,由Mybatis 后台帮我们完成具体SQL 的执行。
当我们使用Configuration 的getMapper 方法时,会调用mapperRegistry.getMapper 方法,而该方法又会调用mapperProxyFactory.newInstance(sqlSession)来生成一个具体的代理。
5.模板方法模式 :BaseExecutor、SimpleExecutor、BaseTypeHandler 和其子类
04.mybatis-config.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/>
<typeAliases>
<package name=""/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${database.driver}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource=""></mapper>
</mappers>
</configuration>
01.properties 元素
作用:一般用于配置外部数据库连接信息
在核心配置文件中,通过 properties 标签来引用外部的属性文件,通过 resource 属性来指定外部属性文件路径。
例如:
在resource目录下,创建一个JDBC.properties文件
内容如下:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=root
这是datasource的属性赋值
02配置环境(environments)
配置环境(environments) MyBatis 可以配置成适应多种环境,也就是配置多个environment子元素,这种机制有助于将 SQL 映射应用于多种数据库之中 注意:
environments的default属性取值要和其中一个environment的id属性取值一致。
03.transactionManager 元素
transactionManager 元素的type属性表示事务管理器类型,在MyBatis中有两种类型:
JDBC – 这种方式是直接使用了JDBC的事务提交和回滚设置
MANAGED(托管) –这种方式从来不提交或回滚一个连接。而是让容器来管理事务的整个生命周期(比如 Spring)
04.dataSource元素
dataSource元素中主要配置了 JDBC 连接对象的资源,它的type属性表示数据源类型,内建的数据源类型有三种:
UNPOOLED: 每次被请求时简单打开和关闭连接
POOLED:这是JDBC连接对象的数据库连接池的实现,用来避免创建新的连接实例
JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器
property子元素中配置了具体的数据库连接信息
driver – 是 JDBC 驱动的 Java 类的完全限定名
url – 是数据库的 JDBC URL 地址
username – 登录数据库的用户名
password – 登录数据库的密码
05.mappers元素
告诉 MyBatis 到哪里去找到这些sql语句。
mappers(映射器) 使用相对于类路径的资源引用
mapper resource=“asia/xiaojiang/mybatis03/dao/UserMapper.xml”
05.MyBatis映射文件
namespace属性
映射文件的根元素为mapper,它的namespace属性为映射器接口的完全限定名
以下子元素的id属性值为接口中的某个方法名称
insert – 映射插入语句
update – 映射更新语句
delete – 映射删除语句
select – 映射查询语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace代表唯一标识符 -->
<mapper namespace="com.mybatis.mapper.UserMapper">
<select id="selectAllUsers"
resultType="com.mybatis.entity.User">
select * from user
</select>
</mapper>
06.具体流程
public class MybatisTest {
public static void main(String[] args) throws IOException {
//1. 读取mybatis-config.xml 文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//2. 构建SqlSessionFactory(创建了DefaultSqlSessionFactory)
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3. 打开SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//4. 通过 SqlSession 获取到 Mapper的代理对象(MapperProxy)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//5. 获取mapper 接口对象的方法操作数据库
List<SysUser> sysUsers = mapper.selectByIdList(Arrays.asList(1L));
System.out.println("查询结果为:" + sysUsers.size());
}
}
我们可以轻易的发现每次去请求数据库操作都需要通过 SqlSessionFactory 去获取到 SqlSession,而 SqlSessionFactory 是通过 SqlSessionFactoryBuilder 构造出来的, 并且最后请求操作完成后都关闭了SqlSession。
1.SqlSessionFactory 一个应用程序中最好只有1个,即单列。
2.SqlSessionFactoryBuilder 只有一个作用: 创建 SqlSessionFactory对象。
3.一个SqlSession应该仅存活于一个业务请求中都有一个SqlSession,也可以说一个SqlSession对应一次数据库会话,它不是永久存活的,每次访问数据库时都需要创建它,并且访问完成后都必须执行会话关闭
详细讲解:
01.SqlSessionFactory
一.SqlSessionFactory 来源:
每一个MyBatis的应用程序都以一个SqlSessionFactory 对象的实例为核心 。
SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来获得 。 这个是建造者模式。
SqlSessionFactoryBuilder会调用XMLConfigBuilder.parse()读取所有的MybatisMapConfig.xml,构建Mybatis运行的核心对象Configuration对象,然后将该Configuration对象作为参数构建一个SqlSessionFactory对象。
其中XMLConfigBuilder在构建Configuration对象时,也会调用XMLMapperBuilder用于读取*.Mapper文件,而XMLMapperBuilder会使用XMLStatementBuilder来读取和build所有的SQL语句。
SqlSessionFactoryBuilder:第二个方法
package org.example.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
二.SqlSessionFactory用法:
在Mybatis中比如SqlSessionFactory使用的是工厂模式,该工厂没有那么复杂的逻辑,是一个简单工厂模式。
SqlSessionFactory用来创建SqlSession对象.
SqlSessionFactory:
//SqlSessionFactory接口源码如下所示:
package org.apache.ibatis.session;
import java.sql.Connection;
public interface SqlSessionFactory {
SqlSession openSession();//这个方法最经常用,用来创建SqlSession对象.
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
在DefaultSqlSessionFactory的默认工厂实现里,有一个方法可以看出工厂怎么产出一个产品:
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
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();
}
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
//defaultExecutorType 为空的话则为:ExecutorType.SIMPLE
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;
}
整个SqlSession 的创建分 3个步骤:
1、 获取到 TransactionFactory 事务工厂对象 ( 如果有仔细看过 SqlSessionFactoryBean.buildSqlSessionFactory() 过程的同学,应该能够看到是 SpringManagedTransactionFactory )
2、 通过 TransactionFactory 获取了一个 事务 Transaction
3、 根据 execType(默认是 SIMPLE ) 获取了一个Executor (真正执行数据库操作的对象)
4、 创建并返回 DefaultSqlSession(SqlSession实现类) 对象
通过源码我们知道每次 SqlSession(准确地说是 DefaultSqlSession )的创建都会 有一个 Transaction(在Mybatis-Spring 中 是 SpringManagedTransaction ) 事务对象 的生成。也就是说:
1、 一个事务 Transaction 对象与一个 SqlSession 对象 是一一对应的关系。
2、 同一个SqlSession 不管执行多少次数据库操作。只要没有执行close,那么整个操作都是在同一个 Transaction 中执行的。
此外:
这是一个openSession调用的底层方法,该方法先从configuration读取对应的环境配置,然后初始化TransactionFactory获得一个Transaction对象,然后通过Transaction获取一个Executor对象,最后通过configuration、Executor、是否autoCommit三个参数构建了SqlSession。
在这里其实也可以看到端倪,SqlSession的执行,其实是委托给对应的Executor来进行的
02.SqlSession
SqlSession是MyBatis的关键对象,是执行持久化操作的独享,类似于JDBC中的Connection
SqlSession是一个接口
//SqlSession接口源码如下所示:
package org.apache.ibatis.session;
import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.executor.BatchResult;
public interface SqlSession extends Closeable {
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
void select(String statement, Object parameter, ResultHandler handler);
void select(String statement, ResultHandler handler);
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
int insert(String statement);
int insert(String statement, Object parameter);
int update(String statement);
int update(String statement, Object parameter);
int delete(String statement);
int delete(String statement, Object parameter);
void commit();
void commit(boolean force);
void rollback();
void rollback(boolean force);
List<BatchResult> flushStatements();
void close();
void clearCache();
Configuration getConfiguration();
<T> T getMapper(Class<T> type);
Connection getConnection();
}
可以看出来这个接口主要定义类关于CRUD、数据库事务、数据库刷新等相关操作。下面看它的默认实现类:DefaultSqlSession
部分具体代码:
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
@Override
public <T> T selectOne(String statement) {
return this.selectOne(statement, null);
}
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
final List<? extends V> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
final DefaultResultContext<V> context = new DefaultResultContext<>();
for (V o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
return mapResultHandler.getMappedResults();
}
@Override
public <T> Cursor<T> selectCursor(String statement) {
return selectCursor(statement, null);
}
@Override
public <T> Cursor<T> selectCursor(String statement, Object parameter) {
return selectCursor(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
registerCursor(cursor);
return cursor;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public <E> List<E> selectList(String statement) {
return this.selectList(statement, null);
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public void select(String statement, Object parameter, ResultHandler handler) {
select(statement, parameter, RowBounds.DEFAULT, handler);
}
@Override
public void select(String statement, ResultHandler handler) {
select(statement, null, RowBounds.DEFAULT, handler);
}
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public int insert(String statement) {
return insert(statement, null);
}
@Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}
@Override
public int update(String statement) {
return update(statement, null);
}
@Override
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public int delete(String statement) {
return update(statement, null);
}
@Override
public int delete(String statement, Object parameter) {
return update(statement, parameter);
}
@Override
public void commit() {
commit(false);
}
@Override
public void commit(boolean force) {
try {
executor.commit(isCommitOrRollbackRequired(force));
dirty = false;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
}
首先要执行sql,先运行这个方法 DefaultSqlSession 的 getMapper 方法:
// type为Mapper接口的类型,例如interface org.example.dao.EmployeeMapper
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);// 这个 是 DefaultSqlSession 的一个属性 configuration
}
configuration.getMapper(type, sqlSession)
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
mapperRegistry.getMapper(type, sqlSession)
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 通过knownMappers获取Mapper接口对应的Mapper代理工厂
// knownMappers在注册的时候已经存好我们的mapper接口了,
// 一个是interface org.example.dao.EmployeeMapper,一个是interface org.example.dao.EmployeeMapperPlus
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 通过该Mapper代理工厂生成一个代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
mapperProxyFactory.newInstance(sqlSession)
public T newInstance(SqlSession sqlSession) {
// 这里mapperInterface也就是org.example.dao.EmployeeMapper
// 创建MapperProxy,它实现了InvocationHandler
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// 是通过代理返回一个Mapper的代理对象org.apache.ibatis.binding.MapperProxy@11d8ae8b
return newInstance(mapperProxy);
}
mapperProxyFactory.newInstance(mapperProxy)
protected T newInstance(MapperProxy<T> mapperProxy) {
// 调用Proxy.newProxyInstance创建MapperProxy的代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
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<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
最后返回一个MapperProxy的代理对象,MapperProxy类作为Mapper接口的实现类代理对象,可利用反射来代理完成业务方法的实现。
03.MapperProxy:
动态代理类MapperProxy,调用Mapper接口的所有方法都会先调用到这个代理类的invoke方法(注意由于Mybatis中的Mapper接口没有实现类,所以MapperProxy这个代理对象中没有委托类,也就是说MapperProxy干了代理类和委托类的事情)。好了下面重点看下invoke方法。
MapperProxy:
//MapperProxy代理类
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;
}
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果方法是Object类的方法,则直接反射执行
if (Object.class.equals(method.getDeclaringClass())) {
//Method.invoke()方法的功能:通过反射运行指定的方法,这里就执行了sql语句了
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//如果不是,从缓存中获取MapperMethod,如果为空则创建并加入缓存,然后执行sql语句
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
// 根据方法从缓存中获取
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
// 不存在则创建一个
mapperMethod = new MapperMethod(this.mapperInterface, method,this.sqlSession.getConfiguration());
// 放入缓存
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
MapperMethod:
private final MapperMethod.SqlCommand command;
private final MapperMethod.MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
SqlCommand一个内部类 封装了SQL标签的类型 insert update delete select
MethodSignature一个内部类 封装了方法的参数信息 返回类型信息等
MapperProxy的invoke方法非常简单,主要干的工作就是创建MapperMethod对象或者是从缓存中获取MapperMethod对象。获取到这个对象后执行execute方法。
所以这边需要进入MapperMethod的execute方法:这个方法判断你当前执行的方式是增删改查哪一种,并通过SqlSession执行相应的操作。(这边以sqlSession.selectOne这种方式进行分析~)
MapperMethod的execute
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//判断是CRUD那种方法
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;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {// 返回类型为void
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) { // 返回类型为集合或数组
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(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;
}
例子:
sqlSession.selectOne
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
sqlSession.selectOne方法会会调到DefaultSqlSession的selectList方法。
selectList方法:
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
这个方法获取了获取了MappedStatement对象,并最终调用了Executor的query方法。
然后,通过一层一层的调用,最终会来到doQuery方法
04.Executor
在Mybatis中,sqlSession的SQL执行,都是委托给Executor实现的,Executor包含以下结构:
其中的BaseExecutor就采用了模板方法模式,它实现了大部分的SQL执行逻辑,然后把以下几个方法交给子类定制化完成:
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
然后,通过一层一层的调用(这边省略了缓存操作的环节,会在后面的文章中介
绍),最终会来到doQuery方法, 这儿咱们就随便找个Excutor看看doQuery方
法的实现吧,我这儿选择了SimpleExecutor:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//内部封装了ParameterHandler和ResultSetHandler
StatementHandler handler = configuration.newStatementHandler(wrapper,ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
//StatementHandler封装了Statement, 让 StatementHandler 去处理
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}