MyBatis

MyBatis简介

MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射,MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作,MyBatis可以通过简单的XML注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录,MyBatis本是apache的一个开源项目ibatis,2010年这个项目由apache迁移到了google code,并且改名为MyBatis (ORM 对象关系映射)

 

在spring中学到的jdbcTemplate只是一个工具类,只能去写和增删改查相关的功能,和框架是有区别的

 

 

 

半自动的好处:相比hibernet的全自动ORM,MyBatis支持手动写sql,我们在后续优化项目的时候可以通过sql语句优化来提高程序的整体执行效率

 

通过xml配置,实现了sql语句和核心代码模块的分离

 

如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

 

 

MyBatis的核心配置文件

与spring中的dataSource数据源不同,驱动的name属性不是driverClassName,而是driver,我们要依照MyBatis官网提供的属性名称来写标签

 

MyBatis采用了池的思想,将数据库链接对象

environment 元素体中包含了事务管理和连接池的配置,mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息

 

读取数据源数据

属性(properties)

属性配置文件

resource属性:引用外部配置文件,相对路径、绝对路径

 

类型别名(typeAliases)

<configuration>标签中配置

类型别名可给类起别名,它仅用于 XML 配置,意在降低冗余的全限定类名书写

这样我们在mapper映射文件中就能使用别名了

mapper文件中使用别名:

 

SqlSessionFactory

SqlSessionFactory的实例是MyBatis应用的核心,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder获得,而SqlSessionFactoryBuilder则可以从XML配置文件或一个预先配置的Configuration实例来构建出SqlSessionFactory实例

 

从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置,但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流,MyBatis 包含一个名叫 Resources 的工具类(使用的是org.apache.ibatis.io包下的类),它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易

 

 

MyBatis在底层会通过动态代理为持久层接口创建对应的实现类(接口代理

测试类:

 

配置Mapper文件

mapper文件存放sql资源,在Resource文件下mapper文件资源路径必须和持久层接口的包结构相同

Resource中的包结构以文件夹的形式创建,不能以"."的方式分隔

可以将映射文件命名为持久层接口名+Mapper的形式

 

引入映射文件约束

 

配置sql语句

详细参考官方文档:https://mybatis.net.cn/sqlmap-xml.html

查询语句

<select> 标签用于编写查询语句

 

id必须是接口中的方法名称

resultType属性:需要将映射实体的全类名传过来

resultMap属性:自定义的字段映射规则,如果没有定义的字段就按照默认的映射规则,需要传入resultMap的id值

parameterType属性:如果有参数需要把参数的全类名传过来

 

默认映射规则

 

解决方案

1、在sql语句中使用别名

这种方式就不能用*,比较麻烦

2、设置映射结果集,自定义映射关

在select标签中添加resultMap属性,引入自定义的映射结果集

以下将字段中的emp_name映射到实例中的empName,没有设置映射关系的就按照默认的映射规则

 

底层模拟

模拟SqlSession中getMapper方法底层:

模拟一个mybatis通过反射创建的持久层接口实现类,通过聚合sqlSession读取mapper文件来映射对应的sql并且返回结果

测试:

 

单元测试中的Before和After标签:

可以减少测试方法中的代码量

 

 

删除语句

OGNL表达式

在mybatis中可以通过OGNL表达式去取值

OGNL表达式: #{ } 可以取到实体中某一个属性的值,在执行sql的时候可以防止sql注入

返回值类型为影响记录条数,可以不用写

在开启事务的条件下,增删改语句后还要提交事务

 

修改语句

 

添加语句

selectKey标签

一些情况下,新增一条数据信息,但其主键(id)是数据库自动在数据库生成(自增),而有些业务逻辑的处理是需要要到这个生成的主键(id)

selectKey 会将 SELECT LAST_INSERT_ID() 函数返回的结果放入到实体中

 

模糊查询

OGNL表达式不支持字符串拼接

所以要在传值的时候就把模糊查询的条件给定义好(百分号要提前写好)

OGNL表达式{}中的内容在此处可以是任意的

OGNL表达式使用的是preparedStatement,是有预编译的

或者使用el表达式

el表达式支持字符串的拼接

但是el表达式必须在{}内部写value

el表达式不能防止sql注入

el表达式在mybatis中是直接使用statement的,没有预编译,但是模糊查询并没有sql注入这一方面的隐患,因此可以使用

 

聚合函数

resultType是需要写的,与增删改语句的影响记录条数不同

parameterType,resultType属性: 如果是java.lang这个包下的可以直接写类名,基本数据类型也可以直接写,不区分大小写

 

 

 

测试单元

 

 

动态SQL

动态SQL指的是根据不同的查询条件,生成不同的Sql语句

 

 

if标签

我们会先给 where 后面加上一个恒成立的条件 1=1

再在后面使用<if></if>标签设置查询的条件

或者使用<where></where>标签,将<if></if>标签放在<where></where>标签中

test属性:会通过参数中实例的get方法获取到属性,对属性值进行测试,如果属性不为空则生成对应的查询条件

 

choose、when、otherwise标签

类似java的 switch 语句

where 元素等价的自定义 trim 元素为:

 

set标签

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)

set 元素等价的自定义 trim 元素

 

foreach标签

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

 

script标签

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素

 

详情查看官方文档:https://mybatis.net.cn/dynamic-sql.html

 

 

多表联查

涉及业务的实体都要对应一个接口

 

一对一:账单对用户,一个账单只能对应一个用户

将账单表看成主表

我们一般在主表中聚合一个从表的引用

为聚合的实例赋值

需要在resultMap中配置映射关系

配置查询语句

配置映射关系

association标签

通过多表联查找到的主键id字段来为聚合的实例赋值

property属性:就是引用的属性名称

column属性:匹配的主键id

 

一对多:用户对账单,一个用户可以对应多个账单

用户表看成主表

配置查询语句

配置映射关系

collection标签

property属性:集合引用的属性名称

ofType属性:集合的泛型的全类名,有别名可以用别名

 

 

多对多:用户和角色,一个用户可以对应多个角色,一个角色也可以对应多个用户

例子:

张三:在家庭中是一个父亲,在工作中,是一个项目经理

项目经理:张三,李四

查询语句

配置映射关系

 

延迟加载(懒加载)和立即加载

什么是延迟加载?

真正在使用数据的时候才发起查询,不用的时候不查询,按需加载(懒加载)

什么是立即加载?

不管用不用,一调用方法,马上发起查询,效率会低于延迟加载

 

 

在查询用户数据的时候,有没有必要将账单数据带出来?

没有必要,我们会采用延迟加载的策略

 

在查询账单数据的时候,有没有必要将用户数据带出来?

有必要,因为在查询账单的时候,需要知道该账单对应的用户是谁,要采用立即加载的策略

配置延迟加载(懒加载)

工程默认情况下采用的是立即加载的策略

设置名:

lazyLoadingEnabled

延迟加载的全局开关,当开启时,所有关联对象都会延迟加载, 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态,默认值就是false

 

aggressiveLazyLoading

开启时,任一方法的调用都会加载该对象的所有延迟加载属性,否则,每个延迟加载属性会按需加载,默认值false

 

需要在configuration标签下创建settings标签

 

一对一中立即加载的配置

查询的时候只查询账户表数据,因为如果使用多表联查是一定会把数据立即查出来的

在association中加入select属性

select属性:提供UserMapper.xml中查询语句的全局标识

column属性:主sql查出来的某一列字段作为参数传递给子sql,提供一对一查询用户表的查询条件

由于全局标识可能会重复,所以我们把名称空间也带上

UserMapper中新建条件查询sql

 

一对多中按需加载的配置

只有在存放多表数据的属性需要被用到的时候才会去查询并返回结果(延迟加载)

select属性:提供AccountMapper.xml中查询语句的全局标识

column属性:主sql查出来的某一列字段作为参数传递给子sql,提供一对多查询账户表的查询条件

AccountMapper中新建条件查询sql

注意在使用resultMap的时候不要在两个表间同时配置对方表的select引用,否则会出现栈溢出死循环

 

★关于mybatis中的缓存

缓存

1、什么是缓存

存在内存中的临时数据

将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题

 

2、为什么使用缓存

减少和数据库的交互次数,减少系统开销,提高系统效率

 

3、什么样的数据能使用缓存

经常查询并且不经常改变的数据

 

关于mybatis中的缓存:一级缓存,二级缓存

一级缓存:sqlSession级别的缓存,这个缓存默认是存在的,在我们进行数据查询的时候,mybatis会先去数据库中将对应的数据查出来,查出来之后,会将数据封装成对象,存到缓存域中,下次再发起查询的时候,mybatis会从缓存域中去读取数据,避免了与数据库的多次交互

 

二级缓存:sqlSessionFactory级别的缓存,这个缓存可以共享所有的sqlSession,将数据通过序列化的方式存到缓存域中,存放的是数据,不是对象

 

测试一级缓存

 

如何让一级缓存失效

1、在调用了清空缓存的方法之后

2、在关闭了sqlSession对象之后

3、第二次查询的数据跟第一次查询的数据有偏差(在第一次查询了之后,你去执行了添加、删除、修改等操作)

 

开启二级缓存

使用二级缓存需要让被查询的实体实现序列化接口

 

设置(settings)

cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存,默认值是true

 

在UserMapper.xml中配置

开启对缓存的支持

 

在需要使用到二级缓存的查询语句中添加useCache属性,true:使用二级缓存,false:不使用,默认情况下是false

 

如何让二级缓存失效

1、在调用了清空缓存的方法之后

2、关闭sqlSessionFactory

 

注解方式写SQL

在以后开发中注解方式用的较少

不用写mapper文件

在配置文件中加上mapper映射路径,使用class属性映射到类

 

注解方式