万字整理MyBatis源码

MyBatis差不多在我刚学编程就一直在用,一直没有去看它源码,这次,正好研究到了,把源码看了一遍,还是有不小的收获的,特意整理了下,如果有任何问题,欢迎指出

概述

MyBatis这个orm框架其实就是把JDBC给封装了一层,用过的朋友肯定知道,创建一个mybatis_config.xml配置文件,创建一个mapper接口,创建一个mapper.xml文件,然后在service层中调用了。暂且先不分析源码,如果假设自己开发这么个orm框架,功能完全喝MyBatis一样,那摆在自己面前的问题总共有如下3个

  • 怎么把配置封装起来(数据库链接地址,用户名,密码),达成只注册一次,后续就不需要管这个
  • 怎么绑定mapper接口和mapper.xml文件
  • 如何生成一个代理对象,让接口中的方法,找到对应的mapper语句,然后把参数带进去执行
    带着这几个问题,一步一步的比较好学习源码,当然,光凭这几个问题是无法完全开发出来的,这里我会尽可能带着讲一下,如果有些比较冷门的配置,可能就要自己去深入研究下了。

JDBC&原生MyBatis调用回顾

首先,MyBatis是对传统jdbc的一层封装,首先我们先来回顾一下传统的jdbc

JDBC

public class User {

    //user表的id
    private Integer id;

    //用户名
    private String username;
    
    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username == null ? null : username.trim();
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username ;
    }

}
public class JDBCDemo {
    //创建一个数据库的连接
    private static Connection getConnection() {
        Connection connection = null;
        try {
            //加载用户驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //连接数据库的地址
            String url = "jdbc:mysql://127.0.0.1:3306/test1";
            //数据库的用户名
            String user = "root";
            //数据库的密码
            String password = "12345678";
            //得到一个数据库的连接
            connection = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException e) {
            System.out.println(JDBCDemo.class.getName() + "数据库驱动包未找到!");
            return null;
        } catch (SQLException e) {
            System.out.println(JDBCDemo.class.getName() + "SQL语句有问题,无法查询成功!");
            return null;
        }
        return connection;//返回该连接
    }

    public User getUser(int id) {
        //得到该数据库的连接
        Connection connection = getConnection();
        //声明一个null的预处理的Statement
        PreparedStatement ps = null;
        //声明一个结果集,用来存放SQL的查询后的结果
        ResultSet rs = null;
        try {
            //对查询的User表的SQL进行预处理编译
            ps = connection.prepareStatement("select * from user where id=?");
            //把参数Id设值到数据的条件中
            ps.setInt(1, id);
            //执行查询语句。把结果返回到ResultSet结果集中
            rs = ps.executeQuery();
            //遍历从结果集中取数
            while (rs.next()) {
                //取出Statement的用户id
                int user_id = rs.getInt("id");
                //取出Statement的用户名
                String username = rs.getString("username");
                User user = new User();
                //存放在user对象中
                user.setId(user_id);
                user.setUsername(username);
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.close(rs, ps, connection);
        }
        return null;
    }

    /**
     * 判断数据库是否关闭
     * @param rs 查看结果集是滞关闭
     * @param stmt 预处理SQL是否关闭
     * @param conn 数据库连接是否关闭
     */
    private void close(ResultSet rs, Statement stmt, Connection conn) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            System.out.println(JDBCDemo.class.getName() + "ResultSet 关闭失败!");
        }
        try {
            if (stmt != null) {
                stmt.close();
            }
        } catch (SQLException e) {
            System.out.println(JDBCDemo.class.getName() + "Statement 关闭失败!");
        }
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            System.out.println(JDBCDemo.class.getName() + "Connection 关闭失败!");
        }
    }
    
    public static void main(String[] args) {
        //我们查询用户的id 为 1 用户
        User user = new JDBCDemo().getUser(1);
        //打印输出查询出来的数据
        System.out.println(user);

    }

}

这里就简单的介绍下3个主要的类,后面会对介绍如何封装的

  • DriverManager: 当调用方法getConnection时, DriverManager将尝试从初始化中加载的驱动程序中找到合适的驱动程序,并使用与当前小程序或应用程序相同的类加载器显式加载驱动程序。
  • Connection:与数据库的连接。 执行SQL语句并在连接的上下文中返回结果。
  • Statement:用于执行静态SQL语句并返回其生成的结果的对象。
  • ResultSet:表示数据库结果集

原生MyBatis调用

大致了解以后我们可以看下原生MyBatis写法
mybatis_config

<?xml version="1.0" encoding="UTF-8" ?>
<!--

       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.

-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <!-- autoMappingBehavior should be set in each test case -->

    <environments default="development">
        <environment id="development">
           <!--配置事务管理器-->
            <transactionManager type="JDBC" />
            <!--配置数据源类型,以及数据库链接想干信息-->
            <dataSource type="UNPOOLED">
                <property name="driver" value="org.hsqldb.jdbcDriver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="12345678"/>

            </dataSource>
        </environment>
    </environments>
    <!--mapper文件位置配置-->
    <mappers>
        <mapper resource="org/apache/ibatis/autoconstructor/AutoConstructorMapper.xml"/>
    </mappers>

</configuration>

mapper

public interface AutoConstructorMapper {
  PrimitiveSubject getSubject(final int id);

  @Select("SELECT * FROM subject")
  List<PrimitiveSubject> getSubjects();

  @Select("SELECT * FROM subject")
  List<AnnotatedSubject> getAnnotatedSubjects();

  @Select("SELECT * FROM subject")
  List<BadSubject> getBadSubjects();

  @Select("SELECT * FROM extensive_subject")
  List<ExtensiveSubject> getExtensiveSubjects();
}

xml

<?xml version="1.0" encoding="UTF-8"?>
<!--

       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.

-->
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.apache.ibatis.autoconstructor.AutoConstructorMapper">
  <select id="getSubject" resultType="org.apache.ibatis.autoconstructor.PrimitiveSubject">
    SELECT * FROM subject WHERE id = #{id}
  </select>
</mapper>

使用

private static SqlSessionFactory sqlSessionFactory;

@BeforeAll
static void setUp() throws Exception {
  // create a SqlSessionFactory
  try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }
}

@Test
void fullyPopulatedSubject() {
  try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
    final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
    final Object subject = mapper.getSubject(1);
    assertNotNull(subject);
  }
}

解析mybatis_config.xml

首先我们来到第一个问题,MyBatis是如何解析mybatis_config.xml

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      reader.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

MyBatis核心类Configuration就是通过XMLConfigBuilder::parse方法创建出来,mybatis把所有配置加载都放在了初始化的时候,而结果保存基本上都保存进了Configuration,它只在初始化的时候创建一个,它包含了mybatis-config.xml里面所有的配置,还包括了所有解析好的mapper,及其映射关系,再加载完成之后都是能够在这里面找到的,是MyBatis里面的核心类。成员变量如下,你可以根据名称看到不少mybatis_config的身影。(成员变量如下,可以先提前感受下mybatis源码没有几乎没注释的代码,顺带感受下我看源码的痛苦QAQ)

public class Configuration {
    protected Environment environment;
    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 boolean shrinkWhitespacesInSql;

    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected Class<? extends VFS> vfsImpl;
    protected Class<?> defaultSqlProviderType;
    protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
    protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
    protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
    protected Integer defaultStatementTimeout;
    protected Integer defaultFetchSize;
    protected ResultSetType defaultResultSetType;
    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
    protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
    protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

    protected Properties variables = new Properties();
    protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    protected ObjectFactory objectFactory = new DefaultObjectFactory();
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

    protected boolean lazyLoadingEnabled = false;
    protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

    protected String databaseId;
    /**
     * Configuration factory class.
     * Used to create Configuration for loading deserialized unread properties.
     *
     * @see <a href='https://github.com/mybatis/old-google-code-issues/issues/300'>Issue 300 (google code)</a>
     */
    protected Class<?> configurationFactory;

    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    protected final InterceptorChain interceptorChain = new InterceptorChain();
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
    //常用类的别名
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
        .conflictMessageProducer((savedValue, targetValue) ->
            ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
    protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
    protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
    protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
    protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
    protected final Set<String> loadedResources = new HashSet<>();
    protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

    protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
    protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
    protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
    protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();

    /*
     * A map holds cache-ref relationship. The key is the namespace that
     * references a cache bound to another namespace and the value is the
     * namespace which the actual cache is bound to.
     */
    protected final Map<String, String> cacheRefMap = new HashMap<>();
... 方法掠过
}

XPathParser用法

下面我们看一下XMLConfigBuilder的构造方法

public class XMLConfigBuilder extends BaseBuilder {
    private boolean parsed;//是否已经解析果mybatis-config.xml
    //解析mybatis-config.xml
    private final XPathParser parser;
    private String environment;
    //创建和缓存Reflctor对象
    private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
    public XMLConfigBuilder(Reader reader, String environment, Properties props) {
      this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
    }
}

它的内部会封装一个XPathParser对象,我们先来看一下它的用法

<employee id="${id_var}">
  <blah something="that"/>
  <first_name>Jim</first_name>
  <last_name>Smith</last_name>
  <birth_date>
    <year>1970</year>
    <month>6</month>
    <day>15</day>
  </birth_date>
  <height units="ft">5.8</height>
  <weight units="lbs">200</weight>
  <active bot="YES" score="3.2">true</active>
</employee>

  @Test
  void constructorWithInputStreamValidationVariablesEntityResolver() throws Exception {

    try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
      XPathParser parser = new XPathParser(inputStream, false, null, null);
      System.out.println(parser.evalLong("/employee/birth_date/year").equals(1970L));//true
      System.out.println(parser.evalNode("/employee/birth_date/year").getLongBody().equals(1970L));//true
      System.out.println(parser.evalNode("/employee").evalString("@id"));//${id_var}
      System.out.println(parser.evalNode("/employee/active").getDoubleAttribute("score"));//3.2    }
  }

XPathParser它封装了JDK原生的DocumentEntityResolverXPathProperties这几个对象,更方便的可以解析XML文件,具体用法我上面写了个例子,这样各个值都可以拿到了。

节点解析

初始化完XMLConfigBuilder后,就会调用它的parse()方法,解析xml,mapper解析,mapper绑定,都是在这个方法内完成的。下面我们来看下这个方法

/**
 * 解析mybatis-config.xml
 * 初始化调用
 * @return
 */
public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  //查找configuration节点解析
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

parser.evalNode("/configuration")这一步就是获取configuration节点,下一步就是在parseConfiguration方法中解析各个子节点
mybatis_config.xml

private void parseConfiguration(XNode root) {
  try {
    //解析各个节点
    // issue #117 read properties first
    propertiesElement(root.evalNode("properties"));
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    loadCustomLogImpl(settings);
    //类别名的注册
    typeAliasesElement(root.evalNode("typeAliases"));
    pluginElement(root.evalNode("plugins"));
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    //解析typeHandler
    typeHandlerElement(root.evalNode("typeHandlers"));
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

看到这里应该就很熟悉了,mybatis_config.xml各个节点放在+Element就是对应的解析方法。由于我们只配置了environmentsmappers我们就看看这两个方法。

解析Environment

private void environmentsElement(XNode context) throws Exception {
  if (context != null) {
    if (environment == null) {
      environment = context.getStringAttribute("default");
    }
    for (XNode child : context.getChildren()) {
      String id = child.getStringAttribute("id");
      if (isSpecifiedEnvironment(id)) {
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        //创建datasource,datasourceFactory,设置对应的属性值
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        DataSource dataSource = dsFactory.getDataSource();
        Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
        configuration.setEnvironment(environmentBuilder.build());
        break;
      }
    }
  }
}
Transaction

这里TransactionFactory负责创建Transaction,MyBatis中Transaction的子类有2个

image.png
Transaction定义了

public interface Transaction {

  /**
   * 获取对应的数据库连接对象
   */
  Connection getConnection() throws SQLException;

  /**
   * 提交事务
   */
  void commit() throws SQLException;

  /**
   * 回滚事务
   */
  void rollback() throws SQLException;

  /**
   * 关闭数据库连接
   */
  void close() throws SQLException;

  /**
   * 获取事务超时时间
   */
  Integer getTimeout() throws SQLException;

}

JdbcTransaction封装了事务隔离级别,连接,数据源这几个对象,方法基本上就是调用Connction对应方法

public class JdbcTransaction implements Transaction {

  private static final Log log = LogFactory.getLog(JdbcTransaction.class);

     //事务对应数据库连接
     protected Connection connection;
     //数据库连接所属datasource
     protected DataSource dataSource;
     //事务隔离级别
     protected TransactionIsolationLevel level;
     //是否自动提交
     protected boolean autoCommit;
     @Override
     public void rollback() throws SQLException {
       if (connection != null && !connection.getAutoCommit()) {
         if (log.isDebugEnabled()) {
           log.debug("Rolling back JDBC Connection [" + connection + "]");
         }
         connection.rollback();
       }
     }
     ..其他略
   }

还有一个是ManagedTransaction它把提交和回滚交给容器实现

public class ManagedTransaction implements Transaction {
       ...其他略
    //容器实现
    @Override
    public void commit() throws SQLException {
      // Does nothing
    }

    @Override
    public void rollback() throws SQLException {
      // Does nothing
    }

}
DataSource

创建完TransactionFactory下面就轮到DataSourceFactory,这个就没啥好说的了,Configuration的构造方法中会注册一堆类的别名,然后通过反射创建,这个没啥好说的。

public Configuration() {
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
。。。其他略
}

主要来说一下DataSource,也就是数据源,它其实就是对驱动的封装。我们来看下MyBatis是怎么进行封装的。
image.png
UnpooledDataSource核心方法如下,是不是很亲切,其实就是对传统JDBC的一种封装。和上面写的例子没啥太大的却别,主要区别是这里可以支持多个,进行了封装。

/**
 * 实现了getConnection()方法及其重载方法
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class UnpooledDataSource implements DataSource {

  //加载Driver类的类加载器
  private ClassLoader driverClassLoader;
  //数据库连接驱动的相关配置
  private Properties driverProperties;
  //缓存所有已注册的数据库连接驱动
  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();

  //数据库连接的驱动名称
  private String driver;
  //数据库连接的url
  private String url;
  //用户名
  private String username;
  //密码
  private String password;
  //是否自动提交
  private Boolean autoCommit;
  //事务隔离级别
  private Integer defaultTransactionIsolationLevel;
  private Integer defaultNetworkTimeout;

  static {
    //向DriverManager注册JDBC驱动
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
      Driver driver = drivers.nextElement();
      registeredDrivers.put(driver.getClass().getName(), driver);
    }
  }

  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return doGetConnection(username, password);
  }

  private Connection doGetConnection(String username, String password) throws SQLException {
    Properties props = new Properties();
    if (driverProperties != null) {
      props.putAll(driverProperties);
    }
    if (username != null) {
      props.setProperty("user", username);
    }
    if (password != null) {
      props.setProperty("password", password);
    }
    return doGetConnection(props);
  }

  //每次创建一个新的连接
  private Connection doGetConnection(Properties properties) throws SQLException {
    //初始化数据库驱动
    initializeDriver();
    //创建真正的数据库连接
    Connection connection = DriverManager.getConnection(url, properties);
    //配置数据库连接的autoCommit和隔离级别
    configureConnection(connection);
    return connection;
  }

  private synchronized void initializeDriver() throws SQLException {
    if (!registeredDrivers.containsKey(driver)) {//检测驱动是否已经注册
      Class<?> driverType;
      try {
        if (driverClassLoader != null) {
          driverType = Class.forName(driver, true, driverClassLoader);//注册驱动
        } else {
          driverType = Resources.classForName(driver);
        }
        // DriverManager requires the driver to be loaded via the system ClassLoader.
        // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
        Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();//创建Driver对象
        //注册驱动,DriverProxy是定义在UnpooledDataSource中的内部类,是Driver的静态代理类
        DriverManager.registerDriver(new DriverProxy(driverInstance));
        //将驱动添加到registeredDrivers
        registeredDrivers.put(driver, driverInstance);
      } catch (Exception e) {
        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
      }
    }
  }

看到这里应该整个线路就很清晰了,下面我们来看看对于传统

  private void configureConnection(Connection conn) throws SQLException {
    if (defaultNetworkTimeout != null) {
      conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
    }
    if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
      conn.setAutoCommit(autoCommit);
    }
    if (defaultTransactionIsolationLevel != null) {
      //设置事务隔离级别
      conn.setTransactionIsolation(defaultTransactionIsolationLevel);
    }
  }

  private static class DriverProxy implements Driver {
    private Driver driver;

    DriverProxy(Driver d) {
      this.driver = d;
    }

    。。。略
  }
  。。。略

}

你以为就讲到这里就完了,远远没有。老夫看代码的时候还看到了一个PooledDataSource,众所周知,每次用的时候创建一个Connection,用完销毁很浪费,最好能够服用,当然有更好的实现,例如Druid等等,Mybatis封装的这个我认为学习学习还是蛮不错的。这块感兴趣的可以了解下,不感兴趣的可以不看(可以直接去看Druid源码)。

先来看看PoolState,作者用2个list维护了Connection对象。

public class PoolState {

  //数据源对象
  protected PooledDataSource dataSource;
  //空闲状态连接集合
  protected final List<PooledConnection> idleConnections = new ArrayList<>();
  //活跃状态连接集合
  protected final List<PooledConnection> activeConnections = new ArrayList<>();
  protected long requestCount = 0;//请求数据库连接次数
  protected long accumulatedRequestTime = 0;//连接的累计时间
  protected long accumulatedCheckoutTime = 0;//连接累积的checkoutTime时长
  protected long claimedOverdueConnectionCount = 0;//超时连接个数
  protected long accumulatedCheckoutTimeOfOverdueConnections = 0;//累积超时时间
  protected long accumulatedWaitTime = 0;//累积等待时间
  protected long hadToWaitCount = 0;//等待次数
  protected long badConnectionCount = 0;//无效的连接数
}

下面我们再来看看MyBatis是如何实现的PooledDataSource,最核心的就是获取和放回Connection了,个人感觉线程池的逻辑差不多。

public class PooledDataSource implements DataSource {
protected void pushConnection(PooledConnection conn) throws SQLException {

  synchronized (state) {
    state.activeConnections.remove(conn);//
    if (conn.isValid()) {//连接是否有效
      //检测空闲连接数是否已达上线
      if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
        state.accumulatedCheckoutTime += conn.getCheckoutTime();//累积checkout时长
        if (!conn.getRealConnection().getAutoCommit()) {//回滚未提交事务
          conn.getRealConnection().rollback();
        }
        //创建PooledConnection
        //实际消除的就是代理对象,实际用的就是realConnection
        PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
        state.idleConnections.add(newConn);//添加进不活跃集合
        newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
        newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
        conn.invalidate();//将原 PooledConnection 对象设置为无效
        if (log.isDebugEnabled()) {
          log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
        }
        state.notifyAll();
      } else {
        //空闲集合已经满了,直接关闭
        state.accumulatedCheckoutTime += conn.getCheckoutTime();
        if (!conn.getRealConnection().getAutoCommit()) {
          conn.getRealConnection().rollback();
        }
        conn.getRealConnection().close();
        if (log.isDebugEnabled()) {
          log.debug("Closed connection " + conn.getRealHashCode() + ".");
        }
        conn.invalidate();
      }
    } else {
      if (log.isDebugEnabled()) {
        log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
      }
      state.badConnectionCount++;
    }
  }
}

private PooledConnection popConnection(String username, String password) throws SQLException {
  boolean countedWait = false;
  PooledConnection conn = null;
  long t = System.currentTimeMillis();
  int localBadConnectionCount = 0;

  //没有获取到连接对象就自旋
  while (conn == null) {
    synchronized (state) {
      /**
       * 有无空闲连接的处理
       */
      //idleConnections空闲状态连接
      if (!state.idleConnections.isEmpty()) {//空闲连接
        // Pool has available connection
        conn = state.idleConnections.remove(0);//获取连接
        if (log.isDebugEnabled()) {
          log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
        }
      } else {
        // Pool does not have available connection 活跃连接数没有到最大值,则可以创建新连接
        if (state.activeConnections.size() < poolMaximumActiveConnections) {
          // Can create new connection 创建新数据库连接,并封装成 PooledConnection 对象
          conn = new PooledConnection(dataSource.getConnection(), this);
          if (log.isDebugEnabled()) {
            log.debug("Created connection " + conn.getRealHashCode() + ".");
          }
        } else {//活跃连接数已到最大值,则不能创建新连接
          // Cannot create new connection 获取最先创建的活跃连接
          PooledConnection oldestActiveConnection = state.activeConnections.get(0);
          long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
          if (longestCheckoutTime > poolMaximumCheckoutTime) {//检测连接是否超时
            // Can claim overdue connection 对超时连接的信息进行统计
            state.claimedOverdueConnectionCount++;
            state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
            state.accumulatedCheckoutTime += longestCheckoutTime;
            //将超时连接移出 activeConnections 集合
            state.activeConnections.remove(oldestActiveConnection);
            //如采超时连接未提交,则自动回滚
            if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
              try {
                oldestActiveConnection.getRealConnection().rollback();
              } catch (SQLException e) {
                /*
                   Just log a message for debug and continue to execute the following
                   statement like nothing happened.
                   Wrap the bad connection with a new PooledConnection, this will help
                   to not interrupt current executing thread and give current thread a
                   chance to join the next competition for another valid/good database
                   connection. At the end of this loop, bad {@link @conn} will be set as null.
                 */
                log.debug("Bad connection. Could not roll back");
              }
            }
            //创建新 PooledConnection 对象,复用老的Collection对象
            conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
            //复用时间戳
            conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
            conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
            //超时PooledConnection设置成无效
            oldestActiveConnection.invalidate();
            if (log.isDebugEnabled()) {
              log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
            }
          } else {
            // 无空闲连接、无法创建新连接且无超时连接,则只能阻塞等待
            try {
              if (!countedWait) {
                state.hadToWaitCount++;//统计等待次数
                countedWait = true;
              }
              if (log.isDebugEnabled()) {
                log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
              }
              long wt = System.currentTimeMillis();
              state.wait(poolTimeToWait);
              state.accumulatedWaitTime += System.currentTimeMillis() - wt;
            } catch (InterruptedException e) {
              break;
            }
          }
        }
      }
      if (conn != null) {
        // ping to server and check the connection is valid or not
        if (conn.isValid()) {//检测连接有效
          //
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
          conn.setCheckoutTimestamp(System.currentTimeMillis());
          conn.setLastUsedTimestamp(System.currentTimeMillis());
          state.activeConnections.add(conn);//统计
          state.requestCount++;
          state.accumulatedRequestTime += System.currentTimeMillis() - t;
        } else {
          //当前连接无效,继续自选
          if (log.isDebugEnabled()) {
            log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
          }
          state.badConnectionCount++;
          localBadConnectionCount++;
          conn = null;
          if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
            if (log.isDebugEnabled()) {
              log.debug("PooledDataSource: Could not get a good connection to the database.");
            }
            throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
          }
        }
      }
    }

  }

  if (conn == null) {
    if (log.isDebugEnabled()) {
      log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }
    throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
  }

  return conn;
}
。。。略
}

下面就是把TransactionFactoryDataSource封装进了Environment,再把environment塞进了Configuration其中用到了建造者模式,如果对建造者模式感兴趣可以看我这边博客https://juejin.cn/post/6985949223413874719

解析Mapper

image.png
mapper配置大约有四种,知道这个,来看源码就很好理解了内容都大同小异,我们这里来看下resource的解析

public void parse() {
  //判断是否已经加载过该映射文件
  if (!configuration.isResourceLoaded(resource)) {
    //通过xml绑定sql
    configurationElement(parser.evalNode("/mapper"));//处理mapper节点
    //解析过的xml添加进loadedResources
    configuration.addLoadedResource(resource);
    //扫描注解绑定sql
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      //package单独处理
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        //解析对应配置
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          }
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          try(InputStream inputStream = Resources.getUrlAsStream(url)){
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          }
        } else if (resource == null && url == null && mapperClass != null) {
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

由于mybatis_config指定了mapper的路径,会加载指定资源,mapperParser.parse();最终会调用如下方法解析select标签,把解析好的结果添加configuration::mappedStatements

public void parseStatementNode() {
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }

  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

  // 先处理include节点
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  String parameterType = context.getStringAttribute("parameterType");
  Class<?> parameterTypeClass = resolveClass(parameterType);

  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);

  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    //根据全局中的useGeneratedKeys配置
    //是否为insert语句,决定是否使用KeyGenerator接口实现
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }
  //解析出原始sql语句,把#{}替换成?,把参数
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  //获取select标签中resultType的值
  String resultType = context.getStringAttribute("resultType");
  //typeAliasRegistry中没有对应的,就反射获取对应的class对象
  Class<?> resultTypeClass = resolveClass(resultType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultSetType = context.getStringAttribute("resultSetType");
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  if (resultSetTypeEnum == null) {
    resultSetTypeEnum = configuration.getDefaultResultSetType();
  }
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  String resultSets = context.getStringAttribute("resultSets");
  //生成mappedStatements并添加进configuration的mappedStatements
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

相信大家也看到注解版的了它是在
注解的是通过MapperRegistry::addMapper具体流程和上述的流程类似,也是解析上面方法中的各个值通过MapperBuilderAssistant添加进configuration
因为所有解析的结果都塞进了Configuration里面,最后,把configuration塞进DefaultSqlSessionFactory
解析部分算是完成了

获取SqlSession

下面就是获取SqlSession了,Mybatis默认是给你DefaultSqlSession

//通过数据源获取数据库连接
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    //获取environment中的transactionFactory没有就创建一个
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    //具体执行sql语句
    final Executor executor = configuration.newExecutor(tx, execType);
    //创建sqlsession
    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();
  }
}

在你获取mapper的时候,这里就是通过动态代理,给你生成了一个代理对象final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);源码如下

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


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


//Mapper接口代理对象
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);
  }
}

public T newInstance(SqlSession sqlSession) {
  //创建代理对象
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}


@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

mapper方法执行

在执行的时候我们只要看代理是如何执行的就行了也就是MapperProxy它内部封装了一个PlainMethodInvoker,最终执行也是调用这个内部类的invoke方法

private static class PlainMethodInvoker implements MapperMethodInvoker {
      private final MapperMethod mapperMethod;

      public PlainMethodInvoker(MapperMethod mapperMethod) {
        super();
        this.mapperMethod = mapperMethod;
      }

      @Override
      public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
        //
        return mapperMethod.execute(sqlSession, args);
      }
    }
    

在执行的时候,会用到前面已经解析好的参数并构造一个MapperMethod


    public class MapperMethod {

    //具体的执行
    public Object execute(SqlSession sqlSession, Object[] args) {
      Object result;
      switch (command.getType()) {//根据sql类型调用不同方法
        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()) {
            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);
            //调用sqlSession执行
            result = sqlSession.selectOne(command.getName(), param);
            if (method.returnsOptional()
                && (result == null || !method.getReturnType().equals(result.getClass()))) {
              result = Optional.ofNullable(result);
            }
          }
          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;
    }

}
@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;
  }
}

//最终调用这一个
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  try {
    //前面解析mybatis_config.xml的时候就已经加载进configuration了
    MappedStatement ms = configuration.getMappedStatement(statement);
    //
    return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

最终调用CachingExecutor,还记得之前sql语句封装进SqlSource么下面的ms.getBoundSql(parameterObject);最终会调用sqlSource.getBoundSql(parameterObject);创建一个新的BoundSql,这下sql,参数都有了

image.png

//
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

接下来就执行查询操作了,最终会调用BaseExecutor来执行查询操作

@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
  }
  List<E> list;
  try {
    queryStack++;
    //查询一级缓存
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    if (list != null) {
      //针对存储过程的处理
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    //查询完成
    queryStack--;
  }
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {
      deferredLoad.load();
    }
    // issue #601
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  return list;
}

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  //缓存中添加占位符
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
    //调用doQuery
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
    //删除占位符
    localCache.removeObject(key);
  }
  localCache.putObject(key, list);
  if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
  }
  return list;
}


@Override
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();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}
//获取Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}

//获取Connection
protected Connection getConnection(Log statementLog) throws SQLException {
  Connection connection = transaction.getConnection();
  if (statementLog.isDebugEnabled()) {
    return ConnectionLogger.newInstance(connection, statementLog, queryStack);
  } else {
    return connection;
  }
}

//通过BaseStatementHandler获取
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  ErrorContext.instance().sql(boundSql.getSql());
  Statement statement = null;
  try {
    statement = instantiateStatement(connection);
    setStatementTimeout(statement, transactionTimeout);
    setFetchSize(statement);
    return statement;
  } catch (SQLException e) {
    closeStatement(statement);
    throw e;
  } catch (Exception e) {
    closeStatement(statement);
    throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
  }
}

//创建PreparedStatement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
  String sql = boundSql.getSql();
  if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
    String[] keyColumnNames = mappedStatement.getKeyColumns();
    if (keyColumnNames == null) {
      return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
    } else {
      return connection.prepareStatement(sql, keyColumnNames);
    }
  } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
    return connection.prepareStatement(sql);
  } else {
    return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  }
}


//PreparedStatementHandler执行查询,这个就应该很亲切了
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  return resultSetHandler.handleResultSets(ps);
}

至此整个原生MyBatis的查询流程就结束了,这里值介绍了查询的流程,增删改的流程也是差不多的,这里就不多赘述了。

参考资料–《MyBatis技术内幕》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值