第三十天 Java基础学习(二十四)

一、主键回填

      在一些特殊的需求中,需要执行完新增的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接口中添加一个新增方法。

 

 

上一篇: 第二十九天 Java基础学习(二十三)
下一篇: 第三十一天 Java基础学习(二十五)
aLntano
博客等级 码龄3年 5粉丝 · 60原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值