Mybatis源码分析(1)

1、Maven

OGNL的三要素

表达式:OGNL表达式是功能强大的表达式语言,何解?在ognl中想要执行取值,赋值,调用方法等等操作,你都需要用表达式表示。通过表达式,底层会解析出来你的想要操作。它支持链式结构

根对象:即root对象,可以理解为OGNL的操作对象,表达式规定做什么,而该对象就指定对谁操作。OGNL叫做对象图导航语言,对象图就是以任意一个对象作为根,通过OGNL可以访问到与这个对象相关的其他对象。底层使用list集合做的。

Context对象:其实就是OGNL的上下文环境。root对象也在OGNL的上下文环境里,底层是一个Map集合。该上下文环境规定了OGNL操作在”哪里进行”,注意访问context对象时候需要在表达式中加上#。

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态”AOP”框架。

核心内容如下:

基于类的动态代理

_Hsqldb_是一个开放源代码的JAVA数据库,其具有标准的SQL语法和JAVA接口,它可以自由使用和分发,非常简洁和快速的

Apache _Derby_是一个完全用java编写的数据库

_H2_是一个短小精干的嵌入式数据库引擎,主要的特性包括: 免费、开源、快速 嵌入式的数据库服务器,支持集群 提供JDBC、ODBC访问接口

什么是Mockito

Mockito是一个非常优秀的模拟框架,可以使用它简洁的API来编写漂亮的测试代码,它的测试代码可读性高同时会产生清晰的错误日志。

Velocity是一个基于Java的模板引擎,可以通过特定的语法获取在java对象的数据 , 填充到模板中,从而实现界面和java代码的分离!

_PostgreSQL_是一种特性非常齐全的自由软件的对象-关系型数据库管理系统(ORDBMS)

java连接mysql数据库

MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一

2、从哪里开始?

用于通过字节流|字符流构建 SqlSessionFactory

内部构建过程:

  • 通过流获取相应的 XMLConfigBuilder
  • XMLConfigBuilder解析返回 Configuration
  • 根据配置构建出 SqlSessionFactory
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
     if (reader != null) {
       reader.close();
     }
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.

    }
  }
}

这个类可以被实例化、使用和丢弃, 一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但 最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情

通过一些配置去创建SqlSession,例如:autoCommit|Connection|TransactionIsolationLevel|ExecutorType

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

使用 MyBatis 的主要 Java 接口。通过此接口,您可以执行命令、获取映射器和管理事务

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

// 以下非全部方法,只是作为举例
public interface SqlSession extends Closeable {
         T selectOne(String statement, Object parameter);
       List selectList(String statement, Object parameter);
       List selectList(String statement, Object parameter, RowBounds rowBounds);
         Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
       Cursor selectCursor(String statement, Object parameter, RowBounds rowBounds);
      void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
      int insert(String statement, Object parameter);
      int update(String statement, Object parameter);
      int delete(String statement, Object parameter);
      void commit(boolean force);
      void rollback(boolean force);
       T getMapper(Class type);
      Connection getConnection();
}

绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession,如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

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

所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

3、缓存

Cache:缓存的顶级接口,定义了缓存的增删改查、获取大小,id等行为

PerpetualCache:在MyBatis里面,它是一级缓存、二级缓存的最基本实现 (详细参见),一下是所有特性的缓存模式均在包: org.apache.ibatis.cache.decorators下:

BlockingCache:读阻塞缓存,对应的key没有被缓存的话,将会 阻塞所有需要获取该key数据(也可以设定超时时间) 的线程,直到有其他线程在缓存中设置该key数据后,阻塞线程将继续执行后续

FifoCache:固定大小队列缓存,维护了一个队列,可以设置队列大小(默认1024个位置),如果新进来一个key值,使得缓存集合中长度大于设定的长度,则将根据先进先出(first in, first out)原则,删除最先进来的key-value键值对儿,此队列只与put方法超过长度时有关,删除缓存元素不影响队列中的元素

LoggingCache:记录缓存命中率日志缓存,获取缓存的命中率日志(命中缓存key的个数与请求缓存总数的比率)

LruCache:最近最少使用的缓存,内部维护了一个LinkedHashMap,通过removeEldestEntry方法找到最近最老的key进行删除

ScheduledCache:定时清空缓存,在增删改查入口做了校验,该校验用于检查定时时间是否超过设定预期时间,超过则清空缓存数据

SerializedCache:序列化缓存,将key对应的value值以对象流的形式存于缓存中

SoftCache:软引用缓存,每次操作都会对应的将原来已被垃圾收集器收集过的对象数据删除,put操作会put一个软引用数据缓存

SynchronizedCache:同步缓存,内部几乎所有方法均加锁,是一个线程安全的缓存

TransactionalCache:事务性缓存,内部有两个方法,commiit以及rollback,在没有commit之前将所有key-value键值对临时存于一个内部map中,commit之后,将临时map以及get的时候的key空数据存储与真正的缓存中

WeakCache:若引用缓存,没什么可解释的

让每一种特性的缓存都实现了事务的功能

内部维护了一个map

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

put是为传入的类型的缓存设置数据,在commit提交方法时,对每中特性缓存实例中的数据轮循做一次提交

public void putObject(Cache cache, CacheKey key, Object value) {
  getTransactionalCache(cache).putObject(key, value);
}
private TransactionalCache getTransactionalCache(Cache cache) {
    return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
}
public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
}
public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
}

构造缓存用的,内部维护了一个 implementation(默认是:PerpetualCache), decorators(默认只有一个LruCache)

public class CacheBuilder {
  private final String id;
  private Class implementation; // 构造缓存时,基于该缓存实例构造的(将该缓存类型传入所有装饰者的构造函数中)
  private final List> decorators;// 缓存类型的集合
  private Integer size;// 设置缓存大小(像fifo缓存以及Lru算法缓存需要根据size这个去判断是否需要删除元素)
  private Long clearInterval; // 设置清理间隔(如果有定时清空类型的缓存时)
  private boolean readWrite; // 暂时没有用到,Cache顶级类中有一个获取读写锁的方法,可能与这个有关
  private Properties properties;// 根据这个属性去填充各个类型缓存的字段数据
  private boolean blocking;// 是否阻塞(阻塞式缓存用到的)

  public CacheBuilder(String id) {
    this.id = id;
    this.decorators = new ArrayList<>();
  }
}

总结:这里Mybatis在缓存的设计上用了装饰器模式、抽象工厂模式

Original: https://www.cnblogs.com/lishanbiaosMark/p/16344093.html
Author: 码出新生活!
Title: Mybatis源码分析(1)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/579720/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 判断是实数的正则表达式

    没啥好说的,想找个能判断字符串是实数的正则表达式网上找了好久,都有各种问题,终于被我试出来一个正确的了,记录一下 ^(-?\d+)(.\d+|\d+)?$ 什么?你问我什么是实数?…

    Java 2023年6月9日
    072
  • java基础-常用类

    以下为本人的学习笔记 1、内部类 分类: 1.内部类:成员内部类,静态内部类, 局部内部类(定义在方法内部),匿名内部类 概念:在一个类的内部再定义一个完整的类(类中类) 特点: …

    Java 2023年6月15日
    075
  • JIRA对接钉钉群机器人-实现任务的自定义格式指派通知

    一、前提 Jira Software、钉钉群、RESTful服务、LDAP服务 二、流程图 三、对接步骤 1、创建项目群,把相关人员拉入群 2、钉钉群的智能群助手里添加自定义机器人…

    Java 2023年6月5日
    0117
  • Spring Cloud Eureka 服务注册中心怎么配置

    「Spring Cloud Eureka 入门系列」Spring Cloud Eureka 入门 (一)服务注册中心详解Spring Cloud Eureka 入门 (二)服务提供…

    Java 2023年5月30日
    062
  • 1、包装类

    包装类 Wrapper 针对八种基本数据类型相应的引用类型——包装类 后六个父类是 Number基本数据类型 包装类 boolean Boolean char Character …

    Java 2023年6月7日
    072
  • html学习笔记

    结构化标准语言(HTML、XML) 表现标准语言(CSS) 行为标准(DOM、ECMAScript) 网页基本标签 标题标签:到 段落标签: 换行标签: 水平线标签: 字体样式标签…

    Java 2023年6月5日
    060
  • 从华为离职了

    遗憾的是,我转正后看到了大家的能力和努力,也意识到在预期的时间内难以达到我想要的高度,最终经过各方面的考虑,决定放弃这个职位,重新回到外企找回适合我的节奏。 依依不舍的离职后,回想…

    Java 2023年6月15日
    052
  • 常用API(Java)

    Object 场景:当我们使用toString方法想要输出对象变量时,官方提供的toString方法会直接输出对象所在的地址,而不是我们想要的对象变量,所以我们要把toString…

    Java 2023年6月6日
    074
  • Guava常用工具类总结

    === -"我想写得更优雅,可是没人告诉我怎么写得更优雅" -"Null的含糊语义让人很不舒服。Null很少可以明确地表示某种语义,例如,Map.ge…

    Java 2023年6月13日
    077
  • [Java编程思想] 第六章 访问权限控制

    6.1 包:库单元 包内含有一组类,它们在单一的名字空间之下被组织在了一起。 当编写一个Java源代码文件时,此文件通常被称为编译单元。每个编译单元都必须有一个后缀名.java,而…

    Java 2023年6月5日
    067
  • Java如何实现消费数据隔离?

    我是3y,一年 CRUD经验用十年的 markdown程序员👨🏻‍💻常年被誉为优质八股文选手 今天继续更新austin项目,如果还没看过该系列的同学可以点开我的历史文章回顾下,在看…

    Java 2023年6月9日
    070
  • Python面向对象

    1.面向对象 2.什么是类和类变量? 3.实例和实例化以及实例变量 4.数据成员 5.方法和静态方法以及类方法 6.什么是方法重写 7. _ init _ 8.self 9.类的初…

    Java 2023年6月7日
    076
  • 常用正则表达式

    一、校验数字的表达式 数字:^[0-9]*$ n位的数字:^\d{n}$ 至少n位的数字:^\d{n,}$ m-n位的数字:^\d{m,n}$ 零和非零开头的数字:^(0|[1-9…

    Java 2023年6月7日
    073
  • Java中float与 double的区别

    float是单精度浮点类型32位,即float占用4个字节的存储空间,精度是8位有效数字,指数段有8bits,指数范围为[-127,127] 。 2^127约等于1.7*10^38…

    Java 2023年6月5日
    054
  • nginx之外的web 服务器caddy

    caddy比nginx的不同: 另外,浏览器通过https连接本地/内部的https网页时,chrome会提示安全问题,此时可以设置将它加入例外,但还有个更简单的方法,在chrom…

    Java 2023年5月30日
    065
  • Redis高并发分布式锁详解

    为什么需要分布式锁 1.为了解决Java共享内存模型带来的线程安全问题,我们可以通过加锁来保证资源访问的单一,如JVM内置锁synchronized,类级别的锁ReentrantL…

    Java 2023年6月16日
    061
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球