IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Mybatis核心原理 -> 正文阅读

[Java知识库]Mybatis核心原理

简介

Mybatis是一款流行的持久层框架,基于ORM(Object-Relation Mapper)思想,对针对JDBC的封装,通过xml配置支持灵活复杂的SQL查询。

框架组件架构图

Mybatis核心成员数据流

image-20210604132936811

核心成员说明

核心成员功能说明
Configuration保存MyBatis大部分配置信息
SqlSessionMyBatis主要的顶层API,与数据库交互,实现数据库增删改查功能。
ExecutorMyBatis 调度器,负责SQL语句的生成和查询缓存的维护
StatementHandler封装JDBC,负责对JDBC statement 的操作,如设置参数等
ParameterHandler用户传递的参数转换成JDBC Statement 所对应的数据类型
ResultSetHandler负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
TypeHandler负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
MappedStatementMappedStatement维护一条<select|update|delete|insert>节点的封装
SqlSource负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql表示动态生成的SQL语句以及相应的参数信息

核心代码流程

1)Mybatis通过SqlSessionFactory获取sqlSession,然后有sqlSession完成数据库的交互。SqlSessionFactory默认接口实现是是DefaultSqlSessionFactory。

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

2) SqlSessionFactory有多个openSession方法,以无参的方法为例。

public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}

//executor 默认有多个实现,根据ExecutorType可知有以下几种
public enum ExecutorType {
    SIMPLE,
    REUSE,
    BATCH;
    private ExecutorType() {
    }
}
  1. 针对Dao层的定义的接口,MapperRegistry维护了Dao层接口的代理工厂,并由工厂生成具体的代理去处理sqlSession,并交底层Executor调度器去执行。
public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    //MapperMethod对应Dao层接口的方法
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperMethod> getMethodCache() {
        return this.methodCache;
    }

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

    //根据接口类型生成具体代理
    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}
  1. Executor调度器与StatementHandler等处理器交互,完成SQL操作
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }

并发场景的思考:DefaultSqlSessionFactory是线程安全的么?如何做到线程安全?

是不线程安全的,这个会在后面解释。

缓存机制

mybatis一级、二级缓存

一级缓存核心类是PerpetualCache,本质是一个hashMap

二级缓存默认不开启。

Spring 与Mybatis的整合

Spring bean 生命周期

SpringBean生命周期

MapperScannerConfigurer

MapperScannerConfigurer的主要工作是扫描basePackage包下所有的mapper接口类,并将mapper接口类封装成为BeanDefinition对象,注册到spring的BeanFactory容器中核心类图如下:

MapperScannerConfigurer

以上知道了Spring的bean注册到容器的核心流程,通过理解Spring的核心流程,可以梳理出Dao层接口Mapper通过MapperScannerConfigurer整合到spring的流程:

MapperScannerConfigurer 改

SqlSessionFactoryBean

类定义如下:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
   //省略一些详细代码 
   
   public void setDataSource(DataSource dataSource) {
      if (dataSource instanceof TransactionAwareDataSourceProxy) {
          this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();
      } else {
          this.dataSource = dataSource;
      }
    }
    public void setMapperLocations(Resource[] mapperLocations) {
        this.mapperLocations = mapperLocations;
    }
}
SqlSessionFactoryBean

从这个类的定义可以看出SqlSessionFactoryBean与DataSource和Mapper有关。

在bean被创建的中,通过接口InitializingBean中的afterPropertiesSet方法设置属性。

public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.dataSource, "Property 'dataSource' is required");
        Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
        this.sqlSessionFactory = this.buildSqlSessionFactory();
 }

buildSqlSessionFactory方法主要完成SqlSession的初始化操作,完成在上面讲的Mybatis核心代码流程。

SqlSessionFactoryBean整合进Spring的流程

SqlSessionFactory初始化

Mybatis整合Spring的整体流程

以下面简单的代码做主要流程说明

@Service
class AService{
   @Autowire
   private BDao bDao;
}

Spring在初始化的过程中@Service注解的类,AService类初始化完成之后,会进行属性赋值,bDao接口就是AService的一个属性,

1)首先根据这个bDao的名字或者类型从spring的BeanFactory中获取它的BeanDefinition,再从BeanDefinition中获取BeanClass,bDao对应的BeanClass就是MapperFactoryBean,这在创建MapperScannerConfigurer对象的时候设置的。

2)创建MapperFactoryBean对象,创建完成后,对属性进行赋值,其中有一个属性就是SqlSessionFactoryBean

3)MapperFactoryBean对象的属性设置完成之后,就调用它的getObject()方法,来获取bDao对应的实现类,获取的是一个JDK的代理类

public class MapperProxy<T> implements InvocationHandler, Serializable {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            MapperMethod mapperMethod = this.cachedMapperMethod(method);
            return mapperMethod.execute(this.sqlSession, args);
        }
    }
}

程序在调用AService对象的某个方法的时候,就会调用到MapperProxy对象的invoke()方法,去完成对数据库的操作。

如何解决SqlSession的线程安全问题

MapperFactoryBean.getObject()获取的实例,实际是通过一个SqlSessionTemplate对象创建的,注入的Mapper对象实际上最终都执行的是SqlSessionTemplate方法。

SqlSessionTemplate

关键代码如下:

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        Assert.notNull(executorType, "Property 'executorType' is required");
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}

private class SqlSessionInterceptor implements InvocationHandler {
        private SqlSessionInterceptor() {
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

            Object unwrapped;
            try {
                Object result = method.invoke(sqlSession, args);
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }

                unwrapped = result;
            } catch (Throwable var11) {
                unwrapped = ExceptionUtil.unwrapThrowable(var11);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }

                throw (Throwable)unwrapped;
            } finally {
                if (sqlSession != null) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }

            }

            return unwrapped;
        }
    }
image-20210527212038675 image-20210527212403567
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-22 22:56:54  更:2021-07-22 22:57:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 4:32:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码