一、MyBatis-plus是什么
MyBatis-plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生
特性:无侵入、损耗小、强大的CRUD操作,支持Lambda形式调用、支持主键自动生成,支持ActiveRecord模式、支持自定义全局通用操作、内置代码生成器、内置分页插件、分页插件支持多种数据库、内置性能分析插件、内置全局拦截插件。
二、MyBatis-plus的一些注解
1、@TableName 表名注解
注解在类上,指定类和数据库表的映射关系,实体类的类名和数据库表名相同时,可以不指定该注解
2、@TableId 主键注解
注解在实体类的某一字段上,表示这个字段对应数据库表的主键,当主键名为ID时,无须使用该注解显式指定主键,mp会自动关联,若类的字段名和表的列名不一致,可用value属性指定表的列名
3、@TavleField 字段注解(非主键)
注解在某一字段上,指定java实体类的字段和数据库表的列的映射关系,应用于 排除非表字段(若实体类中某个字段,不对应表中的任何列,它只是用于保存一些额外的,或组装后的数据)、字段验证策略(通过insertStrategy、updateStrategy、whereStrategy属性进行配置,可以控制在实体对象进行插入、更新、或作为WHERE条件时,对象的字段要如何组装到sql语句中)、字段填充策略(通过fill属性指定,字段为空时会进行自动填充)
4、@Version 乐观锁注解,标记@Version在字段上
5、@EnumValue 通枚举类注解(注解在枚举字段上)
6、@TableLogic 表字段逻辑处理注解(逻辑删除)
7、@KeySequence 序列主键策略 oracle
8、@SqlParser 租户注解,支持method上以及mapper接口上
9、@InterceptorIgnore插件过滤规则
三、条件构造器
MyBatis-Plus通过EntityWrapper(简称EW,MP封装的一个查询条件构造器)或者Condition(与EW类似)来让用户*的构建查询条件,简单便捷,没有额外的负担,能够有效提高开发效率,它主要用于处理sql拼接,排序,实体参数查询等。
使用的是数据库字段,不是java属性
警告:MyBatis-Plus不支持以及不赞成在RPC调用中把Wrapper进行传输,Wrapper很重,传输Wrapper可以类比你的controller用map接收值,正确的RPC调用姿势是写一个DTO进行传输,被调用方再根据DTO执行相应的操作
条件构造器主要涉及到3个类,AbstractWrapper,QueryWrapper,UpdateWrapper。
在AbstractWrapper中提供了非常多的方法用于构建WHERE条件,而QueryWrapper针对SELECT语句,提供了select()方法,可自定义需要查询的列,而updateWrapper针对UPDATE语句,提供了set()方法,用于构造set语句,条件构造器也支持lambda表达式
AbstractWrapper中用于构建sql语句中的WHERE条件的方法进行部分列举
eq等于=
eg:eq(“name”,“老王”)——>name=‘老王’
ne不等于<>
eg:ne(“name”,“老王”)——>name <>‘老王’
gt大于>
eg:gt(“age”,18)——>age>18
ge大于等于>=
eg:ge(“age”,18)——>age>=18
Condition
条件构造器的诸多方法中,均可以指定一个boolean类型的参数condition,用来决定该条件是否加入最后生成的WHERE语句中
实体对象作为条件
调用构造函数创建一个Wrapper对象时,可以传入一个实体对象,后续使用这个Wrapper时,会以实体对象中的非空属性,构建WHERE条件(默认构建等值匹配的WHERE条件,这个行为可以通过实体类里各个字段上的@TableField注解中的condition属性进行改变)
若希望针对某些属性,改变等值匹配的行为,则可以在实体类中用@TableField注解进行配置
allEq方法传入一个map,用来做等值匹配
当allEq方法传入的Map中有value为null的元素时,默认会设置为is null
若想忽略map中value为null的元素,可以在调用allEq时,设置参数boolean null2IsNull为false
若想要在执行allEq时,过滤掉Map中的某些元素,可以调用allEq的重载方法
lambda条件构造器
lambda条件构造器,支持lambda表达式,可以不必像普通条件构造器一样,以字符串形式指定列名,它可以直接以实体类的方法引用来指定列
四、BaseMapper提供的一些方法
1、Insert
2、deleteById(根据主键id删除一条记录)
3、deleteBatchIds(根据主键id进行批量删除)
4、deleteByMap(根据Map进行删除,map中的key为列名,value为值,根据列和值进行等值匹配)
5、delete(根据条件构造器Wrapper进行删除)
6、selectById(根据主键ID进行查找)
7、selectBatchIds(根据主键ID进行批量查找)
8、selectByMap(根据map中指定的列名和列值进行等值匹配查找)
9、selectMaps(根据Wrapper条件,查询记录,将查询结果封装为一个Map,Map 的key为结果的列,value为值)
1、只查部分列,当某个表的列特别多,而select的时候只需要选取个别列,查询的结果也没必要封装成Java实体类对象时(只查部分列时,封装成实体后,实体对象中的很多属性会是null),则可以用selectMaps,获取到指定的列后,再自行进行处理即可
2、进行数据统计
10、selectObjs只会返回第一个字段(第一列)的值,其他字段会被舍弃
11、selectList(根据条件构造器Wrapper进行查询)
12、Update(根据实体entity和条件构造器Wrapper进行更新)
13、updateById(根据入参entity的id进行更新,对于entity中非空的属性,会出现在UPDATE语句的set后面,即entity中非空的属性,会被更新到数据库)
14、selectCount查询满足条件的总数,使用这个方法,不能调用QueryWrapper的select方法设置要查询的列了,这个方法会自动添加select count(1)
五、自定义sql
当mp提供的方法还不能满足需求时,则可以自定义sql
可以使用mp提供的Wrapper条件构造器来自定义SQL
两种方式:原生MyBatis和MyBatis-plus
原生MyBatis
- 注解方式
在usermapper中添加注解
- xml方式(使用xml时,若xml文件与mapper接口文件不在同一目录下,则需要在application.yml中配置mapper.xml的存放路径)
要在application.yml中添加MyBatis-plus配置
mapper-locations:classpath:**/mapper.xml
MyBatis-plus
也可以使用mp提供的Wrapper条件构造器,来自定义sql
六、分页查询
BassMapper中提供了两个方法进行分页查询,分别是selectPage和selectMapsPage,前者会将查询的结果封装成Java实体对象,后者会封装成Map<String,Object>
七、AR模式
ActiveRecord模式,通过操作实体对象,直接操作数据库表,与ORM类似
八、逻辑删除
逻辑删除是为了方便数据恢复,和保护数据本身价值的一种方案
mp提供的逻辑删除实现:
只需要在application.yml中进行逻辑删除的相关配置即可(全局配置)
若要对某些表进行单独配置,在实体类的对应字段上使用@TableLogic即可(局部配置)
开启mp的逻辑删除后,会对sql产生如下的影响
INSERT语句:没有影响
SELECT语句:追加WHERE条件,过滤掉已删除的数据
UPDATE语句:追加WHERE条件,防止更新到已删除的数据
DELETE语句:转变为UPDATE语句
上述的影响,只针对mp自动注入的sql生效,如果是自己手动添加的自定义sql,则不会生效。
九、自动填充
表中常常会有“新增时间”,“修改时间”,“操作人”等字段,比较原始的方式,是每次插入或更新时,手动进行设置,mp可以通过配置,对某些字段进行自动填充
1、在实体类中的某些字段上,通过@TableField设置自动填充
2、实现自动填充处理器
十、乐观锁插件
当出现并发操作时,需要确保各个用户对数据的操作不产生冲突,此时需要一种并发控制手段。悲观锁的方法是,在对数据库的一条记录进行修改时,先直接加锁(数据库的锁机制),锁定这条数据,然后再进行操作;而乐观锁,它先假设不存在冲突情况,而在实际进行数据操作时,再检查是否冲突
乐观锁插件仅支持updateById(id)和update(entity,wrapper)方法
十一、多表查询
1、MyBatis实现一对一有联合查询和嵌套查询。
- 联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置association节点配置一对一的类就可以完成
- 嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过SELECT属性配置
2、MyBatis实现一对多有联合查询和嵌套查询。
- 联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置collection节点配置一对多的类就可以完成
- 嵌套查询是先查一个表,根据这个表里面的结果的外键id,再去另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置
使用resultType、resultMap处理返回结果
resulttype:指定返回值结果的完全限定名,处理多表查询的结果,多表查询需要定义vo封装查询的结果,只能用在单表查询或者定义vo的情况 ,如果是定义vo不能将关联的数据封装为需要获得某个类的对象
使用resultMap:不需要定义vo类,将关联数据对应的类,作为另外一个类的属性
第一步:定义resultMap
第二步:引用resultMap
使用resultMap:用来多表关联的复杂查询中,通过需要将关联查询的数据封装为某个实体类对象,如果有特殊业务需要或者说明需要将管理数据封装为实体类的对象,使用resultMap
插入:如何避免多表联查
一、添加冗余字段
所谓冗余字段,就是在主要的信息上,添加上一些辅助的字段(比如搜索框中的搜索关键字,列表中的列表项等,这些信息都是由其他主体或业务上来的),用添加列的方式,来替换掉使用left join或inner join的方式获取数据,实现列表数据或报表数据的展示,这种方式是违背了数据库三范式的设计的,可能只是满足了三范式中的第一范式或者满足了第一,第二范式,这种方式在实现上,需要在数据录入的时候,将所需的信息都得到,并放入到相应的列上。
适用范围:单体服务,分布式微服务,DDD方式设计的领域服务中心均可,即便是进行了分库分表,也同样适用
缺点:信息录入时候,需要将要使用到的信息都获得到,并将其放入到相应的字段上去,在并发的时候,会存在性能问题
二、小表广播
在业务开发中,之所以需要进行多表联查,是因为很多数据是动态可变的,只是在主表中存入了关联表的关联主键,在需要进行数据展现的时候,通过关联主键,得到关联表的信息,进而进行数据的拼接和处理,最终通过服务的方式体现出来。
这种处理方式,主要是针对主表数据量大,但是关联表数据量小,并且关联表的数据变化频繁的情况下,在分布式的微服务系统中,使用这种方式。
难点是:所谓的小表,其实就是另外一个服务中的副本表,但是这个副本表的信息要远远小于副本的主题表,这样的情况下,主题表的信息同步到副本表,就是一个很重要的事情。既要保证数据的准确性,还要保证数据的及时性,对实现有着比较高的要求。
适用范围:分布式微服务分库情况下,列表上的信息需要从其他一个或几个微服务数据库中而来
缺点:实施难度大,数据同步要求高
三、建立集中的服务
建立一个专供的微服务,背后有一个专门的数据库,通过某种同步机制,把各种需要联合查询的信息同步到这个库,然后这个微服务就专门提供这类联表查询,适合实时性要求不高的分析场合。
这种处理方式,主要还是使用sql的方式去解决问题,即使用数据库引擎去处理数据关联的事情。
难点是:关联数据的实时同步,同步的准确性,以及服务的稳定性,对实现有着比较高的要求
适用范围:分布式微服务分库情况下,列表上的信息需要从其他一个或几个微服务数据库中而来
缺点:实施难度大,数据同步要求高,关联性管理梳理
四、基于数据中心
数据中心可以通过各种数据同步工具将关系型数据库中的数据抽取出来,放到hdf上去,可以根据关联性存到类sql的hive上,或者是列式数据库hbase上去,然后再通过API的 方式向外提供数据服务,当然也可以按照关联关系将数据放入es上去,基于es来提供数据服务。
基于数据中心的这种处理方式,可以比较好的实现数据服务的功能,但是也有它自己的不足,主要体现在实时性上。
难点是:如何保证数据的实施性,如何建立好数据的关联性,同样既要保证数据的准确性,还要保证数据的及时性,对实现有着比较高的要求
适用范围:单体服务,分布式微服务,DDD方式设计的领域服务中心均可
缺点:需要会大数据处理的人才,需要更多的服务器资源,需要更多的项目投入(时间),需要保证数据的准确性,一致性,及时性。
实时要求高的场合,上大数据,流处理,同样也是相关关联的数据以流的方式进行计算得到你用关系数据库必须联表才能得到的数据。
其他的方式,如在内存中进行分页搜索查询,使用CQRS方式,或者是其他,总之都可以解决数据联查,数据显示的问题
时间工具类:
MyBatis-plus框架,自动生成映射文件的工具会将MySQL中的datetime类型转化成java中的LocalDateTime类型
Java中LocalDate、LocalTime、LocalDateTime三个时间工具类的使用介绍:
在jdk8发布的时候,推出了LocalDate、LocalTime、LocalDateTime三个时间处理类,以此来弥补之前的日期时间类的不足,简化日期时间的操作,在jdk8之前,处理日期时间的类是Date、calendar,这两个不是线程安全的
新推出来的这三个类,与MySQL中的 日期时间类型正好对应,如果我们使用的是MySQL数据库的话,在插入相应字段的时候,都不需要再进行任何的转化了
LocalDateTime对应datetime
LocalTime对应time
LocalDate对应date
Mapper CRUD接口
只需要定义好实体类,创建一个接口,继承mp提供的BaseMapper,即可使用,mp会在MyBatis启动时,自动解析实体类和表的映射关系,并注入带有通用CRUD方法的mapper。
selectMaps:只查部分列,进行数据统计,这个方法会将查询结果封装为一个map,map的key为结果的列,value为值
当某个表的列特别多,而select时候只需要选取个别列时,查询出的结果也没必要封装成java实体类对象时(只查部分列,封装成实体后,实体对象中的很多属性会是null),则可以用selectmap,获取到指定的列后,再自行进行处理即可
selectObjs只会返回第一个字段(第一列)的值,其他字段会被舍弃
selectCount查询满足条件的总数,使用这个方法,不能调用QueryWrapper的select方法设置要查询的列了,这个方法会自动添加select count(1)