那位先生

个人站

前世五百次回眸,才能换得今生的一次擦肩而过。


mybatis源码

1.jdbc查询mysql

public static void main(String[] args)throws ClassNotFoundException, SQLException {
        String URL = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8";
        String USER = "root";
        String PASSWORD = "";
        //1.加载驱动程序
        Class.forName("com.mysql.jdbc.Driver");
        //2.获得数据库链接
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        //3.通过数据库的连接操作数据库,实现增删改查(使用Statement类)
        Statement st = conn.createStatement();
        ResultSet rs = st.executeQuery("select * from t_student");
        //4.处理数据库的返回结果(使用ResultSet类)
        while (rs.next()) {
            System.out.println(rs.getString("name"));
        }

        //关闭资源
        rs.close();
        st.close();
        conn.close();
    }
    

2.mybatis查询MySQL

测试类:

package com.xie;

import com.xie.dao.StudentDao;
import com.xie.model.Student;
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.Assert;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

/**
 * @program: mybatistheory
 * @description: test
 * @author: xiewucheng
 * @create: 2020-09-09 18:10
 **/
public class UserTest {

    @Test
    public void findUserById() {
        SqlSession sqlSession = getSessionFactory().openSession();
        Student student1 = sqlSession.selectOne("com.xie.dao.StudentDao.getStudent","2");
        System.out.println(student1.getName());
        Assert.assertNotNull("没找到数据", student1);
    }

    //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与数据库进行交互
    private static SqlSessionFactory getSessionFactory() {
        SqlSessionFactory sessionFactory = null;
        String resource = "configuration.xml";
        try {
            InputStream inputStream = Resources
                    .getResourceAsStream(resource);
            sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sessionFactory;
    }
}

StudentDao

package com.xie.dao;

import com.xie.model.Student;

import java.util.List;

public interface StudentDao {

    Student getStudent(String id);

    List<Student> listStudent(Student student);
}

configuration.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>

    <!-- 指定Mybatis使用log4j -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
               <!-- 如果上面没有指定数据库配置的properties文件,那么此处可以这样直接配置-->
              <property name="driver" value="com.mysql.jdbc.Driver"/>
              <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
              <property name="username" value="root"/>
              <property name="password" value=""/>

            </dataSource>
        </environment>
    </environments>

    <!-- 映射文件-->
    <mappers>
        <mapper resource="com/xie/dao/student-mapper.xml"/>
    </mappers>

</configuration>

student-mapper.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.xie.dao.StudentDao">

    <select id="getStudent" resultType="com.xie.model.Student" parameterType="java.lang.String">
      select * from t_student where id = #{id}
   </select>

</mapper>

3.mybatis获取MySQL的配置信息和mapper信息

读取”configuration.xml”,获取url,username,password,driver。

InputStream inputStream = Resources.getResourceAsStream(resource);
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
--->
SqlSessionFactoryBuilder:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties):
return return build(parser.parse());
-->
XMLConfigBuilder:
Configuration parse():parseConfiguration(parser.evalNode("/configuration"));

parseConfiguration(XNode root)

 private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      //别名
      typeAliasesElement(root.evalNode("typeAliases"));
      //第三方插件
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

propertiesElement(root.evalNode(“properties”));

//解析property,如单独放在另一个文件,然后引入configuration的xml,比如MySQL的配置信息

environmentsElement(root.evalNode(“environments”));

获取数据源的配置:
  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"));
          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());
        }
      }
    }
  }

mapperElement(root.evalNode(“mappers”));

获取mapper配置信息(4种方式:package,resource,url,class)
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        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);
            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);
            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.");
          }
        }
        
----------------------------------
        mapperParser.parse();
----------------------------------        
          public void parse() {
            if (!configuration.isResourceLoaded(resource)) {
              //这里
              configurationElement(parser.evalNode("/mapper"));
              configuration.addLoadedResource(resource);
              bindMapperForNamespace();
            }
        
            parsePendingResultMaps();
            parsePendingChacheRefs();
            parsePendingStatements();
          }
      }
    }
  }
  
  ----------------------------------
   configurationElement(parser.evalNode("/mapper"));
   解析mapper文件
  ----------------------------------  
    private void configurationElement(XNode context) {
      try {
        String namespace = context.getStringAttribute("namespace");
        if (namespace.equals("")) {
      	  throw new BuilderException("Mapper's namespace cannot be empty");
        }
        builderAssistant.setCurrentNamespace(namespace);
        cacheRefElement(context.evalNode("cache-ref"));
        cacheElement(context.evalNode("cache"));
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        sqlElement(context.evalNodes("/mapper/sql"));
        //解析查询语句
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
      } catch (Exception e) {
        throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
      }
    }
  ----------------------------------
   buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
   解析查询语句
  ----------------------------------  
    public void parseStatementNode() {
      String id = context.getStringAttribute("id");
      String databaseId = context.getStringAttribute("databaseId");
  
      if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
  
      Integer fetchSize = context.getIntAttribute("fetchSize");
      Integer timeout = context.getIntAttribute("timeout");
      //null
      String parameterMap = context.getStringAttribute("parameterMap");
      //参数类型 java.lang.String
      String parameterType = context.getStringAttribute("parameterType");
      Class<?> parameterTypeClass = resolveClass(parameterType);
      //null
      String resultMap = context.getStringAttribute("resultMap");
      //结果类型 com.xie.model.Student
      String resultType = context.getStringAttribute("resultType");
      String lang = context.getStringAttribute("lang");
      LanguageDriver langDriver = getLanguageDriver(lang);
  
      Class<?> resultTypeClass = resolveClass(resultType);
      String resultSetType = context.getStringAttribute("resultSetType");
      StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
      ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
      
      //查询类型select
      String nodeName = context.getNode().getNodeName();
      //枚举类型:UNKNOWN, INSERT, UPDATE, DELETE, SELECT; ---》select
      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 Fragments before parsing
      XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
      includeParser.applyIncludes(context.getNode());
  
      // Parse selectKey after includes and remove them.
      processSelectKeyNodes(id, parameterTypeClass, langDriver);
      
      // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
      //sql:select * from t_student where id = ?,property:id,还有datasource信息
      SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
      String resultSets = context.getStringAttribute("resultSets");
      String keyProperty = context.getStringAttribute("keyProperty");
      String keyColumn = context.getStringAttribute("keyColumn");
      KeyGenerator keyGenerator;
      //getStudent!selectKey
      String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
      //com.xie.dao.StudentDao.getStudent!selectKey
      keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
      if (configuration.hasKeyGenerator(keyStatementId)) {
        keyGenerator = configuration.getKeyGenerator(keyStatementId);
      } else {
        keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
            configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
            ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
      }
      
      //加入到map
      builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
          fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
          resultSetTypeEnum, flushCache, useCache, resultOrdered, 
          keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }

4.开启会话

SqlSession sqlSession = getSessionFactory().openSession(); //设置executor,tx

//设置executor
public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
configuration.getDefaultExecutorType();(默认:ExecutorType.SIMPLE;)  
executor = new SimpleExecutor(this, transaction);

//创建tx:
  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommmit = desiredAutoCommit;
  }

事务级别:TransactionIsolationLevel

public enum TransactionIsolationLevel {
  NONE(Connection.TRANSACTION_NONE),
  READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
  READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
  REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
  SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);

  private final int level;

  private TransactionIsolationLevel(int level) {
    this.level = level;
  }

  public int getLevel() {
    return level;
  }
}
  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

5.SQL执行

Student student1 = sqlSession.selectOne("com.xie.dao.StudentDao.getStudent","2");

selectOne

  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    //statement:com.xie.dao.StudentDao.getStudent
    //parameter:"2"
    List<T> list = this.<T>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;
    }
  }

selectList

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      return result;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

query

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

query

  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();
      }
      deferredLoads.clear(); // issue #601
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue #482
      }
    }
    return list;
  }

queryFromDatabase—doQuery

  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.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

获取连接:prepareStatement

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }

执行SQL查询:query

 public <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.<E>handleResultSets(statement);
  }

处理查询结果:handleResultSets

  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    // MySQL的字段名columnNames,返回结果的所有字段类型:classNames
    //MySQL字段的属性类型 jdbcTypes,查询到的字段信息 fields
    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++;
    }

    String[] resultSets = mappedStatement.getResulSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

//处理数据库的返回结果(使用ResultSet类)

  private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
      // move forward to get the first resultset in case the driver
      // doesn't return the resultset as the first result (HSQLDB 2.1)
      if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          // no more results. Must be no resultset
          break;
        }
      }
    }
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
  }

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
你说多少就多少

比五毛钱特效专业哦