一、主键回填
在一些特殊的需求中,需要执行完新增的SQL后立即知道新增时主键的值。如果主键的值是自己设置的固定值,可以知道主键是多少。但是很多时候都是使用MySQL的主键自增,这时想要获取主键的值就需要通过特殊的方式获取了。
●在MyBatis中有两种方式可以获取到自增主键的值:
•使用<selectKey>子标签编写SQL进行回填属性。
•使用<select>的useGeneratedKeys属性进行自动回填
●selectKey的使用
• 在接口中添加方法
在接口中添加方法。
//参数peo中包含了name值和address值。
int insert1(People peo);
•在映射文件中添加标签
在映射文件中添加标签与接口中方法进行绑定。
<selectkey>标签是<insert>的子标签,作用:把查询到的结果填充进行回填
keyProperty:接口方法参数中,对象的哪个属性需要进行回填
resultType:SQL查询到的结果
select @@identity:是MySQL内置的全局变量表示获取到自增主键值。
order:selectKey中SQL是在外面SQL执行之前还是之后。可取值:AFTER和BEFORE
●自动主键回填
MyBatis的映射文件的<insert>标签带有自动主键回填功能,只需要设置useGeneratedKeys进行开启自动主键回填功能,同时设置keyProperty的值需要回填到对象的哪个属性。
直接修改映射文件中insert1的内容,修改后运行测试类,查看控制台输出对象值是否带有主键值。
二、动态SQL
●动态SQL引入
MyBatis在简化操作方法提出了动态SQL功能,将使用Java代码拼接SQL语句,改变为在XML映射文件中截止标签拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护
MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。
为了查看动态SQL最终执行时的SQL,先把日志配置上。
●if标签
通过if处理用户多变的查询条件
接口代码
List<People> selectIf(People people);
mapper映射文件通过if进行判断参数的属性是否为null。
<if>标签的test属性值为OGNL(对象导航图语言)表达式,通过对象属性名可以快速获取到对象属性值。
•namel=null:OGNL表达式,直接写属性名可以获取到属性值。不需要添加$或#
•name=#(name]中name是SQL的列名。#(name)是MyBatis获取参数对象属性值的写法(之前学习的)。
•where 1=1中1=1是为了保证SQL语法的正确性。如果if成立没有1=1最后的SQL就是where and name=xxx这种写法是不对的。
•通过测试类中是否设置对象属性值来控制SQL,如果name属性有值,address属性没有值。最终执行的SQL就没有and name=xxx。
●choose
choose标签相当于Java中的switch..case...default,在choose标签里面可以有多个when标签和一个otherwise(可以省略)标签。只要里面有一个when成立了后面的when和otherwise就不执行了。
●trim标签
if标签中为了保证语法的正确性,需要在SQL中明确指定where 1=1,其中1=1存在的意义单纯为了保证语法的正确性,没有实际意义的。可以通过trim标签动态进行截取添加,省略where1=1,
•trim标签包含四个属性:
prefix:只要内容不是空字符串(""),就在子内容前面添加特定字符串。
prefixOverrides:如果里面内容是以某个内容开头,去掉这个内容。
suffix:只要内容不是空字符串(""),就在子内容后面添加特定字符串。
suffixOverrides:如果里面内容以某个内容结尾,就去掉这个内容。
•trim作为很多其他标签的底层。
•无论是开头操作还是结尾的操作,都是先去掉容,后添加。
•trim只会对里面的子内容进行操作。如果子内容为空则不进行任何操作。
●set标签
set标签是专门用在修改SQL中的,属于trim的简化版,带有下面功能:
●如果子内容不为空串,在最前面添加set
•去掉最后一个逗号
●代码流程
在接口中添加修改方法
int update(People people);
在映射文件中编写动态SQL。
set标签里面id=#(id)是非常重要的,不能不写。因为set在解析时,如果里面为空串,是不会在前面添加set的,对于SQL的修改来说,没有set关键字是不正确的语法。
●foreach标签
foreach标签表示循环,主要用在in查询或批量新增的情况。
•foreache标签的属性
collection:要遍历的数组或集合对象。如果参数没有使用@Param注解:arg0或array(数组)|list(集合).如果使用@Param注解,使用注解的名称或param1
open:遍历结束在前面添加的字符串
close:遍历结束在后面添加的字符串
item:迭代变量。在foreach标签里面#(迭代变量)获取到循环过程中迭代变量的值。
separator:分隔符。在每次循环中间添加的分割字符串。
index:迭代的索引。从0开始的数字。
●代码演示
在接口中添加方法
List<People> selectByIds(int[] ids);
在映射文件中添加下面代码。
由于collection属性值的重要提醒:
如果方法参数前面没有添加@Param注解,只能通过array或arg0获取到数组。
如果方法参数前面添加了@Param注解,只能通过注解值或param1获取到数组。
●bind标签
bind标签表示对传递进来的参数重新赋值。最多的使用场景为模糊查询。通过bind可以不用在Java代码中对属性添加%
List<People> selectLike(People peo);
映射文件中需要注意value属性值中进行字符串拼接。%两次要有单引号,name没有单引号。
●sql和include标签
在企业开发中的表可能都会有很多列,当使用多表联合查询时列的个数更多。很多功能或SQL都需要使用这些列的话,其实在做很多重复工作。及时复制粘贴,一旦碰到表结构改变或添加、删除列的时候也需要修改很多SQL。
MyBatis的sql标签用于定义SQL片段,include标签用于引用sql标签定义的片段。
List<People> selectsQLO;
使用sql标签定义sql片段,使用include引用sql片段
三、MyBatis中常用注解
在MyBatis中对于特别简单的SQL、尤其不需要定义resultMap的SQL可以使用注解进行实现。通过注解能简化映射文件的编写。MyBatis的注解通过全局配置文件<mappers>进行加载注解
•如果一个Mapper接口中所有方法都使用注解定义SQL,可以在全局文件中配
如果一个Mapper接口中既有注解又有mapper.xml定义SQL。可以在全局配置文件中通过<package>进行加载。这种方式和之前的接口绑定方案的配置是一样的。也就是说MyBatis在扫描这个包的时候就可以加载到注解。
在MyBatis中注解都是写在Mapper接口的方法上中,所有的注解都在org.apache.ibatis.annotations包中,常见注解:
@Select 查询
@Insert 新增
@Delete 删除
@Update 修改
@SelectKey 主键回填
@SelectProvider 调用SQL构建器。查询专用
@InsertProvider 调用SQL构建器。添加专用
@UpdateProvider 调用SQL构建器。修改专用
@DeleteProvider 调用SQL构建器。删除专用
@Param 定义参数的名称
●主键回填
使用注解时,主键回填需要通过@SelectKey注解。该注解中:
keyProperty:必有属性。表示回填属性名
statement:执行的sql
before:必有属性。表示是否在@Insert的SQL之前执行。
resultType:必有属性。表示statement对应SQL执行结果
●SQL构建器(Provider)
MyBatis的SQL构建器赋予了程序员在Java类中编写SQL的方式。把以前写在注解参数中的复杂SQL转移到了类的方法中进行书写。
●直接写SQL方式
在接口中提供方法。并在方法上面添加@SelectProvider注解,注解中属性含义:
• type:编写SQL的类
•method:类中哪个方法返回SQL
新建MySQLProvider类,并在类中提供selectprovider方法。
返回SQL的方法必须是publicString的。
●使用SQL类
MyBatis提供了SQL类,该类中封装了很多方法,方法名称和SQL的关键字名称正好对应。
注意:
1.没有and关键字的方法。如果多个条件可以放在一个where中,也可以放在多个连续where中。
2.最终需要调用toString()转换为字符串。
●使用注解进行结果映射
@Results的value属性类型Result:
@Result注解:
column:数据库列
property:属性名
id:是否为主键,默认false
四、多表查询
实现多表查询方案:
●业务装配
●N+1查询
●联合查询:多表SQL
●多表查询业务装配(业务装配:这种查询方式本质都是单表 是通过自己写的业务代码方式把多个单表组装成多表查)
•查询所有学生及班级信息:
首先查询所有学生信息:select*from student 这样就可以获得每一个学生所在班级编号
再跟进每一个学生所在班级编号查询对应的班级信息:select*from clazz where cid=?
•查询所有班级及学生信息:
首先查询所有班级信息:select*fromclazz 这样就可以获得每一个班级编号
再根据每一个班级编号查询每一个班级对应学生 select*from student where cid=?
优点:理解简单 实现简单
缺点:每次查询需要写业务代码进行连接 实现整体查询
●多表查询ON+1方式(N+1含义代表是查询操作执行了多少条SQL语句作用:就是为了代替上面查询业务代码)
•查询所有学生及班级信息:
首先查询所有学生信息:select* fromstudent 这样就可以获得每一个学生所在班级编号
再跟进每一个学生所在班级编号查询对应的班级信息:select米from clazz where cid=?
association:对象
•查询所有班级及学生信息:
首先查询所有班级信息:select*fromclazz 这样就可以获得每一个班级编号
再根据每一个班级编号查询每一个班级对应学生 select*fromstudent where cid=?
collection:集合
优点:减少了业务代码书写
缺点:执行SQL语句数量比较多 效率比较低
●多表查询①联合查询(使用多表查询SQL语句进行查询)
查询所有学生及班级信息:
select* from student s join clazz c on s.cid=c.cid;
查询所有班级及学生信息:
select米from clazz c joinstudent s on s.cid=c.cid;
●延迟加载
延迟加载只能出现在多表联合查询的N+1方式中。
表示当执行当前方法时,是否立即执行关联方法的SQL。
●测试默认情况下效果
以EmpMapper接口的selectAlIN1(0方法进行举例:当前方法的作用是查询全部Emp信息,并且调用DeptMapper的selectByld方法,同时查询Dept的内容。
在测试类中中调用EmpMapper的selectAllN1()方法。
绝对不能输出list,如果输出list对象,表示使用了emp对象。如果输出list对象,关联的SQL一定被执行。
观察控制台。如果发现依然执行查询dept表的SQL。
说明此时MyBatis的延迟加载没有生效。
●启用延迟加载
配置延迟加载有两种方式:
全局配置。整个项目所有N+1位置都生效。
局部配置。只配置某个N+1位置。
两种方式需要选择其中一种,如果两种方式都使用了,局部配置方式生效。
●全局配置方式
官方文档全局设置属性说明
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态 true | false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性,否则,每个延迟加载属性会按需加载 true | false false
●局部配置方式
局部配置方式需要在collection或association标签中配置fetchType属性。fetchType可取值:lazy(延迟加载)和earge(立即加载)。当配置了fetchType属性后,全局settings的配置被覆盖,对于当前标签以fetchType属性值为准。
●ResultType和ResultMap的区别和使用场景
ResultType:某些单表或者数据库查询字段和实体类中属性完全一致
ResultMap:
•数据库字段和实体类属性不一致
•N+1查询时候 使用标签association、collection调用其他查询方法时候
•联合查询在进行多表查询结果集接收的时候使用ResultMap
五、缓存
缓存是一种临时存储少量数据至内存或者是磁盘的一种技术.减少数据的加载次数,可以降低工作量,提高程序响应速度缓存的重要性是不言而喻的。
MyBatis的缓存将相同查询条件的SQL语句执行一遍后所得到的结果存在内存或者某种缓存介质当中,当下次遇到一模一样的查询SQL时候不在执行SQL与数据库交互,而是直接从缓存中获取结果,不再查询数据库,提升了性能;尤其是在查询越多、缓存命中率越高的情况下,使用缓存对性能的提高更明显。
MyBatis分为一级缓存和二级缓存,同时也可配置关于缓存设置。一级存储是SqlSession上的缓存,二级缓存是在SqlSessionFactory(namespace)上的缓存。默认情况下,MyBatis开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据。
●一级缓存
一级缓存是SqlSession级缓存。只要是同一个SqlSession对象(必须是同一个)调用同一个<select>标签相同参数值时(不同<select>完全相同的SQL不会走同一个缓存),将直接使用缓存数据,而不会访问数据库。
●一级缓存想要生效,必须同时满足3个条件:
1.同一个SqlSession对象。
2. 同一个select标签。本质为底层同一个JDBC的Statemen对象
3.完全相同的SQL,包含SQL的参数值也必须相同
insert、delete、update操作会清空一级缓存数据。
commit也会清空缓存。
●命中缓存:从Map中查询是否存在指定key。如果存在表示命中缓存,如果不存在这个key,需要访问数据库。
更新到缓存:把查询结果put到map中。
●二级缓存
二级缓存是以namespace为标记的缓存,可能要借助磁盘,磁盘上的缓存,可以是由一个SglSessionFactory创建的SglSession之间共享缓存数据。默认并不开启。
二级缓存生效条件:
1.同一个SqlSessionFactory对象。
2.同一个方法(<select>)
3.SQL完全相同。
二级缓存默认不开启,需要手动开启。
只有当SalSession执行commit或close时才会把一级缓存数据,刷新到二级缓存中
●注意:
1.MyBatis的二级缓存的缓存介质有多种多样,而并不一定是在内存中,所以需要对JavaBean对象实现序列化接口。
2.二级缓存是以namespace为单位的,不同nameppace下的操作互不影响
3.查询数据顺序 二级->一级--->数据库--->把数据保存到一级,当sqlsession关闭或者提交的时候,把数据刷入到二级缓存中
4.执行了DML操作,会清空一级缓存,所以数据更不可能到达二级缓存中。
5.cache有一些可选的属性 type,eviction, flushlnterval, size, readOnly, blocking。
六、四大核心接口介绍及执行流程(面试题)
●四大核心接口介绍
MyBatis执行过程中涉及到非常重要的四个接口,这个四个接口成为MyBatis的四大核心接口:
•Executor执行器,执行器负责整个SQL执行过程的总体控制。默认SimpleExecutor执行器。
•StatementHandler语句处理器语句处理器负责和JDBC层具体交互,包括prepare语句,执行语句,以及调用ParameterHandler.parameterize()。默认是PreparedStatementHandler。
•ParameterHandler参数处理器,参数处理器,负责PreparedStatement入参的具体设置。默认使用DefaultParameterHandler
•ResultSetHandler结果集处理器,结果处理器负责将JDBC查询结果映射到java对象。默认使用DefaultResultSetHandler。
●完整执行流程文字说明
•使用执行器Executor控制整个执行流程
•实例化StatementHandler,进行SQL预处理
•使用ParameterHandler设置参数
•使用StatementHandler执行SQL
•使用ResultSetHandler处理结果集
七、执行器类型
MyBatis的执行器都实现了Executor接口。作用是控制SQL执行的流程。
在MyBatis中执行器共分为三个类型:SimpleExecutor、ReuseExecutor、BatchExecutor。
•SimpleExecutor是默认的执行器类型。每次执行query和update (DML)都会重新创建Statement对象。
•ReuseExecutor只预编译一次。把Statement放入到Map中,后面复用Statement(JDBC)对象。
• BatchExecutor。用在update(DML)操作中。所有SQL一次性提交。
除了上面的三种执行器还有个Executor接口的实现类CachingExecutor,这个是处理缓存的。无论使用上面三种执行器中的哪个。都是会执行CachingExecutor的
在项目可以通过factory.openSession()方法参数设置执行器类型。通过枚举类型ExecutorType进行设置。
也可以在全局配置文件中通过<settings>中defaultExecutorType进行全局设置(不推荐)。
执行器主要控制的就是Statpment对SQL如何进行操作。有效范围:同一个SqlSession对象。
●SimpleExecutor
SimpleExecutor是MyBatis默认的执行器类型。在没有明确设置执行器类型时,默认就是这个类型。
ReuseExecutor主要用在执行时,重用预编译SQL。在同一个SqlSession对象中下次调用已经预编译的SQL直接设置参数。
●BatchExecutor
BatchExecutor底层使用DBC的批量操作。每一条SQL都不会立即执行,而是放到了List<Statement>中,最终统一提交。
由于底层的批量操作只支持DML操作,所以BatchExecutor也主要用在批量新增、批量删除、批量修改中。在DeptMapper接口中添加一个新增方法。





被折叠的 条评论
为什么被折叠?



