Mybatis源码解析一(SqlSessionFactory和SqlSession的获取)

Mybatis源码解析一(SqlSessionFactory和SqlSession的获取)一、SqlSessionFactorySqlSessionFactory是MyBatis的关键对象,它是个单个数据库映射关系经过编译后的内存镜像;SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象类获得;SqlSessionFactoryBuilder从XML配置文件或一个预先定制的Configuration的实例构建出SqlS…

大家好,又见面了,我是你们的朋友全栈君。

一、SqlSessionFactory

SqlSessionFactory是MyBatis的关键对象,  它是个单个数据库映射关系经过编译后的内存镜像;  SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象类获得;  SqlSessionFactoryBuilder从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例;  每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心, 同时SqlSessionFactory也是线程安全的, SqlSessionFactory一旦被创建, 应该在应用执行期间都存在; 在应用运行期间不要重复创建多次, 建议使用单例模式SqlSessionFactory是创建SqlSession的工厂; 
 

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>

    <properties>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true"/>
        <property name="username" value="root"/>
        <property name="password" value="m123"/>
    </properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="m123"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com\lic\ibatis\mapper\UserMapper.xml"/>
        <mapper class="com.lic.ibatis.mapper.UserMapperAnn"></mapper>
    </mappers>
</configuration>

UserMapper.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.lic.ibatis.dao.UserMapper">

    <select id="getUserById" parameterType="int" resultType="com.lic.ibatis.entity.User">
        select * from user where id = #{id}
    </select>
</mapper>

测试类:

package com.lic.ibatis.test;

import com.lic.ibatis.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 java.io.IOException;
import java.io.Reader;

public class MybatisHelloWorld {
  public static void main(String[] args) {
    try {
      Reader reader = Resources.getResourceAsReader("Configuration.xml");
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
      SqlSession session = sqlSessionFactory.openSession();
      try {
        User user = (User) session.selectOne("com.lic.ibatis.dao.UserMapper.getUserById", 1);
        System.out.println(user.toString());

        User user2 = (User) session.selectOne("com.lic.ibatis.dao.UserMapper.getUserById", 1);
        System.out.println("第二次查询"+user2.toString());
      } finally {
        session.close();
      }

    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

分析:

  1. 对Configuration.xml配置文件进行解析, 生成SqlSessionFactory
  2. 通过SqlSessionFactory获取一个SqlSession实例
  3. 使用selectOne()方法进行查询

 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); 

 SqlSessionFactoryBuilder#build(java.io.Reader)方法实现:

 public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      /**
       * 创建XML配置解析器
       */
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      /**
       * 1. parser.parse(): 解析配置文件,创建配置类Configuration
       * 2. build(): 创建SqlSessionFactory对象,并返回
       */
      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.
      }
    }
  }

分析: 

  1. 创建XML配置解析器
  2. 解析配置文件
  3. 创建DefaultSqlSessionFactory对象

1. 创建XML配置解析器

XMLConfigBuilder()方法实现:

public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    /**
     * new XPathParser(reader, true, props, new XMLMapperEntityResolver())的参数含义: Reader,是否进行DTD 校验,属性配置,XML实体节点解析器
     */
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }


public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
    //参数配置
    commonConstructor(validation, variables, entityResolver);
    //将配置信息输入流封装为Document对象, 以便后面进行解析
    this.document = createDocument(new InputSource(reader));
  }

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

2. 解析配置文件

XMLConfigBuilder#parse()方法实现:

 public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    /**
     * mybatis配置文件解析的主流程:
     * 1. parser.evalNode("/configuration") --> 获取到根节点
     * 2. 根据根标签<configuration>开始解析
     */
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

XMLConfigBuilder#parseConfiguration()方法实现:

private void parseConfiguration(XNode root) {
    //解析配置文件中根标签下的所有子标签
    try {
      //issue #117 read properties first
      /**
       * 1. 解析properties节点
       */
      propertiesElement(root.evalNode("properties"));
      /**
       * 2. 解析settings节点
       */
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      /**
       * 3. VFS主要用来加载容器内的各种资源,比如jar或者class文件
       */
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      /**
       * 4. 解析类型别名typeAliasesElement
       */
      typeAliasesElement(root.evalNode("typeAliases"));
      /**
       * 5. 加载插件pluginElement
       *
       *  比如: 分页插件PageHelper,再比如druid连接池提供的各种监控、拦截、预发检查功能,
       *  在使用其它连接池比如dbcp的时候,在不修改连接池源码的情况下,就可以借助mybatis的插件体系实现
       */
      pluginElement(root.evalNode("plugins"));

      /**
       * 6. 加载对象工厂objectFactoryElement
       *
       * MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
       * 默认的对象工厂DefaultObjectFactory做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。
       */
      objectFactoryElement(root.evalNode("objectFactory"));
      /**
       * 7. 创建对象包装器工厂objectWrapperFactoryElement
       *
       * 对象包装器工厂主要用来包装返回result对象,比如说可以用来设置某些敏感字段脱敏或者加密等。
       * 默认对象包装器工厂是DefaultObjectWrapperFactory,也就是不使用包装器工厂。
       */
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      /**
       * 8. 加载反射工厂reflectorFactoryElement
       *
       * 因为加载配置文件中的各种插件类等等,为了提供更好的灵活性,
       * mybatis支持用户自定义反射工厂,不过总体来说,用的不多,
       * 要实现反射工厂,只要实现ReflectorFactory接口即可。默认的反射工厂是DefaultReflectorFactory。
       * 一般来说,使用默认的反射工厂就可以了。
       */
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      /**
       * 得到setting之后,调用settingsElement(Properties props)将各值赋值给configuration,
       * 同时在这里有重新设置了默认值,所有这一点很重要,configuration中的默认值不一定是真正的默认值。
       */
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      /**
       * 9. 加载环境配置environmentsElement
       *
       * 环境可以说是mybatis-config配置文件中最重要的部分,
       * 它类似于spring和maven里面的profile,允许给开发、
       * 生产环境同时配置不同的environment,根据不同的环境加载不同的配置,
       * 这也是常见的做法,如果在SqlSessionFactoryBuilder调用期间没有传递使用哪个环境的话,
       * 默认会使用一个名为default”的环境。找到对应的environment之后,就可以加载事务管理器和数据源了。
       * 事务管理器和数据源类型这里都用到了类型别名,JDBC/POOLED都是在mybatis内置提供的,
       * 在Configuration构造器执行期间注册到TypeAliasRegister。
       *
       *  mybatis内置提供JDBC和MANAGED两种事务管理方式,前者主要用于简单JDBC模式,
       * 后者主要用于容器管理事务,一般使用JDBC事务管理方式。
       * mybatis内置提供JNDI、POOLED、UNPOOLED三种数据源工厂,一般情况下使用POOLED数据源。
       */
      environmentsElement(root.evalNode("environments"));
      /**
       * 10. 数据库厂商标识加载databaseIdProviderElement(了解即可)
       */
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      /**
       * 11. 加载类型处理器typeHandlerElement
       *
       * 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,
       * 还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
       * mybatis提供了两种方式注册类型处理器,package自动检索方式和显示定义方式。
       * 使用自动检索(autodiscovery)功能的时候,只能通过注解方式来指定 JDBC 的类型。
       */
      typeHandlerElement(root.evalNode("typeHandlers"));

      /**
       * 12. 加载mapper文件 或 mapperElement  --->  (重点)
       */
      mapperElement(root.evalNode("mappers"));

    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

分析: 对配置文件中各个节点进行解析, 将解析结果封装到Configuration类中; 这里主要看对<mappers>标签的解析

XMLConfigBuilder#mapperElement()方法实现:

 /**
   * <mappers>
   *   <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> -----> 引入类路径(编译后以classes为跟的路径)下的资源
   *   <mapper url="file:///var/mappers/BlogMapper.xml"/>        -----> 入网络或磁盘路径下的资源
   *   <mapper class="org.mybatis.builder.BlogMapper"/>          -----> 引用(注册)接口: 1.有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;2.没有sql映射文件,所有的sql都是基于注解写在接口上。
   *   <package name="org.mybatis.builder"/>                     -----> 扫描包下所有的引用接口
   * </mappers>
   * @param parent
   * @throws Exception
   */
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        /**
         *   如果要同时使用package自动扫描和通过mapper明确指定要加载的mapper,
         *   一定要确保package自动扫描的范围不包含明确指定的mapper,否则在通过
         *   package扫描的interface的时候,尝试加载对应xml文件的loadXmlResource()
         *   的逻辑中出现判重出错,报org.apache.ibatis.binding.BindingException异常,
         *   即使xml文件中包含的内容和mapper接口中包含的语句不重复也会出错,
         *   包括加载mapper接口时自动加载的xml mapper也一样会出错。
         */
        //如果配置包扫描
        if ("package".equals(child.getName())) {
          //获取需要扫描的包路径
          String mapperPackage = child.getStringAttribute("name");
          //解析包信息, 注册该包下的Mappers
          configuration.addMappers(mapperPackage);
        } else {
          //获取resource,url,mapperClass属性的值
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");

          //resource属性解析
          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) {
            //url属性解析
            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) {
            //mapperClass属性解析
            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.");
          }
        }
      }
    }
  }

 分析:  根据<mappers> 中配置资源的方式的不同采用不同的解析方案, 这里主要看resource方式的解析

XMLMapperBuilder#parse()方法实现:

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      /**
       * 解析mapper.xml中的<mapper></mapper>标签
       */
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      /**
       * 根据接口创建MapperProxyFactory工厂
       */
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

分析

  1.  解析mapper.xml中的<mapper></mapper>标签
  2. 根据接口创建MapperProxyFactory工厂, 用于创建mapper接口的代理对象

XMLMapperBuilder#configurationElement()方法实现:

private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //设置名称空间
      builderAssistant.setCurrentNamespace(namespace);
      //开始对mapper.xml中各个标签进行解析
      /**
       * 1. 解析缓存映射<cache-ref></cache-ref>
       */
      cacheRefElement(context.evalNode("cache-ref"));
      /**
       * 2. 解析缓存<cache></cache>
       */
      cacheElement(context.evalNode("cache"));
      /**
       * 3. 解析参数映射<parameterMap></parameterMap>
       */
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      /**
       * 4. 解析结果集映射<resultMap></resultMap>
       */
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      /**
       * 5. 解析<sql></sql>
       */
      sqlElement(context.evalNodes("/mapper/sql"));
      /**
       * 6. 解析CRUD语句<select></select> |<insert></insert> |<update></update> |<delete></delete>  (重点)
       */
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

分析: 对<mapper>的各种子标签进行解析, 主要看CRUD语句标签<select></select> |<insert></insert> |<update></update> |<delete></delete> 的解析

XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)方法实现:

 private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    //遍历解析每条sql语句
    for (XNode context : list) {
      //用每个sql标签的上下文对象创建statementParser解析器
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        /**
         * 解析SQL节点
         */
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

分析: 创建statementParser解析器, 调用parseStatementNode()对标签进行解析

XMLStatementBuilder#parseStatementNode()方法实现: 

 public void parseStatementNode() {
    /**
     *  context:
     *  <select id="getUserById" parameterType="int" resultType="com.lic.ibatis.entity.User">
     *       select * from user where id = #{id}
     *   </select>
     */
    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 Fragments before parsing
    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 {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }

    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");
    String resultType = context.getStringAttribute("resultType");
    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");

    /**
     * 将解析内容封装到MappedStatement中
     */
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

分析:

  1. 对之前解析好的各种属性进行解析配置
  2. 将所有的sql配置进行封装

MapperBuilderAssistant#addMappedStatement(. . . )方法实现:

 public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) {
      throw new IncompleteElementException("Cache-ref not yet resolved");
    }
    //接口路径 + 方法名称
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);  //二级缓存配置

    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }

    MappedStatement statement = statementBuilder.build();
   /**
     * 将解析好的statement实例加入mappedStatements集合中
     */
    configuration.addMappedStatement(statement);
    return statement;
  }

Configuration#addMappedStatement()方法实现:

 public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);
  }

分析: 将解析完成的MappedStatement实例加入mappedStatements集合中, key为接口路径+方法名称 value为该接口方法对应的MappedStatement实例; 

3. 创建DefaultSqlSessionFactory对象

SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)方法实现:

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }


public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

 

二、SqlSession

SqlSession是MyBatis的关键对象, 是执行持久化操作的独享, 类似于JDBC中的Connection; 它是应用程序与持久层之间执行交互操作的一个单线程对象, 也是MyBatis执行持久化操作的关键对象; SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法, 它的底层封装了JDBC连接, 可以用SqlSession实例来直接执行被映射的SQL语句; 每个线程都应该有它自己的SqlSession实例; SqlSession的实例不能被共享, 同时SqlSession也是线程不安全的, 绝对不能讲SqlSeesion实例的引用放在一个类的静态字段甚至是实例字段中; 也绝不能将SqlSession实例的引用放在任何类型的管理范围中, 比如Servlet当中的HttpSession对象中; 使用完SqlSeesion之后关闭Session很重要, 应该确保使用finally块来关闭它.

SqlSession session = sqlSessionFactory.openSession()

DefaultSqlSessionFactory#openSession()方法实现: 

  @Override
  public SqlSession openSession() {
    // 使用默认的执行器类型(默认是SIMPLE),默认隔离级别,非自动提交 委托给openSessionFromDataSource方法
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      // 获取事务管理器, 支持从数据源或者直接获取
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 从数据源创建一个事务, 同样,数据源必须配置, mybatis内置了JNDI、POOLED、UNPOOLED三种类型的数据源,
      // 其中POOLED对应的实现为org.apache.ibatis.datasource.pooled.PooledDataSource,它是mybatis自带实现的一个同步、
      // 线程安全的数据库连接池 一般在生产中,我们会使用dbcp或者druid连接池
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //创建执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

DefaultSqlSession#DefaultSqlSession()方法的实现:

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

相关文章:

        Mybatis源码解析一(SqlSessionFactory和SqlSession的获取)

        Mybatis源码解析二(请求处理过程解析)

        Mybatis源码解析三(模拟Mybatis)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/139673.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • jsp开发环境搭建实验报告_eclipse安卓开发环境搭建

    jsp开发环境搭建实验报告_eclipse安卓开发环境搭建一.配置Java开发工具(JDK)1.下载JDKhttp://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html使用默认安装路径,默认安装在C盘中。如果更改安装路径,在安装jre时,更改安装路径,会出现选择文件夹不为空的情况。所以最好的办法是安装在默认路径下。2.环

    2022年10月20日
  • 虚拟机怎么安装vmware tools

    虚拟机怎么安装vmware tools这篇文章主要为大家详细介绍了VMwareWorkstation12安装Ubuntu和VMwareTools教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下之前我通过百度经验上的过程来安装Ubuntu16,但是每次安装的时候没有什么问题,就是安装好了Tools,也设置好了共享文件夹,但是在路径:/mnt/hgfs下每次都找不到共享文件夹。后来我研究了好久,应该是安装的时候…

  • 个人数字作品合作协议

    个人数字作品合作协议个人数字作品合作协议甲方:地址:身份证号:联系方式:乙方:北京创新乐知网络技术有限公司地址:北京市朝阳区酒仙桥路10号恒通商务园B8b二层本协议系由北京创新乐知网络技术有限公司(以下简称”CSDN”)与所有通过CSDN下载平台发布作品的用户就资源的引入、使用及相关服务所订立的有效合约,您必须同意并遵守本协议。您通过任何方式参与数字作品提交均被视为您完全接受本协议。本协议具有合同效力。一、总则1.1本协议内容包括协议正文及所有CSDN及CSDN下载平台已经发布的或将来.

  • python表情代码_Python实现表情包的代码实例[通俗易懂]

    python表情代码_Python实现表情包的代码实例[通俗易懂]本文主要介绍了使用Python进行简单图像处理的方法以及Python自动生产表情包的实例,具有很好的参考价值,下面跟着小编一起来看下吧作为一个数据分析师,应该信奉一句话——“一图胜千言”。不过这里要说的并不是数据可视化,而是一款全民向的产品形态——表情包!!!!表情包不仅仅是一种符号,更是一种文化——是促进社交乃至社会发展的动力之一,就像懒。我们坚持认为,一张优秀的表情包,应该是一幅艺术品,是那忽…

    2022年10月29日
  • oracle 错误01017,ORA-01017:用户名密码出错 故障实例

    oracle 错误01017,ORA-01017:用户名密码出错 故障实例sysdba登录ORA-01017:用户名密码出错故障排查实例早上接到一个朋友的急call,说是数据库的sys登录不了系统叻。普通用户连接可以登录,只要是assysdba就提示ORA-01017:用户名密码出错。很显然这是一个典型的sysdba登录的问题。首先要他查看了sqlnet文件。SQLNET.AUTHENTICATION_SERVICES=(NONE)启动密码文件验证了,接着查看…

  • Java设计模式(五)之创建型模式:原型模式

    Java设计模式(五)之创建型模式:原型模式

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号