Mybatis详解

1提前了解

传统JDBC编码格式

public class DataBaseUtil {
    public static final String URL = "jdbc:mysql://localhost:3306/mblog";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";

    public static void main(String[] args) throws Exception {
        
        Class.forName("com.mysql.jdbc.Driver");
        //2. 
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        //3.
        Statement stmt = conn.createStatement();
        //4.
        ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");
        //如果有数据,rs.next()返回true
        while(rs.next()){
            System.out.println("name: "+rs.getString("name")+" 年龄:"+rs.getInt("age"));
        }
    }
}

业务一多代码重复次数会非常之多

传统JDBC的问题

  • 创建数据库的连接存在大量的硬编码,
  • 执行statement时存在硬编码.
  • 频繁的开启和关闭数据库连接,会严重影响数据库的性能,浪费数据库的资源.
  • 存在大量的重复性编码

为了解决以上问题,就诞生了各种各样替换JDBC的产品。即就是ORM框架。

什么是ORM?

全称为Object Relational Mapping。对象-映射-关系型数据库。对象关系映射(,简称ORM,或O/RM,或O/R mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象与关系数据库相互映射。

2Mybatis流程

 

 

 

 

 

 

 

 

 

 3sqlsession详解,搞懂弄明白一半mybatis

mybatis每个sqlsession都有一个新的Executor,通过它来操控数据库,增删改是Executor的update方法执行,查询语句是用query方法。
 SqlSession作为一个接口,其并没有线程安全性的问题,我们常说的线程安全问题是SqlSession的一个实现类DefaultSqlSession,mybatis的作者也对此类加以"Note that this class is not Thread-Safe"的注释。
此外SqlSession还有两个实现类SqlSessionManager和SqlSessionTemplate,这两个实现类是线程安全的。
线程不安全的DefaultSqlSession?
        我们都知道DefaultSqlSession是线程不安全的,也会有很多博主讲解"SqlSessionTemplate是如何保证DefaultSqlSession线程安全的",但是DefaultSqlSession不安全的体现是什么?不安全产生的原因在哪?

首先看一下缓存流程和机制

 

 

什么是 一级缓存:

每当一个新 session 被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在 session 执行过的查询结果都会被保存在本地缓存中,所以,当再次执行参数相同的相同查询时,就不需要实际查询数据库了。本地缓存将会在做出修改、事务提交或回滚,以及关闭 session 时清空。(所以这里经常问缓存失效问题

  • sqlsession变了 缓存失效
  • sqlsession不变,查询条件不同,一级缓存失效
  • sqlsession不变,中间发生了增删改操作,一级缓存失败(即执行了新一次事务并提交到数据库)
  • sqlsession不变,手动清除缓存,一级缓存失败


所以问题就出现在这,即源码中的localCache, 查询时,由于第一次查询是不存在缓存的

Thread1进入query方法,用key取缓存localCache数据不存在,则进入了queryFromDatabase,并执行了"localCache.putObject(key, EXECUTION_PLACEHOLDER)",而此时Thread2进入了query方法,用key取缓存localCache数据,此时取出来的是Thread1刚缓存的EXECUTION_PLACEHOLDER,然后执行类型转换,由于EXECUTION_PLACEHOLDER不是list类型,所以转换抛出异常。
所以:

是BaseExecutor中缓存机制(mybatis的一级缓存)导致了并发问题。这种并发问题,产生原因:并发操作使用了同一个DefaultSqlSession的实例,而同一个DefaultSqlSession的实例使用的是同一个Executor对象,当缓存命中时就会出现异常或者数据不完整的情况。

 

 那SqlSessionTemplate如何线程安全

SqlSessionTemplate是MyBatis专门为Spring提供的,支持Spring框架的一个SqlSession获取接口。主要是为了继承Spring,并同时将是否共用SqlSession的权限交给Spring去管理。查看getSqlSession()方法就知道每个线程对应的SqlSession都是私有的不会被共用,所以SqlSessionTemplate是线程安全的。

究其根本SqlSession真正的实现类只有DefaultSqlSession,SqlSessionManager和SqlSessionTemplate都是通过代理转发到DefaultSqlSession对应方法。

单例模式下的DefaultSqlSession不是线程安全的,SqlSessionManager和SqlSessionTemplate线程安全的根本就是每一个线程对应的SqlSession都是不同的。如果每一个操作都创建一个SqlSession对象,操作完又进行销毁导致性能极差。通过线程私有ThreadLocal存储SqlSession进行复用,从而提高性能。
结论:
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。

posted @ 2021-04-04 11:18  To_Yang  阅读(142)  评论(0编辑  收藏  举报