Mybatis原理

一. 为什么要使用Mybatis?

1.1 jdbc的使用步骤
首先,在pox.xml中引入MySQl驱动的依赖
第一步, Class.forName 注册驱动
第二步,获取一个Connection。
第三步,创建一个Statement对象。
第四步,execute()方法执行SQL。execute()方法返回一个ResultSet结果集。
第五步,通过ResultSet获取数据,给POJO的属性赋值。
最后,关闭数据库相关的资源,包括ResultSet,Statement,Connection

1.2 jdbc使用存在哪些问题?
重复代码
资源管理
结果集处理
SQL耦合(处理业务逻辑和处理数据的代码是耦合在一起的)

1.3 jdbc问题的解决
Apache在2003年Commons DbUtils工具类,简化对数据库的操作。
DbUtils提供了一个QueryRunner类,它对数据库的增删改查的方法进行了封装。

在这里插入图片描述

private static QueryRunner queryRunner;

在QueryRunner的构造函数里面,可以传入一个数据源,比如这里用Hikari,这样我们就不需要再去写各种创建和释放连接的代码了。

queryRunner = new QueryRunner(dataSource);

通过这个工具类,提供获取QueryRunner实例的方法。
方法封装解决了重复代码的问题,出入数据源解决了资源管理的问题。
怎么把结果集转换为对象呢?比如转换成POJO或者List或者Map? 肯定不能一个属性去set或者put .
我希望做到的是,只要指定一个类型,它就可以自动把结果集给我转换为这种类型。
为了避免给每种类型创建要给自动转换类,在DbUtils里面提供了一系列的支持泛型的ResultSetHandleer,比如:用来把结果集转换为JavaBean的,转换为List的,转换为Map的 等等。
在这里插入图片描述
只要在DAO层调用QueryRunner封装好的查询方法,传入一个指定了类型的Handler,它就可以自动把结果集转换为实体类Bean或者List或者Map。
com.gupaoedu.dbutils.dao.BlogDao.java
比如:传入一个BeanHandler或者BeanListHandler;
在这里插入图片描述
com.gupaoedu.dbutils.QueryRunnerTest测试一下,结果:
BlogDto{bid=3,name=‘丘山一郎’,quthorId=‘null’}

实现的原理

return rs.next()?this.convert.toBean(rs,this.type):null;
最后到了populateBean():
在这里插入图片描述
在这里插入图片描述
通过for循环,把rs的值填充到了指定的类型属性中。
输出的结果中,authorId为什么是空的?
这种自动映射,要求数据库的字段跟对象的属性名称完全一致,才可以实现自动映射。

spring-jdbc
除了DbUtils之外,Spring也对原生的JDBC进行了封装。
1.代码重复——Spring提供了一个模板方法JdbcTemplate,里边封装了各种各样的execute,query,和update方法。
JDBCTemplate这个类(类的注释)
它是JDBC的核心包装类。简化了JDBC的使用,可以避免常见的异常。它封装了JDBC的核心流程,应用只要提供SQL,提取结果集就可以了。它是线程安全的。
初始化的时候可以设置数据源,所以资源管理的问题也可以解决。

public JdbcTemplate(DataSource dataSource){
   setDataSource(dataSource);
   afterPropertiesSet();
}

对于结果集的处理,Spring JDBC提供了一个RowMapper接口,可以把结果集转换为Java对象,它作为JdbcTemplate的参数使用。
比如:要把查询tbl_emp表得到的结果集转换为Employee对象,就可以针对一个Employee创建一个RowMapper对象,实现RowMapper接口,并且重写mapRow()方法。在mapRow()方法里面完成对结果集的处理。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

DbUtils 和 Spring JDBC ,这两个对JDBC做了轻量级封装的框架,或者说 工具类里边,帮我们解决的问题:
在这里插入图片描述
在这里插入图片描述

二,ORM关系型数据库 ,什么是ORM?为什么叫ORM?

ORM的全称是Object Relaional Mapping,也就是对象与关系的映射,对象是程序里边的对象,关系是它与数据库里面的数据的关系。也就是说,ORM框架帮助我们解决的问题是程序对象和关系型数据库的相互映射的问题。
Hibernate是要给很流行的ORM框架,2001年的时候就出了第一个版本。在使用Hibernate的时候,我们需要为实体类建立一些hbm的xml映射文件。
然后通过Hibernate提供(session)的增删改查的方法来操作对象。
Session相关方法也可以通过继承JpaRepository活得,无需手动创建。
在这里插入图片描述

操作对象根操作数据库的数据一样。Hibernate的框架内会自动帮我们生成SQL语句(可以屏蔽数据库差异),自动进行映射。这样我们呢的代码变得简洁了,程序的可读性也提高了。

总结Hibernate的特性:
1,根据数据方言自动生成SQL,移植性好;
2,自动管理理解资源(支持数据源);
3,实现了对象和关系型数据库的完全映射,操作对象就像操作数据库记录一样;
4,提供了缓存功能机制。

存在的问题:
但是Hibernate在业务复杂的项目中使用也存在一些问题:
1,比如使用get(),update(),save()对象的这种方式,实际操作的是所有字段,没有办法指定部分字段,换句话说就是不够灵活。
2,自动生成SQL的方式,如果要基于SQL去做一些优化的话,是非常困难的,也就是说可能出现性能的问题。
3,不支持动态SQL,比如分表中的表名,条件,参数变化等,无法根据条件自动生成SQL。
因此我们需要一个更加灵活的框架。

三,Mybatis的核心特性解决了那些问题?

  • 使用连接池对连接进行管理
  • SQL和代码分离,集中管理
  • 结果集映射
  • 参数映射和动态SQL
  • 重复SQL的提取
  • 缓存管理
  • 插件机制

四,Mybatis核心组件及其生命周期

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

核心配置:
在这里插入图片描述在这里插入图片描述
特殊的类型:typeHandlers
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

objectFactory
当我们把数据库返回的结果集转换为实体类的时候,需要创建对象的实例,由于我们不知道需要处理的类型是什么,右哪些属性,所以不能用new的方式去创建。只能通过反射来创建。

在这里插入图片描述
在这里插入图片描述
如果想要修改对象工厂在初始化实体类的时候的行为,就可以通过创建自己的对象工厂,继承DefaultObjectFactory来实现(不再需要实现ObjectFactory接口)。
例如:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

plugins

在这里插入图片描述
在这里插入图片描述

五,Mybatis动态SQL

在这里插入图片描述
为什么要用批量操作?

提升了写的效率,原因是:减少了跟数据库交互的次数,并且避免了开启和结束事务的时间消耗。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六,嵌套查询/N+1问题/延迟加载

在这里插入图片描述
在这里插入图片描述
association和collection的区别:
association是用于一对一和多对一,而collection是用于一对多的关系。

嵌套查询会存在N+1问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.没有开启延迟加载的开关(lazyLoadingEnabled没有配置成true),会连续发送两次查询。
2.开启了延迟加载的开关,调用blog.getAuthror()以及默认的(equals,clone,hashCode,toString)时才会发起第二次查询,其他方法并不会触发查询,比如 blog.getName(); (这里是bolg和作者关系是一对一,所以只有开启懒加载,并且使用到作者的时候才会用到)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

逻辑分页与物理分页
在我们查询数据库的操作中,有两种翻页方式,一种是逻辑翻页(假分页),一种是物理翻页(真分页)。逻辑翻页的原理是把所有数据查出来,在内存中删选数据。物理分页是真正的分页,比如MySQL使用limit语句,Oracle使用rownum语句,SQL Server使用top语句。
逻辑翻页:
Mybatis里边有一个逻辑分页对象RowBounds,里边主要有两个属性,offset和limit(从第几条开始,查询多少条)。我们可以在Mapper的方法上加上这个参数,不需要修改Xml里边的SQL语句。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Mybatis-Plus
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

七, Mybatis常见的问题

用注解还是用Xml配置?
在这里插入图片描述
Mapper接口无法注入或Invalid bound statement (not found)
在这里插入图片描述
怎么获取传入的最新自动生成的ID?
在这里插入图片描述
什么时候用#{},什么时候用${}?
在这里插入图片描述
在这里插入图片描述

XML中怎么使用特殊符号,如果小于&
在这里插入图片描述

如何实现模糊查询Like?
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

二,Mybatis的一级缓存与二级缓存以及插件
单条数据操作->动态标签(多条数据)-> 批量查询(batch execute) ->缓存

八, Mybatis的主要工作流程分析

2.1.1 解析配置文件
首先在Mybatis启动的时候我们呢要去解析配置文件,包括全局配置文件和映射器配置文件,这里边包含了控制Mybatis的行为,和我们要对数据库下达的指令。
也就是我们的SQL信息,我们会把它们解析称一个Configuration对象。
2.1.2 提供操作接口
在这里插入图片描述
2.1.3,执行SQL操作
在这里插入图片描述
在这里插入图片描述
2.2 mybatis架构分层
在这里插入图片描述
接口层:
首先接口层是我们打交道最多的。核心对象是SqlSession,它是上层应用和Mybatis打交道的桥梁,SqlSession上定义了非常多的对数据库的操作方法。接口层在接收到调用请求的时候,会调用核心处理层的相应模块来完成具体的数据库操作。

核心处理层
在这里插入图片描述

基础支持层:
在这里插入图片描述
2.3MyBatis缓存详解
cache 缓存
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一级缓存(本地缓存)介绍
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一级缓存的不足
使用一级缓存的时候,因为缓存不能跨会话共享,不同的会话之间对于相同的数据可能有不一样的缓存。存在多个会话或者分布式环境下,会存在查到过时数据的问题。如果要解决这个问题,就要用到工作范围更广的二级缓存。

二级缓存

在这里插入图片描述
在这里插入图片描述
实际上MyBatis用了装饰器的类来维护,就是CachingExecutor。如果启用了二级缓存,MyBatis在创建Executor对象的时候会对Executor进行装饰。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
只要没有显式地设置cacheEnabled = false,都会用 CachingExecutor装饰基本的执行器(SIMPLE,REUSE,BATCH)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
验证二级缓存
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
什么时候开启二级缓存?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

八,Mybatis源码分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3.1配置解析过程
在这一步,我们主要完成了config配置文件,Mapper文件,Mapper接口中注解的解析。得到了最重要的对象configuration,这里面存放了全部的配置信息,它在属性里面还有各种各样的容器,最后返回一个DefaultSqlSessionFactory,里边持有Configuration的实例
在这里插入图片描述
在这里插入图片描述

具体:XmlConfigurationBuilder是继承抽象类BaseBuilder的一个子类,用来解析全局配置文件,针对不同的构建目标还有其他的一些子类(关联到源码路径),比如:XMLMapperBuilder:解析Mapper映射器 XMLStateMentBuilder:解析增删改查标签 XMLScriptBuilder:解析动态SQL。解析过程会获取 typeHandler类型映射关系,xml配置和注解两种方式的Mapper信息,约等于扫描,把别名信息typeAliases,数据库配置等赋值到Configuration中。 把实现了Interceptor接口,所以这一步做的事情就是把插件解析成Interceptor类,设置属性,然后添加到Configuration的InterceptorChain属性里面,interceptorChain是一个List。

3.2 会话创建过程
创建会话的过程,实际通过DefaultSqlSessionFactory 获得了一个DefaultSqlSession,里面包含了一个Executor,Executor是SQL的实际执行对象。
Executor中既要执行执行器类型和事务类型。

事务工厂类型可以配置为JDBC和MANAGED
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

执行器可以分为:
Executor的基本类型有三种:SIMPLE,BATCH,REUSE,默认是SIMPLE。

BatchExecutor : 通过批量操作来提高性能。(执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。)

ReuseExecutor: 重复使用执行,其定义了一个Map<String, Statement>,将执行的sql作为key,将执行的Statement作为value保存,这样执行相同的sql时就可以使用已经存在的Statement,就不需要新创建了。(执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。)

SimpleExecutor通过类名可以看出,它是一个简单的执行类,并不会做一些处理就执行sql。(每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象)。

CacheExecutor有一个重要属性delegate,它保存的是某类普通的Executor,值在构照时传入。执行数据库update操作时,它直接调用delegate的update方法,执行query方法时先尝试从cache中取值,取不到再调用delegate的查询方法,并将查询结果存入cache中。(上述三种模式的装饰器模式)。

创建Executor对象:
(1)创建执行器
(2)缓存装饰
如果 cacheEnabled=true,会用装饰器模式对executor进行装饰。
在这里插入图片描述

(3)插件代理
装饰完毕后,会执行:
在这里插入图片描述
此处会对executor植入插件逻辑

在这里插入图片描述

(4)返回SqlSession实现类
最终返回DefaultSqlSession,它的属性包括Configuration,Executor对象。
在这里插入图片描述

3.3 获取Mapper的过程
获得Mapper对象的过程,实质上是获取了一个JDK动态代理对象(类型是$Proxy数字)。这个代理类会继承Proxy类,实现被代理的接口,里边持有了一个MapperProxy类型的触发管理类。

3.4 执行SQL
由于所有的Mapper都是JDK动态代理对象,所以任意的方法都是执行触发管理类MapperProxy的invoke()方法。

在这里插入图片描述

以查询为例子:首先是查询缓存,
MapperMethod.execute();
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

selectOne底层调用的是selectList方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
查询二级缓存的时候,根据 查询cacheKey 根据 方法+翻页偏移相同+SQL相同+参数值相同+数据源环境相同,所以被认为是同一个查询,如果存在则直接返回。首先从MappedStatement中取出cache对象,判断cache对象是否为空,如果为空,则没有查询到到二级缓存,写入二级缓存流程。

在这里插入图片描述

查询一级缓存
再查一级缓存,如果一级缓存,如果flushCache=true的时候,会先清理本地缓存,或者栈为空,则清空缓存。
在这里插入图片描述
先在缓存用占位符占位。执行查询后,移除占位符,放入数据。
在这里插入图片描述
数据库查询,创建StateMentHandle
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值