广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java Mybatis架构设计深入了解
  • 873
分享到

Java Mybatis架构设计深入了解

2024-04-02 19:04:59 873人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录架构设计mybatis主要构件Mybatis缓存总结:架构设计 我们可以把Mybatis的功能架构分为三层: 1.api接口层:提供给外部使用的接口API,开发人员通过这些本地

架构设计

我们可以把Mybatis的功能架构分为三层:

1.api接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

Mybatis和数据库的交互有两种方式:

  1. 使用传统的Mybatis提供API
  2. 使用Mapper代理的方式

2.数据处理层:负责具体的sql查找、SQL解析、SQL执行和执行结果映射处理等。他主要的目的是根据调用的请求完成一次数据库操作。

3.基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来最为基础组件。为上层的数据处理层提供最基础的支撑。

Mybatis主要构件

构件 描述
SqlSession 作为Mybatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删查改功能
Executor Mybatis执行器,是Mybatis调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler 封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数、将Statement结果集转换为List集合
ParameterHandler 负责对用户传递的参数转换为JDBC Statement所需要的参数
ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换为List类型的集合
TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换
MappedStatement MappedStatement维护了一条<select、 update 、 delete 、insert >节点的封装
SqlSource 负责根据用户传递的parameterObject,动态的生成SQL语句,将信息封装到BoundSql对象中
BoundSql 表示动态生成的SQL语句以及相应的参数信息

总体流程:

1.加载配置并初始化

配置来源于两个地方,一个是配置文件(conf.xml,mapper*.xml),一个是java代码中的注解,将配置文件内容封装到Configuration,将sql的配置信息加载成为一个mappedstatement对象,存储在内存中。

2. 接收调用请求

触发条件:调用Mybatis提供的API

传入参数:为SQL的ID和传入的参数

将请求传递给下层的请求处理层进行处理

3.处理操作请求

  • 根据SQL的ID查找对应的MappedStatement对象
  • 根据传入参数对象解析,得到最终要执行的SQL和执行传入参数
  • 获取数据库连接,将最终SQL语句和参数给到数据库执行,并得到执行结果
  • 根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果
  • 释放连接资源

4.返回处理结果

Mybatis缓存

Mybatis有一级缓存和二级缓存。Mybatis收到查询请求后首先会查询二级缓存,若二级缓存未命中,再去查询一级缓存,一级缓存没有,再查询数据库。

一级缓存


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++;
      //从localCache缓存里查数据,没有就去查数据库
      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();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

这个localCache是BaseExecutor里面的一个属性


public abstract class BaseExecutor implements Executor {


  protected PerpetualCache localCache;

PerpetualCache类


public class PerpetualCache implements Cache {

  private final String id;

  private Map<Object, Object> cache = new HashMap<Object, Object>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

二级缓存

启用二级缓存步骤:

1.开启cacheEnabled(默认打开)


<settings>
<setting name="cacheEnabled" value="true"/>
</settings>

2.需要在二级缓存的Mapper配置文件中加入


<cache></cache>

3.注意,二级缓存要想生效,必须要调用sqlSession.commit或close方法


 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

注意Cache cache = ms.getCache();,这个cache是从MappedStatement中获取到的,由于MappedStatement存在全局配置中,可以多个CachingExecutor获取到,这样就会出现线程安全问题。除此之外,若不加以控制,多个事务共用一个缓存实例,会导致脏读的存在。

那么mybatis是怎么解决脏读的呢?借用了上面的tcm这个变量,也就是TransactionalCacheManager类来解决的。

TransactionalCacheManager类维护了Cache,TransactionalCache的关系,真正的数据还是交由TransactionalCache处理的。

结构如图:


public class TransactionalCacheManager {

  private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();

  public void clear(Cache cache) {
    getTransactionalCache(cache).clear();
  }

  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }
  
  public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
  }

  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }

  public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
  }

  private TransactionalCache getTransactionalCache(Cache cache) {
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
      txCache = new TransactionalCache(cache);
      transactionalCaches.put(cache, txCache);
    }
    return txCache;
  }

}

接下来看一下TransactionalCache的代码


public class TransactionalCache implements Cache {

  private static final Log log = LogFactory.getLog(TransactionalCache.class);

  // 真正的缓存对象
  private final Cache delegate;
  private boolean clearOnCommit;
  //在事务被提交前,所有从数据库中查询的结果将缓存在此集合中
  private final Map<Object, Object> entriesToAddOnCommit;
  //在事务被提交前,当缓存未命中时,CacheKey 将会被存储在此集合中
  private final Set<Object> entriesMissedInCache;

  public TransactionalCache(Cache delegate) {
    this.delegate = delegate;
    this.clearOnCommit = false;
    this.entriesToAddOnCommit = new HashMap<Object, Object>();
    this.entriesMissedInCache = new HashSet<Object>();
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  @Override
  public Object getObject(Object key) {
    // issue #116
    //获取缓存的时候从delegate里获取的
    Object object = delegate.getObject(key);
    if (object == null) {
      //缓存未命中,将key存入entriesMissedInCache.
      entriesMissedInCache.add(key);
    }
    // issue #146
    if (clearOnCommit) {
      return null;
    } else {
      return object;
    }
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  @Override
  public void putObject(Object key, Object object) {
    //put的时候只是将数据库的数据放入到了entriesToAddOnCommit
    entriesToAddOnCommit.put(key, object);
  }

  @Override
  public Object removeObject(Object key) {
    return null;
  }

  @Override
  public void clear() {
    clearOnCommit = true;
    entriesToAddOnCommit.clear();
  }

  public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    //刷新未缓存的结果到delegate中去
    flushPendingEntries();
    reset();
  }

  public void rollback() {
    unlockMissedEntries();
    reset();
  }

  private void reset() {
    clearOnCommit = false;
    entriesToAddOnCommit.clear();
    entriesMissedInCache.clear();
  }

  private void flushPendingEntries() {
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.geTKEy(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }

  private void unlockMissedEntries() {
    for (Object entry : entriesMissedInCache) {
      try {
        delegate.removeObject(entry);
      } catch (Exception e) {
        log.warn("Unexpected exception while notifiying a rollback to the cache adapter."
            + "Consider upgrading your cache adapter to the latest version.  Cause: " + e);
      }
    }
  }

}

我们存储二级缓存的时候是放入到TransactionalCache.entriesToAddOnCommit这个map中,但是每次查询的时候是从delegate查询的,所以这个二级缓存查询数据库后,缓存是没有立刻生效的。只有当执行了sqlSession的commit或close方法后,它会调用到tcm的commit,在调用到transactionlCache的commit,刷新缓存到delegate了。

总结:

二级缓存的设计上,大量运用了装饰器模式,如SynchronizedCache、LogginGCache。

二级缓存实现了Sqlsession之间的缓存数据共享,属于namespace级别

二级缓存的实现由CachingExecutor和一个事务型预缓存TransactionlCache完成。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!

--结束END--

本文标题: Java Mybatis架构设计深入了解

本文链接: https://www.lsjlt.com/news/156293.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java Mybatis架构设计深入了解
    目录架构设计Mybatis主要构件Mybatis缓存总结:架构设计 我们可以把Mybatis的功能架构分为三层: 1.API接口层:提供给外部使用的接口API,开发人员通过这些本地...
    99+
    2022-11-12
  • 深入了解Mysql逻辑架构
      Mysql现在是大多数公司企业在用的数据库,之所以用Mysql,一点是因为Mysql是开源软件,一些有能力的公司会基于已有的Mysql架构,进行修改、调整改成适合自己公司的业务需要。 一点是因为Mysql免费,相...
    99+
    2022-05-30
    MySQL 架构 MySQL 逻辑架构
  • 深入了解Java和Django的响应式框架构建
    Java和Django都是非常流行的Web开发框架。它们都拥有自己的优点和特点,但是它们都可以使用响应式框架进行构建。在本文中,我们将深入了解Java和Django响应式框架的构建。 响应式编程是一种编程范式,它可以让我们更加灵活地处理异步...
    99+
    2023-09-07
    django 响应 框架
  • 深入了解C语言结构化的程序设计
    目录C语言是结构化的程序设计语言!if语句1:单分支结构2:多分支结构悬空elsewhile循环总结C语言是结构化的程序设计语言! 结构有三大类:顺序结构,选择结构,循环结构! 顺序...
    99+
    2022-11-12
  • 深入了解Java设计模式之策略模式
    目录定义解决的问题核心要点类图溢出效用代码实现核心接口实现类-三个Context类Main方法拓展JDK源码Spring源码定义 定义了算法家族,分别封装起来,让他们之间可以相互替换...
    99+
    2022-11-13
  • Java Mybatis框架由浅入深全解析上篇
    目录学习路线什么是三层架构常用的SSM框架(了解)什么是框架什么是Mybatis框架添加框架的步骤1.新建库建表2.新建maven项目3.修改目录4.修改pom.xml文件5.修改p...
    99+
    2022-11-13
  • Java Mybatis框架由浅入深全解析中篇
    目录前言添加框架的步骤在idea中添加数据库的可视化添加jdbc.properties属性文件(数据库配置)添加SqlMapCongig.xml创建实体类Student用来封装数据添...
    99+
    2022-11-13
  • Java Mybatis框架由浅入深全解析下篇
    目录前言什么是MavenMaven环境配置Maven 构建生命周期Maven项目的创建目录结构pom.xml文件什么是pom.xml文件加入项目所需依赖添加资源文件的指定总结前言 上...
    99+
    2022-11-13
  • 深入了解Java设计模式之职责链模式
    目录定义解决的问题核心要点类图代码实现抽象父类执行者-三个客户端拓展应用场景定义 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链,并沿着...
    99+
    2022-11-13
  • 深入了解Java线程池:从设计思想到源码解读
    目录为什么需要线程池线程池设计思路线程池的工作机制线程池的参数及使用线程池的状态提交任务任务队列线程工厂拒绝策略关闭线程池Executors 静态工厂合理地配置线程池线程池的监控源码...
    99+
    2022-11-12
  • 深入了解Java行为型设计模式之策略模式
    目录策略模式应用场景优缺点主要角色策略模式的基本使用创建抽象策略角色创建具体策略角色创建上下文角色客户端执行策略模式实现支付方式的选择创建抽象策略角色创建具体策略角色创建上下文角色客...
    99+
    2022-11-13
  • 深入了解Java中循环结构的使用
    目录1.Java 循环结构概述2. while 循环2.1 while 循环结构简介2.2 while 循环语法格式2.3 while 循环代码实例3. do…whil...
    99+
    2022-11-13
    Java 循环结构 Java 循环
  • 如何深入了解J2ME网络程序设计
    这篇文章给大家介绍如何深入了解J2ME网络程序设计,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。和大家重点讨论一下J2ME网络程序设计,javax.mic...
    99+
    2022-10-19
  • 深入了解GoLang中的工厂设计模式
    目录1. 定义2. 优点3. 代码实现3.1 普通工厂3.2 工厂方法3.3 抽象工厂1. 定义 工厂模式是一种创建型设计模式,有了工厂只需要知道要制造的东西名字,就能让对应工厂进行...
    99+
    2023-05-20
    GoLang工厂模式 Go工厂模式 GoLang设计模式工厂模式
  • 深入解析Spring架构与设计原理-数据库的操作实现
    关于Spring JDBC 还是从Spring JDBC说起吧,虽然现在应用很多都是直接使用Hibernate或者其他的ORM工具。但JDBC毕竟还是很基本的,其中的JdbcTemplate就是我们经常使用...
    99+
    2022-10-18
  • Java架构设计之六步拆解DDD
    目录引言项目需求信息DDD落地实践战略设计1、业务分析(1)事前准备(2)邀请参会的人(3)业务讨论2、领域建模(1)领域对象分析(2)构建业务聚合3、划分边界上下文战术设计1、微服...
    99+
    2022-11-13
  • Java深入了解数据结构之哈希表篇
    目录1,概念2,冲突-避免3,冲突-避免-哈希函数设计4,冲突-避免-负载因子调节5,冲突-解决-闭散列①线性探测②二次探测6,冲突-解决-开散列/哈希桶7,完整代码1,概念 顺序结...
    99+
    2022-11-13
  • 一文带你深入了解Java的数据结构
    目录枚举接口(Enumeration)位集合(BitSet)向量(Vector)栈(Stack)字典(Dictionary)哈希表(Hashtable)属性(Properties)J...
    99+
    2023-05-18
    Java数据结构 接口 Java数据结构 类 Java数据结构
  • 深入了解Java数据结构和算法之堆
    目录1、堆的定义2、遍历和查找3、移除4、插入5、完整的Java堆代码总结1、堆的定义 ①、它是完全二叉树,除了树的最后一层节点不需要是满的,其它的每一层从左到右都是满的。注意下面两...
    99+
    2022-11-13
  • 深入了解PHP trait DTO的设计模式与实践
    Introduction:在PHP开发中,设计模式是必不可少的一部分。其中,DTO(Data Transfer Object)是一种常用的设计模式,用于封装数据传输的对象。而在实现DTO的过程中,使用trait(特征)可以有效地提高代码的复...
    99+
    2023-10-21
    PHP dto trait
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作