1、历史(百度百科)
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
- iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
2、作用 (百度百科)
- MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
3、说说持久化
持久化就是把数据存在磁盘而不是内存。
- 程序产生的数据首先都是在内存。
- 内存是不可靠的,他丫的一断电数据就没了。
- 那可靠的存储地方是哪里,硬盘、U盘、光盘等。
- 我们的程序在运行时说的持久化通常就是指将内存的数据存在硬盘。
- 硬盘用哪些地方能存?数据库文件、xml文件、反正你将来能将数据读出来的文件都行。
高级一点:持久化就是将数据从瞬时态转化为持久态,长久保存。
4、说说持久层
- 业务是需要操作数据的
- 数据是在磁盘上的
- 具体业务调用具体的数据库操作,耦合度太高,复用性太差
- 将操作数据库的代码统一抽离出来,自然就形成了介于业务层和数据库中间的独立的层
5、持久层框架
- 写过jdbc没,里边的重复代码太多了,我们在讲jdbc的时候尝试带大家利用反射写了个简单的baseDao,之后基本的crud就能不用写了,这就有了框架的影子了。
以下的代码,如果50个字段是不是写50次set
咱们写了basedao(mysql的jdbc讲解视频里有)
- 利用框架能够及其简单的帮助我们实现持久层的操作,从而规避了复杂且重复的操作。
- 实现了从实体类到数据库的映射关系,也就是orm映射。
6、mybatis的优点和缺点
-
sql语句与代码分离,存放于xml配置文件中:
优点:便于维护管理,不用在java代码中找这些语句;
缺点: JDBC方式可以用打断点的方式调试,但是Mybatis不能,需要通过log4j日志输出日志信息帮助调试,然后在配置文件中修改。
-
用逻辑标签控制动态SQL的拼接:
优点:用标签代替编写逻辑代码;
缺点:拼接复杂SQL语句时,没有代码灵活,拼写比较复杂。不要使用变通的手段来应对这种复杂的语句。
-
查询的结果集与java对象自动映射:
优点:保证名称相同,配置好映射关系即可自动映射或者,不配置映射关系,通过配置列名=字段名也可完成自动映射。
缺点:对开发人员所写的SQL依赖很强。
-
编写原生SQL:
优点:接近JDBC,比较灵活。
缺点:对SQL语句依赖程度很高;并且属于半自动,数据库移植比较麻烦,比如mysql数据库编程Oracle数据库,部分的sql语句需要调整。
-
最重要的一点,使用的人多!公司需要!但是应为用了反射,效率会下降,所有有些公司会使用原生的jdbc
7、代理设计模式
代理模式分为静态代理和动态代理。代理的核心功能是方法增强。
(1)静态代理
静态代理角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户 : 使用代理角色来进行一些操作 .
代码实现
写一个接口
定义男歌手
定义经纪人
Client . java 即客户
分析:在这个过程中,你直接接触的就是鹿晗的经济人,经纪人在鹿晗演出的前后跑前跑后发挥了巨大的作用。
优点
- 鹿晗还是鹿晗,没有必要为了一下前置后置工作改变鹿晗这个类
- 公共的统一问题交给代理处理
- 公共业务进行扩展或变更时,可以更加方便
- 这不就是更加符合开闭原则,单一原则吗?
缺点 :
- 每个类都写个代理,麻烦死了。
(2)动态代理
- 动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理–cglib(有兴趣自己研究)
- 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
- 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、
JDK的动态代理需要了解两个类
核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看
【InvocationHandler:调用处理程序】
【Proxy : 代理】
代码实现
抽象角色和真实角色和之前的一样!
还是歌星和男歌星
Agent. java 即经纪人
Client . java
核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!、
(3)万能代理(扩展,代理只有两种)
批量做代理,不用一个一个写
我们来使用动态代理实现代理我们后面写的UserService!
我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!
测试!
测试,增删改查,查看结果,依然可以
思考那我们其他所有的类是不是都可以了?
咱们的dao,service的类是不是都能代理了,批量去给这些方法加一些日志之类的是不是就可以了,或者统一加上开启事务,关系事务等。
(4)只有接口的dao
还是要传入一个代理对象,那么我们能不能直接根据接口生成个代理啊!
能
数据库相关
实体类
搞个接口
全国统一的插入代理
这个代理能针对所有的实体类(实体类和表名一致,字段名一致)生成对应的sql并形成代理。
简单感受即可,此代理功能简单不严谨。
以后有插入需求,只需要写个接口生成代理即可,测试
结果
真牛逼!!!!!
思考,比如有一个Admin的实体类和dao接口是不是也能进行插入啊!!!
1、建立数据库
2、编写实体类
自行学习lombok
3、maven配置
4、编写MyBatis核心配置文件,mybatis-config.xml
小知识
- DTD(document Type Definition)即文档类型定义,是一种XML约束模式语言,是XML文件的验证机制
- 一个DTD文档包含:(1)元素的定义规则;(2)元素间关系的定义规则;(3)元素可使用的属性,可使用的实体或符号规则
当然我们还有可能接触其他类型的头文件,比如XML Schemal语言就是XSD,XML Schema描述了XML文档的结构,可以用一个指定的XML Schema来验证某个XML
- XML Schema基于XML,没有专门的语法
- XML Schema可以象其他XML文件一样解析和处理
- XML Schema比DTD提供了更丰富的数据类型.
- XML Schema提供可扩充的数据模型。
- XML Schema支持综合命名空间
总之不管是dtd还是xsd文件都是用来约束我们的xml文件,保证我们的xml能够使用哪些标签,保证xml的有效性,在idea中工具还能根据头文件为我们提供强大的提示功能。
有兴趣的自行深入研究。
说明
第一次使用mybatis,要注意mybatis,需要两个文件
- 一个接口类mapper(就是咱们写的dao)java文件,不需要有实现
- 一个与接口对应的xml文件
- 两个文件名字最好一样,比如一个UserMapper.java一个UserMapper.xml
- 框架要根据mapper和xml联合,形成代理对象
xxxMapper基本格式
xxxmapper.xml需要在核心配置文件中注册
简单理解namespace就是 维护mapper的对应关系
配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,叫mapper或dao无所谓,但名字必须一致!
1、select(查询)
select标签是mybatis中最常用的标签
1、在UserMapper中添加对应方法
2、在UserMapper.xml中添加Select语句
知识点:
- resultType:制定返回类型,查询是有结果的,结果啥类型,你得告诉我
- parameterType:参数类型,查询是有参数的,参数啥类型,你得告诉我
- id:制定对应的方法,就是你的告诉我你这sql对应的是哪个方法
- #{id}:sql中的变量,要保证大括号的变量必须在User对象里有,
- 当然你可以使用map,这或啥也能干,这要你将来传进的map有id这个key就行
- #{}:占位符,其实就是咱们的【PreparedStatement】处理这个变量
除了#{}还有${},看看有啥区别,面试常问
-
#{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】
-
${} 的作用是直接进行字符串替换
3、测试类中测试
2、insert(插入)
insert标签被用作插入操作
1、接口中添加方法
2、xml中加入insert语句
3、测试
注:增、删、改操作需要提交事务!此处引出,mybatis其实设置了事务的手动提交,其实不管如何,都应该提交事务,那么我们可以增强一下测试类。
思考,如果参数没有传实体类而是传了多个参数,,能不能执行
比如数据库为id,方式传入userId
1、在UserMapper中添加对应方法
2、在UserMapper.xml中添加Select语句
3、测试
这就无法隐射了。
这就需要个注解了@Param
结论多个参数时,使用@Param注解,能制定对应的参数,当然如果能封装对象最好封装对象
3、update(修改)
update标签用于更新操作
1、写接口
2、写SQL
3、测试
4、delete(删除)
delete标签用于做删除操作
1、写接口
2、写SQL
3、测试
简单的梳理逻辑
- SqlSessionFactoryBuilder().build(inputStream)-读取核心配置文件,
- 核心配置文件负责核心配置,重要的一点是通过核心配置找到Mapper的xml文件。
- xml文件通过 namespace 又能找到 对应的接口文件。
- 有了方法,有了sql,会用动态代理,生成代理类,实现对象的方法。
- session.getMapper(UserMapper.class) 获取的就是代理对象,当然就有了对应的实现。
5、模糊查询
方案一:在Java代码中拼串
方案二:在配置文件中拼接
为什么必须用双引号?
6、map的使用
map可以代替任何的实体类,所以当我们数据比较复杂时,可以适当考虑使用map来完成相关工作
1、写sql
2、写方法
3、测试
7、别名小插曲
思考:
resultType写成java.util.HashMap,也行写成map也行
说明mybatis内置很多别名:
给自己类设定别名
在核心配置文件中加入
标签中 有 和 两个属性
填写 实体类的全类名, 可以不填,不填的话,默认是类名,不区分大小写,
填了的话就以 alias里的值为准。
标签 为某个包下的所有类起别名; 属性填写包名。 别名默认是类名,不区分大小写。
小结
- map工作中及其常用,特别在多表查询中,字段又多又复杂。
- 大家一定学会合理使用map传参,不要一味的修改实体类,导致某些实体类过于臃肿。
7、resultMap详解
数据库不可能永远是你所想或所需的那个样子
属性名和字段名不一致,我们一般都会按照约定去设计数据的,但确实阻止不了一些孩子,瞎比起名字。
1、Java中的实体类设计
3、mapper
4、mapper映射文件
5、测试
结果:
- User{id=1, name=‘null’, password=‘123’}
- 查询出来发现 name为空 . 说明出现了问题!
分析:
-
select * from user where id = #{id} 可以看做
select id,username,password from user where id = #{id}
-
mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 利用反射去对应的实体类中查找相应列名的set方法设值 ,当然找不大username;
解决方案
方案一:为列名指定别名 , 别名和java实体类的属性名一致 .
方案二:使用结果集映射->ResultMap 【推荐】
结论:
这个地方我们手动调整了映射关系,称之为手动映射。
但如果不调整呢?mybatis当然会按照约定自动映射。
有了映射这种牛逼的事情之后:
我们的:
还用写吗?两个字【牛逼】!
当然约定的最基本的操作就是全都都一样,还有就是下划线和驼峰命名的自动转化
- mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建
- sql 类型主要分成 :
- @select ()
- @update ()
- @Insert ()
- @delete ()
**注意:**利用注解开发就不需要mapper.xml映射文件了 .
1、接口中添加注解
2、核心配置文件中配置
3、进行测试
mybatis的配置文件分为核心配置文件和mapper配置文件
1、核心配置文件
-
mybatis-config.xml 系统核心配置文件
-
核心配置文件主要配置mybatis一些基础组件和加载资源,核心配置文件中的元素常常能影响mybatis的整个运行过程。
-
能配置的内容如下,顺序不能乱:
找几个重要的讲解一下
(1)environments元素
environments可以为mybatis配置多环境运行,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定),如果想切换环境修改default的值即可。
最常见的就是,生产环境和开发环境,两个环境切换必将导致数据库的切换。
-
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
-
数据源是必须配置的。
-
有三种内建的数据源类型
-
数据源也有很多第三方的实现,比如druid,hikari,dbcp,c3p0等等…
-
这两种事务管理器类型都不需要设置任何属性。
-
具体的一套环境,通过设置id进行区别,id保证唯一!
-
子元素节点:transactionManager - [ 事务管理器 ]
-
子元素节点:数据源(dataSource)
(2)mappers元素
mappers的存在就是要对写好的mapper和xml进行统一管理
要不然系统怎么知道我写了哪些mapper
通常这么引入
Mapper文件
- namespace中文意思:命名空间,作用如下:
- namespace的命名必须跟某个接口同名,这才能找的到啊!
(3)Properties元素
数据库连接信息我们最好放在一个单独的文件中。
1、 在资源目录下新建一个db.properties
2、 将文件导入properties 配置文件
(4)设定别名
别名小插曲里已将讲了,这里就不重复说了。
(5)其他配置浏览
settings能对我的一些核心功能进行配置,如懒加载、日志实现、缓存开启关闭等
完整的 settings 元素:
设置一览表
1、 概述
MyBatis提供了对SQL语句动态的组装能力,大量的判断都可以在 MyBatis的映射XML文件里面配置,以达到许多我们需要大量代码才能实现的功能,大大减少了我们编写代码的工作量。
动态SQL的元素
2、if元素(非常常用)
if元素相当于Java中的if语句,它常常与test属性联合使用。现在我们要根据name去查找学生,但是name是可选的,如下所示:
3、choose、when、otherwise元素
选一个
有些时候我们还需要多种条件的选择,在Java中我们可以使用switch、case、default语句,而在映射器的动态语句中可以使用choose、when、otherwise元素。
4、where元素
上面的select语句我们加了一个的绝对true的语句,目的是为了防止语句错误,变成这样where后没有内容的错误语句。这样会有点奇怪,此时可以使用元素。
5、trim元素
有时候我们要去掉一些特殊的SQL语法,比如常见的and、or,此时可以使用trim元素。trim元素意味着我们需要去掉一些特殊的字符串,prefix代表的是语句的前缀,而prefixOverrides代表的是你需要去掉的那种字符串,suffix表示语句的后缀,suffixOverrides代表去掉的后缀字符串。
6、set元素
在update语句中,如果我们只想更新某几个字段的值,这个时候可以使用set元素配合if元素来完成。注意:set元素遇到,会自动把,去掉。
7、foreach元素
foreach元素是一个循环语句,它的作用是遍历集合,可以支持数组、List、Set接口。
-
collection配置的是传递进来的参数名称
-
item配置的是循环中当前的元素。
-
index配置的是当前元素在集合的位置下标。
-
open和 close配置的是以什么符号将这些集合元素包装起来。
-
separator是各个元素的间隔符。
8、SQL片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
提取SQL片段:
引用SQL片段:
配置日志的一个重要原因是想在调试的时候能观察到sql语句的输出,能查看中间过程
1、标准日志实现
指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
STD:standard out:输出
STDOUT_LOGGING:标准输出日志
这就好了,执行一下看看。
2、组合log4j完成日志功能(扩展)
使用步骤:
1、导入log4j的包
2、配置文件编写 log4j.properties
3、setting设置日志实现
4、在程序中使用Log4j进行输出!
5、测试,看控制台输出!
- 部门和员工的关系,一个部门多个员工,一个员工属于一个部门
- 那我们可以采取两种方式来维护关系,一种在一的一方,一种在多的一方!
数据库设计
1、自己手动维护
自己维护,多写几个语句,使用java组装,有时候使用mybatis维护关系反而复杂,不如自己维护。
1、先搜索部门
2、根据部门搜索员工
3、手动拼装
2、在多的一方维护关系
1、编写实体类
2、编写实体类对应的Mapper接口
3、编写Mapper接口对应的 mapper.xml配置文件
——————————————————————————————————————————————————
按查询嵌套,级联查询处理,就像SQL中的子查询
1、写方法
2、mapper处理
3、编写完毕去Mybatis配置文件中,注册Mapper
4、测试
5、结果
——————————————————————————————————————————————
按结果嵌套处理,就像SQL中的联表查询
除了上面这种方式,还有其他思路吗?
我们还可以按照结果进行嵌套处理;
1、接口方法编写
2、编写对应的mapper文件
3、去mybatis-config文件中配置
4、测试
3、在一的一方维护关系
在部门处维护关系,此处可以联想订单和订单详情。
实体类编写
按结果嵌套处理
1、写方法
2、写配置文件
3、将Mapper文件注册到MyBatis-config文件中
4、测试
按查询嵌套处理
1、TeacherMapper接口编写方法
2、编写接口对应的Mapper配置文件
3、将Mapper文件注册到MyBatis-config文件中
4、测试
结果:
1、为什么要用缓存?
-
如果缓存中有数据,就不用从数据库获取,大大提高系统性能。
-
mybatis提供一级缓存和二级缓存
2、一级缓存:
一级缓存是sqlsession级别的缓存
- 在操作数据库时,需要构造sqlsession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据
- 不同的sqlsession之间的缓存区域是互相不影响的。
一级缓存工作原理:
图解:
-
第一次发起查询sql查询用户id为1的用户,先去找缓存中是否有id为1的用户,如果没有,再去数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
-
如果sqlsession执行了commit操作(插入,更新,删除),会清空sqlsession中的一级缓存,避免脏读
-
第二次发起查询id为1的用户,缓存中如果找到了,直接从缓存中获取用户信息
-
mybatis默认支持并开启一级缓存。
一级缓存演示
1、必须配置日志,要不看不见
2、编写接口方法
3、接口对应的Mapper文件
4、测试
5、通过日志分析
一级缓存失效
- sqlSession不同
- 当sqlSession对象相同的时候,查询的条件不同,,原因是第一次查询时候一级缓存中没有第二次查询所需要的数据
- 当sqlSession对象相同,两次查询之间进行了插入的操作
- 当sqlSession对象相同,手动清除了一级缓存中的数据
3、二级缓存:
二级缓存是mapper级别的缓存
- 多个sqlsession去操作同一个mapper的sql语句,多个sqlsession可以共用二级缓存,所得到的数据会存在二级缓存区域,
- 二级缓存是跨sqlsession的
- 二级缓存相比一级缓存的范围更大(按namespace来划分),多个sqlsession可以共享一个二级缓存
二级缓存实现原理
首先要手动开启mybatis二级缓存。
在config.xml设置二级缓存开关 , 还要在具体的mapper.xml开启二级缓存
class Student implements Serializable{}
(1)cache属性的简介:
eviction回收策略(缓存满了的淘汰机制),目前MyBatis提供以下策略。
- LRU(Least Recently Used),最近最少使用的,最长时间不用的对象
- FIFO(First In First Out),先进先出,按对象进入缓存的顺序来移除他们
- SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU, 移除最长时间不用的对形象
flushInterval:刷新间隔时间,单位为毫秒,
- 这里配置的是100秒刷新,如果你不配置它,那么当SQL被执行的时候才会去刷新缓存。
size:引用数目,
-
一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
**readOnly:**只读,
-
意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改
(2)操作过程:
sqlsession1查询用户id为1的信息,查询到之后,会将查询数据存储到二级缓存中。
如果sqlsession3去执行相同mapper下sql,执行commit提交,会清空该mapper下的二级缓存区域的数据
sqlsession2查询用户id为1的信息, 去缓存找 是否存在缓存,如果存在直接从缓存中取数据
禁用二级缓存:
在statement中可以设置useCache=false,禁用当前select语句的二级缓存,默认情况为true
在实际开发中,针对每次查询都需要最新的数据sql,要设置为useCache=“false” ,禁用二级缓存
flushCache标签:刷新缓存(清空缓存)
一般下执行完commit操作都需要刷新缓存,flushCache="true 表示刷新缓存,可以避免脏读
二级缓存应用场景
对于访问多的查询请求并且用户对查询结果实时性要求不高的情况下,可采用mybatis二级缓存,降低数据库访问量,提高访问速度,如电话账单查询
根据需求设置相应的flushInterval:刷新间隔时间,比如三十分钟,24小时等。
二级缓存局限性:
mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。
二级缓存演示
先不进行配置
结果:
可以看见两次同样的sql,却都进库进行了查询。说明二级缓存没开。
配置二级缓存
1、开启全局缓存
2、使用二级缓存,这个写在mapper里
3、测试执行
3、第三方缓存–EhCache充当三级缓存
我们的三方缓存组件很对,最常用的比如ehcache,Memcached、redis等,我们以比较简单的ehcache为例。
1、引入依赖
2、修改mapper.xml中使用对应的缓存
3、添加ehcache.xml文件,ehcache配置文件,具体配置自行百度
3、测试