Mybaits
ORM 是什么(思想)
对象关系映射(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。
MyBatis与Hibernate区别
-
hibernate是全自动,而mybatis是半自动。
hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。而mybatis仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写sql来实现和管理。
-
hibernate数据库移植性远大于mybatis。
hibernate通过它强大的映射结构和hql语言,大大降低了对象与数据库(oracle、mysql等)的耦合性,而mybatis由于需要手写sql,因此与数据库的耦合性直接取决于程序员写sql的方法,如果sql不具通用性而用了很多某数据库特性的sql语句的话,移植性也会随之降低很多,成本很高。
-
hibernate拥有完整的日志系统,mybatis则欠缺一些。
hibernate日志系统非常健全,涉及广泛,包括:sql记录、关系异常、优化警告、缓存提示、脏数据警告等;而mybatis则除了基本记录功能外,功能薄弱很多。
-
mybatis相比hibernate需要关心很多细节
hibernate配置要比mybatis复杂的多,学习成本也比mybatis高。但也正因为mybatis使用简单,才导致它要比hibernate关心很多技术细节。mybatis由于不用考虑很多细节,开发模式上与传统jdbc区别很小,因此很容易上手并开发项目,但忽略细节会导致项目前期bug较多,因而开发出相对稳定的软件很慢,而开发出软件却很快。hibernate则正好与之相反。但是如果使用hibernate很熟练的话,实际上开发效率丝毫不差于甚至超越mybatis。
-
sql直接优化上,mybatis要比hibernate方便很多
由于mybatis的sql都是写在xml里,因此优化sql比hibernate方便很多。而hibernate的sql很多都是自动生成的,无法直接维护sql;虽有hql,但功能还是不及sql强大,见到报表等变态需求时,hql也歇菜,也就是说hql是有局限的;hibernate虽然也支持原生sql,但开发模式上却与orm不同,需要转换思维,因此使用上不是非常方便。总之写sql的灵活度上hibernate不及mybatis。
-
总结:
mybatis:小巧、方便、高效、简单、直接、半自动
hibernate:强大、方便、高效、复杂、绕弯子、全自动
Mybaits和JDBC对比
- MyBatis是一个优秀的持久层框架,它对JDBC操作数据库的过程进行了封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建连接、创建执行语句、手动设置参数和结果集检索等繁杂的过程。
- Mybatis通过封装JDBC解决了原生jdbc在编程中出现的问题 数据库连接池。我们可以在MyBatis的全局配置文件SqlMapConfig.xml中配置数据库连接池,使用它来管理数据库连接,这样就可以避免由于频繁地创建和释放数据库连接而造成的资源浪费。
- MyBatis使用动态SQL技术解决了JDBC编程中存在的SQL硬编码的问题。
- MyBatis通过输入映射技术将Java对象映射至SQL语句,SQL语句可以通过使用该Java对象取出查询参数值,简化了JDBC中设置查询参数的过程。
- MyBatis通过输出映射技术将SQL的执行结果映射至Java对象,省去了JDBC编程中对结果集检索的过程。
Mybatis 配置步骤
为什么需要使用到外部的properties文件
外部的properties文件和内部的datasource标签中的配置是可以叠加使用的.并且datasource中的配置的优先级是最高的.
Mapper代理开发
原理
-
加载mybatis-config.xml文件,进行初始化的配置.
[Configuration对象以及MappedStatement语句对象]
-
设置普通信息以及注册mapper
-
配置的是扫包 - 所有的XXXMapper.java类 - > 获取到接口的名称
由于mapper代理开发方式 - 接口的名称和xml的名称保持一致.
由接口的名称找到对应的映射文件,比如UserMapper.java->UserMapper.xml
因此:位置应该是出于同一个目录中. - 找到之后加载到内存(配置文件)
-
创建一个MapperRegistry - mapper注册器 - 注册mapper
-
创建一个MapperProxyFactory - mapper代理工厂 - 负责创建mapper接口的代理实现类.
mapper代理开发规范
-
mapper接口和mapper映射文件必须在同一个目录中
-
映射文件的namespace属性的值必须是mapper接口的全限定名
-
映射文件中的每条sql语句的id属性的值必须要和mapper接口中的方法的名称高度保持一致.
-
映射文件的名称和映射的接口名称高度保持一致
什么是Mybatis的接口绑定(mapper代理的开发方式),有哪些注意点,有什么好处
- 接口映射就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置.
接口绑定有几种实现方式,分别是怎么实现的?
- 接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select@Update等注解里面包含Sql语句来绑定,另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.
Mybatis接口中参数传递方式(三种)
- @Param注解,第三方实体类,map集合
查询出来的列名和实体类名称不一致怎么办
- 使用ResultMap映射,取别名
#{}和${}符号的区别
- #{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换, 表 示 拼 接 s q l 串 , 通 过 {}表示拼接sql串,通过 表示拼接sql串,通过{}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换,
- #{}括号中可以是value或其它名称。${}括号中只能是value。
- ${}会导致SQL注入,#{}可以有效防止sql注入。
extends属性
简介:用来表示继承另外一个封装好的resultMap
resultMap, resultType
- 多表查询时, 关系中加载 "1"的一方用的是association ,加载"多"的一方用的是collection
- 在一对一查询中,如果查询出来的列名与某个类中的属性相对应,或者某个实体类中的属性包含查询出来的列名,则使用resultType实现较为简单
- 如果查询出来的列名和实体类名称不对应可以使用resultMap
- resultType无法实现延迟加载,但是resultMap可以实现
延迟加载
- Mybatis中的延迟加载是使用动态代理完成的,代理的对象往往是多的一方。
- 当查询订单时,每个订单都会有对应的用户信息,但是目前的查询到的订单信息已经可以满足业务需要,而对于对应的用户信息,当我们需要的时候再进行查询,通过按需查询用户的订单信息所进行的操作就是延迟加载
- 在mybatis中,只要对应的对象中包含equals,hashCode,clone,toString方法时,延迟加载就会失效。
<!-- 改变默认配置,使只有调用clone方法的时候才会触发完全加载
默认情况下,当对象调用了hashCode,equals,clone,toString()都会是延迟加载失效-->
<setting name="lazyLoadTriggerMethods" value="clone"/>
- 开启延迟加载:
- 在Mybaits的全局配置文件里面的settings标签里添加配置
<!--打开延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
- 延迟加载的条件
- resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
- 延迟加载的好处
- 先从单表进程查询,等到需要时再从关联表去关联查询,大大提高了查询性能,因为单表查询效率要比多表查询效率高的
悲观锁,乐观锁
- 悲观锁 假设会发生冲突,屏蔽一切违反数据完整性的操作
- 乐观锁 假设不会发生并发冲突,只有在数据提交的时候才会检查数据的完整性
缓存
一级缓存
- Mybatis默认开启一级缓存,不需要进行任何配置。
- 一级缓存的生命周期是属于SqlSession级别.
- 不同的sqlSession空间中的缓存区在一级缓存级别中,数据是不共享的.
- 好处:保证缓存数据中存储的是最新的信息,避免出现脏读现象
二级缓存
-
二级缓存默认是关闭的.需要在配置文件中手动进行开启.
-
二级缓存是跨sqlSession级别.二级缓存是作用在mapper级别的.
-
获取数据的时候,首先会放一份到一级缓存,然后再一份到二级缓存.
第二次再次获取相同数据的时候,优先肯定是先从一级缓存中去查询数据,如果一级缓存中没有,那么才会到二级缓存中去查询数据.如果还没有,才会和DB进行交互.
注意
调用sqlSession.clearCache()或者commit() - > 清空一级和二级缓存.
调用close() - > 仅仅是清空了一级缓存
缓存失效情况
- 不在同一个SQLSession对象中
- 执行语句的参数不同,缓存中也不存在数据
- 执行增,删,改,语句,会清空掉缓存
- 手动清空缓存数据
ehcache缓存和redis缓存的区别
- ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。
- redis是通过socket访问到缓存服务,效率比ecache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。
分页
方法
- 使用SQL语句中的limit进行分页,可以传入两个参数,一个是当前页,还有一个是每页显示的条数
- 使用Mybatis内置的分页插件PageHelper进行分页
物理分页和逻辑分页
- 物理分页 物理分页依赖的是某一物理实体,这个物理实体就是数据库,比如MySQL数据库提供了limit关键字,程序员只需要编写带有limit关键字的SQL语句,数据库返回的就是分页结果。
- 逻辑分页 逻辑分页依赖的是程序员编写的代码。数据库返回的不是分页结果,而是全部数据,然后再由程序员通过代码获取分页数据,常用的操作是一次性从数据库中查询出全部数据并存储到List集合中,因为List集合有序,再根据索引获取指定范围的数据。
PageHelper分页插件原理
- PageHelper的底层采用的是动态代理和拦截器实现分页。
- PageHelper拦截的是Executor的query方法,传参的核心原理是通过ThreadLocal进行的
- 当我们需要对某个查询进行分页的时候,可以在这个查询之前调用一次PageHelper.startPage()方法,这样PageHelper会把分页信息存储进一个ThreadLocal变量中,
- 在拦截到Executor的query方法执行时,会从对应的ThreadLocal中获取分页信息,获取到之后,则进程分页处理。然后在清空ThreadLocal中的分页信息。
- 因此当使用PageHelper.startPage()后,每次都是对最近的一次查询进行分页查询,如果下次还需要进行分页查询,需要重新进行一次PageHelper.startPage()方法,这样就可以做到对之前的查询代码没有任何的侵入性
- 此外进行分页查询时,返回的结果一般是一个List,PageHelper分页查询的结果会变成PageInfo,这个PageInfo中包含分页所需要的所有信息,包含每页显示的页数,当前页数,分页结果等。
Mybatis有几种执行器
- SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
- ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map
- BatchExecutor:完成批处理。