MyBatis核心流程源码分析(上)

2024-01-17 21:49:45 浏览数 (2)

MyBatis核心流程源码分析

1.mybatis基础开发流程

1.引入mybatis相关依赖

代码语言:xml复制
 <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

2.准备数据库文件t_user表以及数据

代码语言:sql复制
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`  (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES (1, 'demo1');
INSERT INTO `t_user` VALUES (2, 'demo2');
INSERT INTO `t_user` VALUES (3, 'demo3');
INSERT INTO `t_user` VALUES (4, 'demo4');
INSERT INTO `t_user` VALUES (5, 'demo5');

SET FOREIGN_KEY_CHECKS = 1;

3.创建实体类User以及UserDAO和对应的xml文件,以及mybats主配置文件

mybatis-config.xml

代码语言: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">
<!-- mybatis的主配置文件-->
<configuration>

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <typeAliases>
        <typeAlias type="com.example.entity.User" alias="User"></typeAlias>
        <typeAlias type="com.example.entity.Account" alias="Account"></typeAlias>
    </typeAliases>


    <!-- 配置环境-->
    <environments default="default">
        <!-- 配置mysql环境(可以配置多个环境完成多数据源场景,只需要在对应xml文件的sql标签中配置环境的id)-->
        <environment id="default">
            <!-- 配置事务环境-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池)-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserDAOMapper.xml"></mapper>
    </mappers>
</configuration>

实体类:

代码语言:java复制
package com.example.entity;

import java.io.Serializable;

public class User implements Serializable {
    private Integer id;
    private String name;

    @Override
    public String toString() {
        return "User{"  
                "id="   id  
                ", name='"   name   '''  
                '}';
    }

    public User() {
    }

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

UserDAO.java:

代码语言:java复制
package com.example.dao;

import com.example.entity.User;

import java.util.List;


public interface UserDAO {

    void save(User user);

    List<User> queryAllUsers();
}

UserDAOMapper.xml

代码语言:xml复制
<?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">
<mapper namespace="com.example.dao.UserDAO">
    
    <insert id="save" parameterType="User">
        insert into t_user (name) values (#{name})
    </insert>

    <select id="queryAllUsers" resultType="User" useCache="true" >
        select id,name from t_user
    </select>
</mapper>

4.编写mybatis测试类

代码语言:java复制
package com.example;

import com.example.dao.UserDAO;
import com.example.entity.User;
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 org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MybatisTest {

    /**
     * mybatis基础开发流程
     * @throws IOException
     */
    @Test
    public void test1() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
        List<User> users = userDAO.queryAllUsers();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

5.衍生出以前老版本ibatis中sqlsession的用法

代码语言:java复制
    /**
     * sqlsession的第二种用法
     * @throws IOException
     */
    @Test
    public void test2() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> users = sqlSession.selectList("queryAllUsers");
        for (User user : users) {
            System.out.println(user);
        }
    }

综上测试可知俩种方法都可以通过mybatis查询数据,但是通过 List users = sqlSession.selectList("queryAllUsers");这行代码并不能完全定位到具体执行的sql语句,因为不同的mapper.xml中是可以使用同名的方法(同一mapper.xml中方法名(id)必须唯一),所以需要在方法前加上所在类的全限定类名也就是如下代码

代码语言:java复制
 /**
     * sqlsession的第二种用法
     * @throws IOException
     */
    @Test
    public void test2() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> users = sqlSession.selectList("com.example.dao.UserDAO.queryAllUsers");
        for (User user : users) {
            System.out.println(user);
        }
    }

核心代码分析

代码语言:java复制
 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 SqlSession sqlSession = sqlSessionFactory.openSession();

第一种:List<User> users = sqlSession.selectList("queryAllUsers");

第二种:sqlSession.selectList("com.example.dao.UserDAO.queryAllUsers");

第一种的表达概念更清晰,能很明确知道是查询所有用户的操作(字符串表达比较模糊,体现出封装定义类型的重要性),第一种比较符合面向对象的概念
第一种本质上是对第二种的开发的封装(代理设计模式)

2.mybatis的核心对象分析

mybatis是JDBC的封装,通过SqlSession这个对象来对JDBC中Connection,Statement,ResultSet进行封装,当然mybatis的核心对象远不止这些.

1.数据存储类对象

代码语言:txt复制
1.数据存储类对象
	概念:在java中对mybatis相关配置信息进行封装
	分为:
	mybatis-config.xml  最终封装对象----> Configuration
	***DAOMapper.xml    最终封装对象----> MappedStatement
Configuration对象

通过mybatis中Configuration的源码来对应配置文件的相关标签如下图(代码只贴出分析相关的):

代码语言:java复制
public class Configuration {

    //对应mybatis-config中的environment标签
    //public final class Environment {
    //private final String id;
  	//private final TransactionFactory transactionFactory;
  	//private final DataSource dataSource;
  protected Environment environment;

  //下面这段属性对应的正是mybatis-config.xml中setting标签中的属性,我们比较熟悉的有cacheEnabled=true(开启二级缓存)
  protected boolean safeRowBoundsEnabled;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase;
  protected boolean aggressiveLazyLoading;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys;
  protected boolean useColumnLabel = true;
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls;
  protected boolean useActualParamName = true;
  protected boolean returnInstanceForEmptyRow;

  protected String logPrefix;
  protected Class <? extends Log> logImpl;
  protected Class <? extends VFS> vfsImpl;
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  protected Integer defaultStatementTimeout;
  protected Integer defaultFetchSize;
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
}
代码语言:java复制
//这几个maps中例如resultMap对应的是***DAOMapper.xml中的标签,而resultMaps则为存储所有的resultMap,他们Map的key为每个***DAOMapper.xml的namespace(也就是对应的命名空间也可以说对应DAO类的全限定类名),Mapper文件的相关内容也在Configuration中进行了汇总
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
代码语言:java复制
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
protected final InterceptorChain interceptorChain = new InterceptorChain();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
//对应mybatis-config.xml中typeAliases标签的内容
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
//对应mybatis-config.xml中mappers标签中的路径
protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

由此可以知道mybatis核心类封装了mybatis-config.xml和***DAOMapper.xml中的部分配置信息,那我们继续往下看

代码语言:java复制
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

  public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  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;
  //可以发现mybatis核心类不仅可以封装配置文件,还负责创建了mybatis其他核心对象
代码语言:txt复制
总结Configuration:
	1.封装了mybatis-config.xml
	2.封装了mapper文件 MappedStatement
	3.创建了mybatis其他核心对象
MappedStatement对象
代码语言:txt复制
MappedStatement对象
	对应的就是mapper文中一个一个的配置标签
	<select id		---> MappedStatement
	<insert id		---> MappedStatement
	
	注定了一个mapper文件中有多个MappedStatement对象

下面继续分析MappedStatement相关源码看看是如何封装的mapper文件中的标签

代码语言:java复制
public final class MappedStatement {

  private String resource;
  //可以得知MappedStatement也对应一个Configuration,他们的关系是对应关系,Configuration中可以找到所有的MappedStatement,而MappedStatement中也可以获取到对应的Configuration.
  private Configuration configuration;
  //这里的id不仅仅是对应mapper文件中id的方法名,而是namespace.id,这样才可以确保id的唯一性
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;

  MappedStatement() {
    // constructor disabled
  }

  public static class Builder {
    private MappedStatement mappedStatement = new MappedStatement();

    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
      mappedStatement.configuration = configuration;
      mappedStatement.id = id;
      mappedStatement.sqlSource = sqlSource;
      //从这里可以得知myabtis底层默认创建的statement类型为PreparedStatement,通过<select statementType="PREARED">的方式可以去自己指定类型
      mappedStatement.statementType = StatementType.PREPARED;
      mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>()).build();
      mappedStatement.resultMaps = new ArrayList<ResultMap>();
      mappedStatement.sqlCommandType = sqlCommandType;
      mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
      String logId = id;
      if (configuration.getLogPrefix() != null) {
        logId = configuration.getLogPrefix()   id;
      }
      mappedStatement.statementLog = LogFactory.getLog(logId);
      mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
    }

关于标签中sql语句的封装

在MappedStatement对象中也有java对象的体现如下代码

代码语言:java复制
//这个BoundSql对象就是我们sql语句的封装对象,里面一定包含一个String类型的sql语句 
public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }
BoundSql对象
代码语言:java复制
/**
 *    Copyright 2009-2017 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;

/**
 * An actual SQL String got from an {@link SqlSource} after having processed any dynamic content.
 * The SQL may have SQL placeholders "?" and an list (ordered) of an parameter mappings 
 * with the additional information for each parameter (at least the property name of the input object to read 
 * the value from). 
 * </br>
 * Can also have additional parameters that are created by the dynamic language (for loops, bind...).
 *
 * @author Clinton Begin
 */
public class BoundSql {
//这里便是我们写的sql语句
  private final String sql;
  private final List<ParameterMapping> parameterMappings;
  private final Object parameterObject;
  private final Map<String, Object> additionalParameters;
  private final MetaObject metaParameters;

  public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<String, Object>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
  }

  public String getSql() {
    return sql;
  }

  public List<ParameterMapping> getParameterMappings() {
    return parameterMappings;
  }

  public Object getParameterObject() {
    return parameterObject;
  }

  public boolean hasAdditionalParameter(String name) {
    String paramName = new PropertyTokenizer(name).getName();
    return additionalParameters.containsKey(paramName);
  }

  public void setAdditionalParameter(String name, Object value) {
    metaParameters.setValue(name, value);
  }

  public Object getAdditionalParameter(String name) {
    return metaParameters.getValue(name);
  }
}

BoundSql总结

MappedStatment中封装SQL语句的对象是BoundSql,里面不仅仅有sql语句还有sql语句对应的参数,类型...体现了Java面向对象的设计.

2.操作类对象

代码语言:txt复制
2.操作类对象
	Excutor
		Excutor 是mybatis中处理功能的核心
				增删改   查
	StatmentHandler
	ParmeterHandler
	TypeHandler
	...
Excutor对象

可以知道mybatis处理的核心主要是增删改查功能,带着这个想法进入Excutor的源码中查看

代码语言:java复制
/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor;

import java.sql.SQLException;
import java.util.List;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;

/**
 * @author Clinton Begin
 */
public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;
  //提供了修改操作这里修改指的是(增删改三个操作)
  int update(MappedStatement ms, Object parameter) throws SQLException;

   //提供了查询的相关操作可以发现上面的query方法对比下面多了俩个参数cacheKey,boundSql通过参数名称可以猜测为缓存相关查询
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

  List<BatchResult> flushStatements() throws SQLException;
  //还提供了事务相关的操作如提交和回滚
  void commit(boolean required) throws SQLException;

  void rollback(boolean required) throws SQLException;
  //也提供了缓存的相关操作
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);

  boolean isCached(MappedStatement ms, CacheKey key);

  void clearLocalCache();

  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);

  Transaction getTransaction();

  void close(boolean forceRollback);

  boolean isClosed();

  void setExecutorWrapper(Executor executor);

}
```txt

从上述源码得知,Executor是一个接口提供了增删改查的对应功能,还有事务和缓存的相关操作,大概已经完成了mybatis的全部功能,我们一般开发时也主要是这几个操作.那么Executor为什么会被定义成一个接口?在java语言的设计中所有的操作都会定义成接口,比如我们熟悉的service(业务操作),dao(数据库操作),这样可以方便架构进行拓展延深(哪怕这个接口只有一个实现类)例如后面看到的SqlSession也是接口.

下面看下Executor的几个实现类

常用的主要是三个分别为BatchExecutor,ReuseExecutor,SimpleExecutor,在这三个中最常用的为SimpleExecutor一个简单的执行器.

代码语言:txt复制
SimpleExecutor		最常用的 mybatis推荐的 默认实现类 通过Configuration中一段源码可以发现有一个defaultExecutorType的属性如果不进行配置则为 SimpleExecutor

 protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

ReuseExecutor		复用Statement(这里Statement是来执行sql语句的,也就是当sql语句不变的情况下(sql语句完全一样,参数也必须一样),还是使用之前的Statement对象,这样可以加快效率,这个时候会使用ReuseExecutor执行器)

BatchExecutor		JDBC中的批处理操作(由于数据库操作是使用操作系统资源所以可以使我们一次连接多次操作这个是数据库的批处理操作)
StatementHandler对象

StatementHandler是mybatis封装了JDBC statement,真正mybatis操作数据库的核心.为什么不直接使用Executor,还要封装StatementHandler对象(单一职责的设计),下面看看源码

代码语言:java复制
/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.statement;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.session.ResultHandler;

/**
 * @author Clinton Begin
 */
public interface StatementHandler {

  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

  void parameterize(Statement statement)
      throws SQLException;
  //批处理操作
  void batch(Statement statement)
      throws SQLException;
  //增删改操作
  int update(Statement statement)
      throws SQLException;
  //查询操作
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;
  //数据操作所需要的sql语句对象
  BoundSql getBoundSql();
  //也需要处理参数的对象
  ParameterHandler getParameterHandler();

}

StatementHandler也是一个接口里面封装了增删改查的方法,我们可以通过StatementHandler的实现类来看看具体提供了什么功能.

这里提供了3个实现类分别是存储过程,预编译,和最简单的SimpleStatementHandler.我们可以通过阅读SimpleStatementHandler的源码发现里面实现了查询的接口,而具体实现正是JDBC的常用操作如下代码:

代码语言:java复制
public class SimpleStatementHandler extends BaseStatementHandler {

  public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    int rows;
    if (keyGenerator instanceof Jdbc3KeyGenerator) {
      statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
      rows = statement.getUpdateCount();
      keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
    } else if (keyGenerator instanceof SelectKeyGenerator) {
      statement.execute(sql);
      rows = statement.getUpdateCount();
      keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
    } else {
      statement.execute(sql);
      rows = statement.getUpdateCount();
    }
    return rows;
  }

  @Override
  public void batch(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    statement.addBatch(sql);
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    //这里就是封装JDBC操作的具体体现
    statement.execute(sql);
    //最终返回了一个返回结果处理器
    return resultSetHandler.<E>handleResultSets(statement);
  }
ParameterHandler对象
代码语言:txt复制
ParameterHandler
	目的:mybatis参数 ---> JDBC相关参数
		mybatis注解 ---> @Parm - #{name}
ResultSetHandler对象
代码语言:txt复制
ResultSetHandler
	目的:对JDBC的返回结果集的封装

我们还是通过源码来了解这个结果集对象

代码语言:java复制
/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.resultset;

import org.apache.ibatis.cursor.Cursor;

import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @author Clinton Begin
 */
public interface ResultSetHandler {
  //在JDBC中必须要使用Statement才能获取ResultSet,所以必须传入Statement才能获取到
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

通过查看到这个接口的实现类DefaultResultSetHandler,可以看到源码中handleResultSets()方法的实现就是通过Statement去获取resultSet.

代码语言:java复制
 @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount  ;
    }
TypeHandler对象
代码语言:txt复制
TypeHandler
	主要是处理java程序在操作数据库中java类型和数据库类型的转换,这里我们大概了解后续在进行详细分析实现流程

0 人点赞