MyBatis

mybatis相比拟传统jdbc

  1. 传统数据库配置信息存在硬编码,包含sql语句,设置参数,获取后果集须要手动封装后果集,较为繁琐。
  2. 须要频繁创立数据库连贯。

解决方案:

  1. 配置文件解决硬编码,数据库连贯应用连接池。
  2. 后果集能够应用反射。

解析数据库配置 mapper.xml中的sql

sqlsession在getMapper时 应用动静代理生成代理对象(其中外部类中蕴含invoke办法),代理对象在调用接口中的任意办法都会执行invoke办法.这样就能够吧创立数据库连贯放在外面

为什么myabtais mapper中的namespace与 id 要与接口名与办法名雷同?

因为在通过动静代理时只有依据办法能力获取到以后的类名曾经办法名,而这两个组成成为了sql mapper的惟一标识,才晓得执行哪个语句,说白了就是为了做辨别. 在做形象封装是达成的约定.

mybatis:

基于ORM半自动轻量级、长久层框架(对象关系映射)

为什么调用办法是能够间接返回对象.

从技术层面来说是因为用了反射,但实际上是因为做了束缚 实体中的字段与数据库字段做了映射关系,利用这层关系,实现后果集封装.

为什么是半自动,全自动(hibernate).不必sql语句的话,没法优化.

轻量级: 占用资源少

底层还是jdbc的封装,躲避了常见的问题

API

传统开发方式

  1. Resoures工具类加载配置文件SqlConfigMap
  2. SqlSessionFactoryBuilder解析创立SqlSessionFactory
  3. SqlSessionFactory生产一个SqlSession

    openSession() 默认开启一个事务,不会主动提交,须要手动提交

    openSession(true)则默认主动提交

  4. SqlSession调用办法

代理开发方式

Mapper 接⼝开发须要遵循以下标准:

  1. Mapper.xml⽂件中的namespace与mapper接⼝的全限定名雷同
  2. Mapper接⼝⽅法名和Mapper.xml中定义的每个statement的id雷同
  3. Mapper接⼝⽅法的输⼊参数类型和mapper.xml中定义的每个sql的parameterType的类型雷同
  4. Mapper接⼝⽅法的输入参数类型和mapper.xml中定义的每个sql的resultType的类型雷同

通过sqlsession.getMapper(dao.class).获取代理对象

Mybatis缓存

一级缓存

介绍

一级缓存是sqlsession级别的缓存,通过hashmap存储缓存对象,不同sqlsession之间的缓存数据区不受影响,一级缓存默认开启

  • 先去一级缓存中查问,如果没有就查询数据库,并进行一级缓存

    cacheKey: statementId,params,bonundsql,rowbounds(分页参数[两个])组成

    如果配置文件中配置了Environment,则会增加Environment ID

    value:查问后果

  • 第二次查问间接命中

    能够通过比拟后果地址验证。缓存的是对象
  • 如果两头有commit操作,会清空一级缓存。防止脏读。

    除了commit 也能够手动调用sqlsession.clearCache或者close办法手动刷新一级缓存。

源码

数据结构HashMap

二级缓存

⼆级缓存是基于mapper⽂件的namespace,多个sqlsession共享二级缓存区域.二级缓存须要手动开启.

多个sqlsession中如果产生commit操作会清空二级缓存.

二级缓存缓存的是数据并不是对象.

开启形式

xml开发

全局配置文件sqlMapConfig中

<!--开启⼆级缓存--><settings> <setting name="cacheEnabled" value="true"/></settings>

其次在UserMapper.xml⽂件中开启缓存

<!--开启⼆级缓存--><cache></cache>
注解开发

应用@CacheNamespace开启二级缓存

usecache(默认为false)和flushcache(默认为true)

xml:能够在单个statement中配置应用

注解: 增加@options注解

源码:

底层数据结构还是hashmap

存在的问题
  1. 二级缓存是单服务器工作,无奈实现共享所以无奈实现分布式缓存.
解决办法

redis、memcached、ehcache

自定义二级缓存

mybatis提供了redis实现类,能够间接应用mybatis-redis依赖

架构原理

三层构造

  1. API接口层

    提供给内部使⽤的接⼝ API,开发⼈员通过这些本地API来操纵数据库。接⼝层⼀接管 到 调⽤申请就会调⽤数据处理层来实现具体的数据处理

  2. 数据处理层

    负责具体的SQL查找、SQL解析、SQL执⾏和执⾏后果映射解决等。它次要的⽬的是根 据调⽤的申请实现⼀次数据库操作

  3. 根底撑持

    包含连贯治理、事务管理、配置加载和缓存解决,这些都是共⽤的东⻄,将他们抽取进去作为最根底的组件。为下层的数据处理层提供最根底的⽀撑

总体流程

  1. 加载配置初始化

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

  2. 接管调⽤申请

    为SQL的ID和传⼊参数对象

  3. 解决操作申请

    为SQL的ID和传⼊参数对象

    • 依据SQL的ID查找对应的MappedStatement对象。
    • 依据传⼊参数对象解析MappedStatement对象,失去最终要执⾏的SQL和执⾏传⼊参数。
    • 获取数据库连贯,依据失去的最终SQL语句和执⾏传⼊参数到数据库执⾏,并失去执⾏后果。
    • 依据MappedStatement对象中的后果映射配置对失去的执⾏后果进⾏转换解决,并失去最终的处 理 后果。
    • 开释连贯资源。
  4. 返回处理结果

    将最终的处理结果返回。

源码剖析

sqlsession 线程不平安的,应用后须要敞开close()

  1. 读取配置文件以及注解,读成输出流
  2. 解析配置文件封装configuration对象,创立defaultsqlsessionfactory
  3. openSession :创立了DefaultSqlsession实例对象并且设置了事务不主动提交.以及实现executor对象创立
  4. sqlsession.selectlist依据statementid从configuration中的map汇合中获取到mappedstatement

二级缓存

先执行二级缓存而后一级缓存而后数据库

开启二级缓存后走的就是CachingExecutor

先查问二级缓存 如果没有的话会调用delegate(这里的delegate此时是BaseExecutor的实现类 simpleExecutor)去执行query 而执行query的时候会查问一级缓存,而后再查数据库 ,此时会保留一级缓存,而后保留二级缓存,然而在保留二级缓存的时候还没有真正的去存cache对象,而是放到一个hashmap汇合中 (这个汇合的key是缓存对象,value是事务缓存),存储的都是事务没提交的缓存汇合。为什有事务因为二级缓存是从MappedStatement中获取,而后这玩意存在全局配置中,能够多个CachingExecutor取到,会呈现线程平安问题.并且多个事务共用一个缓存实例会导致脏读.所以就有了针对缓存的事务管理器 tcm(TransactionalCacheManager).

然而在第一步查问二级缓存的时候是间接取的真正缓存对象,所以两次查问之间须要用commit来提交下面 没提交的事务的缓存以此保留缓存

    Cache cache = ms.getCache();    if (cache != null) {      flushCacheIfRequired(ms);      if (ms.isUseCache() && resultHandler == null) {        ensureNoOutParams(ms, boundSql);        @SuppressWarnings("unchecked")        List<E> list = (List<E>) tcm.getObject(cache, key);        if (list == null) {          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);          tcm.putObject(cache, key, list); // issue #578 and #116        }        return list;      }    }    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
存储⼆级缓存对象的时候是放到了TransactionalCache.entriesToAddOnCommit这个map中,然而每 次查问的时候是间接从TransactionalCache.delegate中去查问的,所以这个⼆级缓存查询数据库后,设 置缓存值是没有⽴刻⽣效的,次要是因为间接存到 delegate 会导致脏数据问题

总结

  • ⼆级缓存实现了Sqlsession之间的缓存数据共享,属于namespace级别
  • ⼆级缓存具备丰盛的缓存策略。
  • ⼆级缓存可由多个装璜器,与根底缓存组合⽽成
  • ⼆级缓存⼯作由 ⼀个缓存装璜执⾏器CachingExecutor和 ⼀个事务型预缓存TransactionalCache 实现。

提早加载

原理动静代理 javasist实现

因为sqlsession查问返回的对象是个代理对象.在调用代理对象属性的getter办法时 会进入拦截器的invoke办法,如果须要提早加载则会查问。

本文由博客一文多发平台 OpenWrite 公布!