Mybatis引出的一系列问题-Mybatis原理的探究

1 Mybatis的工作原理

mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理 - 加载驱动driver、创建连接 connection、创建 statement 等繁杂的过程。

    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection(url, username, password);
    Statement stmt = conn.createStatement();
    String sql1 = "update user set age = 100 where id = 1";
    int rows1 = stmt.executeUpdate(sql1);
    stmt.close();
    conn.close();

mybatis 通过 xml或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 sql的动态参数进行映射生成最终执行的 sql 语句, 最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。 采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

在这里插入图片描述

读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。

<?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>
    <!--数据源配置-->
    <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://localhost:3306/default?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--加载映射文件-->
    <mappers>
        <package name="com.inspur.mybatis.dao"/>
    </mappers>
</configuration>

加载映射文件:映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。

创建会话对象SqlSession :由会话工厂SqlSessionFactory创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法,是一个既可以发送sql执行并返回结果的,也可以获取mapper的接口。

Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
// 通过SqlSession实现增删改查
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> users = userMapper.findAll();

Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。

MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。

输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。

输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

SqlSessionFactoryBuilder是利用XML或者Java编码获得资源来构建SqISessionFactory的,通过它可以构建多个SessionFactory。它的作用就是一个构建器,一旦我们构建了
SqlSessionFactory,它的作用就已经完结,失去了存在的意义,将它回收。所以它的生命周期只存在于方法的局部,它的作用就是生成SqlSessionFactory对象。

SqlSessionFactory作用:创建sqlSession,sqlSession是一个会话,相当于jdbc中的Connection对象。每次访问数据库就要通过SqlSessionFactory创建sqlSession,所以SqlSessionFactory会在mybatis整个生命周期都会使用。

如果多次创建同一个数据库的SqlSessionFactory,每次创建SqlSessionFactory会打开更多的数据库连接资源,连接资源会被耗尽,所以这个采用单例模式。一个数据库只对应一个SqlSessionFactory。

SqlSession是一个会话,相当于jdbc的一个connection对象,生命周期是在请求数据库处理事务的过程中,线程不安全的对象。

每次创建SqlSession都必须即时关闭,否则数据库连接池的活动资源少。SqlSession提供了增删改查,使用mapper接口,其中有映射器,映射器作为一个动态代理,进入到mapperMethod的方法就能简单找到SqlSession的增删改查。其实就是通过动态代理记录,让接口跑起来,使用命令模式,最后采用sqlSession的方法执行sql语句。

什么时候会创建sqlSession?
由代码去决定什么时候更换sqlSession,所以你可以自己一直使用一个SqlSession

public class MybatisUtils {
    
        
	public static SqlSession getSqlSession() {
    
    
        return sqlSessionFactory.openSession();
    }
}
-------------------------------测试方法------------------------------------------------
@Test
public void getUserById() {
    
    
    //工具类中获取SqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    try {
    
    
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.getUserById(1);
        System.out.println(1);
        User user2 = userDao.getUserById(1);
        System.out.println(2);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        //关闭SqlSession
        sqlSession.close();
    }
}
@Autowired
private UserMapper userMapper;
@Test
//    @Transactional
void contextLoads() {
    
    
    List<User> users = userMapper.selectList(null);
    List<User> us = userMapper.selectList(null);
    users.forEach(System.out::println);
    us.forEach(System.out::println);
}

没有给方法添加事务时:每调用一次dao层的查询接口就会创建一个sqlSession,然后销毁一个sqlSession。

在这里插入图片描述
给方法添加事务时:一个事务中,使用一个SqlSession,如图所示,第二次查询使用同一个sqlSession,然后缓存命中没有再次查询:
在这里插入图片描述
这里引出sqlSession 与 事务之间的联系!!!
这里引出sqlSession 与 事务之间的联系!!!
这里引出sqlSession 与 事务之间的联系!!!

SqlSession的实现类:
在这里插入图片描述
【DefaultSqlSession】 是mybatis默认使用的实现类。这个类的线程是不安全的。所以每一个线程都要创建一个他自己有的sqlSession,所以不能把他设置成单例。

【SqlSessionManager】 线程安全

【SqlSessionTemplate】 SqlSessionTemplate是一个线程安全的类,当你运行一个sqlSessionTemplate时,会重新获取一个sqlSession,所有每一个方法都有独立的一个sqlSession,说明是线程安全的。

Executor执行器: mybatis一共有三个执行器

在这里插入图片描述
SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。所以这种会特别浪费性能,不合适使用。

ReuseExecutor:可重用处理器,执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map<String, Statement>内,供下一次使用。简言之,就是重复使用 Statement 对象。

BatchExecutor: 执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理。与 JDBC 批处理相同。

作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

Mapper是一个接口,没有任何实现类,作用是发送SQL返回需要的结果,或者执行SQL从而修改数据库的数据,因此应该在一个SqlSession事务方法之内,是方法基本的。

在这里插入图片描述
在这里插入图片描述

2 sqlSession与映射器

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。 而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。 MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。 后面会再探讨 XML 配置文件的详细内容,这里先给出一个简单的示例:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

当然,还有很多可以在 XML 文件中配置的选项,上面的示例仅罗列了最关键的部分。 注意 XML 头部的声明,它用来验证 XML 文档的正确性。 environment 元素体中包含了事务管理和连接池的配置。mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。

既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

try (SqlSession session = sqlSessionFactory.openSession()) {
    
    
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    Blog blog = mapper.selectBlog(101);
}

现在你可能很想知道 SqlSession 和 Mapper 到底具体执行了些什么操作,但 SQL 语句映射是个相当广泛的话题,可能会占去文档的大部分篇幅。这里给出一个基于 XML 映射语句的示例,它应该可以满足上个示例中 SqlSession 的调用:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{
    
    id}
  </select>
</mapper>

为了这个简单的例子,我们似乎写了不少配置,但其实并不多。在一个 XML 映射文件中,可以定义无数个映射语句,这样一来,XML 头部和文档类型声明部分就显得微不足道了。 文档的其它部分很直白,容易理解。 它在命名空间 “org.mybatis.example.BlogMapper” 中定义了一个名为 “selectBlog” 的映射语句, 这样你就可以用全限定名 “org.mybatis.example.BlogMapper.selectBlog” 来调用映射语句了,就像上面例子中那样:

Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

你可能会注意到,这种方式和用全限定名调用 Java 对象的方法类似。这样,该命名就可以直接映射到在命名空间中同名的映射器类, 并将已映射的 select 语句匹配到对应名称、参数和返回类型的方法。 因此你就可以像上面那样,不费吹灰之力地在对应的映射器接口调用方法,就像下面这样:

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

第二种方法有很多优势,首先它不依赖于字符串字面值,会更安全一点;其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择到映射好的 SQL 语句。

对命名空间的一点补充: 命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了你上面见到的接口绑定。就算你觉得暂时用不到接口绑定,你也应该遵循这里的规定,以防哪天你改变了主意。 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,你的代码会变得更加整洁,也有利于你更方便地使用 MyBatis。

对于像 BlogMapper 这样的映射器类来说,还有另一种方法来完成语句映射。 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。比如,上面的 XML 示例可以被替换成如下的配置:

package org.mybatis.example;
public interface BlogMapper {
    
    
    @Select("SELECT * FROM blog WHERE id = #{id}")
    Blog selectBlog(int id);
}

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。

BlogMapper mapper = session.getMapper(BlogMapper.class);

虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。 但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。 尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:

try (SqlSession session = sqlSessionFactory.openSession()) {
    
    
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    // 你的应用逻辑代码
}

映射器是一些由用户创建的、绑定 SQL 语句的接口。 SqlSession 中的 insert、update、delete 和 select 方法都很强大,但也有些繁琐。更通用的方式是使用映射器类来执行映射语句。 一个映射器类就是一个仅需声明与 SqlSession 方法相匹配的方法的接口类。

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

MyBatis 将配置文件中的每一个 节点抽象为一个 Mapper 接口,而这个接口中声明的方法和跟 节点中的 <select|update|delete|insert> 节点相对应, 即 <select|update|delete|insert> 节点的 id 值为 Mapper 接口中的方法名称,parameterType 值表示 Mapper 对应方法的入参类型, 而 resultMap 值则对应了 Mapper 接口表示的返回值类型或者返回结果集的元素类型。

MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个 Mapper 实例;MyBatis 会根据这个方法的方法名和参数类型,确定 Statement id,然后和 SqlSession 进行映射, 底层还是通过 SqlSession 完成和数据库的交互。

映射器接口不需要去实现任何接口或继承自任何类。只要方法可以被唯一标识对应的映射语句就可以了。

映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。 尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。

在这里插入图片描述
SqlSessionFactory 应用了工厂设计模式,它提供了一组方法,用于创建 SqlSession 实例。

事务处理:你希望在 session 作用域中使用事务作用域,还是使用自动提交(auto-commit)?

数据库连接:你希望 MyBatis 帮你从已配置的数据源获取连接,还是使用自己提供的连接?

语句执行:你希望 MyBatis 复用 PreparedStatement 和/或批量更新语句(包括插入语句和删除语句)吗?

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();

默认的 openSession() 方法没有参数,它会创建具备如下特性的 SqlSession:

在这里插入图片描述
事务作用域将会开启(也就是不自动提交)。
将由当前环境配置的 DataSource 实例中获取 Connection 对象。
事务隔离级别将会使用驱动或数据源的默认设置。
预处理语句不会被复用,也不会批量处理更新。

向 autoCommit 可选参数传递 true 值即可开启自动提交功能。若要使用自己的 Connection 实例,传递一个 Connection 实例给 connection 参数即可。注意,我们没有提供同时设置 Connection 和 autoCommit 的方法,这是因为 MyBatis 会依据传入的 Connection 来决定是否启用 autoCommit。

JDBC中的Connection的属性autocommit是默认为true的,就是说自动提交事务,当每执行一个update、delete、insert语句的时候都会自动提交到数据库,无法回滚事务。

设置connection.setAutoCommit(false),即设置autoCommit为false时,就是说这个连接不自动提交,须手动conn.commit()来提交到数据库,只有conn.commit(),才会真正提交事务,才会将先前执行的语句一起提交到数据库(即执行commit()才会提交之前所有的数据库操作),否则都不会提交,这样就实现了数据库的事务。一旦commit, 事务就会提交,就不可回滚。

所以在使用时应先调用conn.setAutoCommit(false),然后执行完所有数据库操作语句后就可以conn.commit()提交,异常时conn.rollback()回滚,用jdbc实现对事务的控制。

如果不设置autoCommit为false,后面调用con.commit()或con.rollback()方法则一定会报SQLException(报错为:已经设置了autocommit,不能再commit)。

对于事务隔离级别,MyBatis 使用了一个 Java 枚举包装器来表示,称为 TransactionIsolationLevel,事务隔离级别支持 JDBC 的五个隔离级别(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),并且与预期的行为一致。

你可能对 ExecutorType 参数感到陌生。这个枚举类型定义了三个值:

ExecutorType.SIMPLE:该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。
ExecutorType.REUSE:该类型的执行器会复用预处理语句。
ExecutorType.BATCH:该类型的执行器会批量执行所有更新语句,如果 SELECT 在多个更新中间执行,将在必要时将多条更新语句分隔开来,以方便理解。

提示 在 SqlSessionFactory 中还有一个方法我们没有提及,就是 getConfiguration()。这个方法会返回一个 Configuration 实例,你可以在运行时使用它来检查 MyBatis 的配置。

Mybatis内置的ExecutorType有3种,默认的是simple单句模式,该模式下它为每个语句的执行创建一个新的预处理语句,单句提交sql;

batch模式重复使用已经预处理的语句,并且批量执行所有语句,大批量模式下性能更优。请注意batch模式在Insert操作时事务没有提交之前,是没有办法获取到自增的id,所以请根据业务情况使用。

使用simple模式提交10000条数据,时间为19s,batch模式为6s ,大致情况如此,优化的具体还要看提交的语句情况。

如果需要使用 foreach 来优化数据插入的话,需要将每次插入的记录控制在 10-100 左右是比较快的,建议每次100来分割数据,也就是分而治之思想。

普通插入:默认的插入方式是遍历insert语句,单条执行,效率肯定低下,如果成堆插入,更是性能有问题。

   INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
   INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
   INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
   INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
   INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");

foreach 优化插入:

如果要优化插入速度时,可以将许多小型操作组合到一个大型操作中。理想情况下,这样可以在单个连接中一次性发送许多新行的数据,并将所有索引更新和一致性检查延迟到最后才进行。

   <insert id="batchInsert" parameterType="java.util.List">
       insert into table1 (field1, field2) values
       <foreach collection="list" item="t" index="index" separator=",">
       (#{
    
    t.field1}, #{
    
    t.field2})
       </foreach>
   </insert>

翻译成sql语句也就是:

   INSERT INTO `table1` (`field1`, `field2`)
   VALUES ("data1", "data2"),
   ("data1", "data2"),
   ("data1", "data2"),
   ("data1", "data2"),
   ("data1", "data2");

foreach 遇到数量大,性能瓶颈:项目实践发现,当表的列数较多(超过20),以及一次性插入的行数较多(上万条)时,插入性能非常差,通常需要20分钟以上。这个时候就需要观察曲线了,10-100个来讲是很快的,当然也要根据项目请来看,总之建议100个就ok了,不要太高。

在这里插入图片描述

**executeBatch方法:**批量执行的一种方式,使用PreparedStatement预编译进行优化。

   int insertNum = 100;
   Connection connection = 
       DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/xxx?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true","root","root123");
   connection.setAutoCommit(false);
   PreparedStatement ps = connection.prepareStatement("insert into table1(field1) values(?)");
   for (int i = 0; i < insertNum; i++) {
    
    
       ps.setString(1,"大狼狗"+insertNum);
       ps.addBatch();
   }
   ps.executeBatch();
   connection.commit();
   connection.close();

开启ExecutorType.BATCH模式:简单的讲就是openSession的时候带上参数ExecutorType.BATCH,可以几乎无损优化你的代码性能。

   SqlSession session = sessionFactory.openSession(ExecutorType.BATCH);
   for (Model model : list) {
    
    
       session.insert("insertStatement", model);
   }
   session.flushStatements();

BatchInsert模式: 也是官方针对批量数据插入优化的方法之一

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
   try {
    
    
       TableMapper mapper = session.getMapper(TableMapper.class);
       // 自定义你的方法来获取需要插入的数据
       List<TableRecord> records = getRecordsToInsert();
       //BatchInsert
       BatchInsert<TableRecord> batchInsert = insert(records)
           .into(table)
           .map(id).toProperty("id")
           .map(field1).toProperty("field1")
           .map(field2).toProperty("field2")
           .build()
           .render(RenderingStrategy.MYBATIS3);
       batchInsert.insertStatements().stream().forEach(mapper::insert);
       session.commit();
   } finally {
    
    
   session.close();
   }

SimpleExecutor: 简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个 Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象)

ReuseExecutor: 可重用执行器,这里的重用指的是重复使用 Statement,它会在内部使用一个 Map 把创建的 Statement 都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement 对象,如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement 对象,并将其缓存起来。

因为每一个 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的Statement 作用域是同一个 SqlSession。

BatchExecutor: 批处理执行器,用于将多个SQL一次性输出到数据库

CachingExecutor: 缓存执行器,先从缓存中查询结果,如果存在,就返回;如果不存在,再委托给 Executor delegate 去数据库中取

ExecutorType 来决定 Configuration 对象创建何种类型的执行器,它的赋值可以通过两个地方进行赋值:
可以通过 settings 标签来设置当前工程中所有的 SqlSession 对象使用默认的 Executor

   <settings>
    <!--取值范围 SIMPLE, REUSE, BATCH -->
     <setting name="defaultExecutorType" value="SIMPLE"/>
   </settings>

另外一种直接通过Java对方法赋值的方式:

   session = factory.openSession(ExecutorType.BATCH);

在这里插入图片描述
SQLSessionFactory 应该以单例形式在应用的运行期间一直存在。

SqlSession 正如之前所提到的,SqlSession 在 MyBatis 中是非常强大的一个类。它包含了所有执行语句、提交或回滚事务以及获取映射器实例的方法。

SqlSessions 是由 SqlSessionFactory 实例创建的;而 SqlSessionFactory 是由 SqlSessionFactoryBuilder 创建的。

注意:当 MyBatis 与一些依赖注入框架(如 Spring 或者 Guice)同时使用时, SqlSessions 将被依赖注入框架所创建,所以你不需要使用 SqlSessionFactoryBuilder 或者 SqlSessionFactory。

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。

正确在 Web 中使用 SqlSession 的场景是:每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。

猜你喜欢

转载自blog.csdn.net/zs18753479279/article/details/132078430