说明
是一个 Mybatis 的增强工具,它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。
官网:
https://mp.baomidou.com/
https://mp.baomidou.com/guide/
https://baomidou.gitee.io/mybatis-plus-doc/#/api?id=globalconfiguration
特性:
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere ) 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
支持数据库: 任何能使用 mybatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。 mysql,oracle,db2,h2,hsql,sqlite,postgresql,sqlserver,Phoenix,Gauss ,clickhouse,Sybase,OceanBase,Firebird,cubrid,goldilocks,csiidb 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库 |
springboot整合mybatis-plus:
把mybatis的依赖换成mybatis-plus的依赖
<!-- mp 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
编写实体类
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("t_order")
public class Order {
/**
* order_id
*/
@TableId("order_id")
private Long orderId;
/**
* name
*/
@TableField("name")
private String name;
/**
* age
*/
@TableField("age")
private Integer age;
}
编写mapper文件
public interface OrderMapper extends BaseMapper<Order> {
}
编写service文件
public interface OrderService extends IService<Order> {
}
编写service实现类
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
}
编写业务实现类
@Resource
private OrderService orderService;
public void save() {
Order order = new Order();
order.setOrderId(System.currentTimeMillis());
order.setAge(10);
order.setName("xgss");
orderService.save(order);
}
public List < Order > list() {
LambdaQueryWrapper < Order > qw = new LambdaQueryWrapper < > ();
qw.eq(Order::getName, "xgss").lt(Order::getAge, 20);
return orderService.list(qw);
}
public void updateByCondition() {
LambdaUpdateWrapper < Order > uw = new LambdaUpdateWrapper < > ();
uw.eq(Order::getName, "xgss");
uw.set(Order::getAge, 11);
orderService.update(uw);
}
public void updateById() {
Order od = new Order();
od.setOrderId(111 L);
od.setName("xgss");
od.setAge(15);
orderService.updateById(od);
}
public void deleteByCondition() {
LambdaQueryWrapper < Order > qw = new LambdaQueryWrapper < > ();
qw.eq(Order::getName, "xgss").lt(Order::getAge, 20);
orderService.remove(qw);
}
public void deleteById() {
orderService.removeById(111 L);
在springboot启动类添加@MapperScan扫描dao层接口
@MapperScan("com.ctfo.dao.mapper")
mybatis-plus配置
官网:https://mp.baomidou.com/config/#localcachescope
配置结构:
mybatis-plus:
......
configuration:
......
global-config:
......
db-config:
......
configLocation
默认值:null
MyBatis 配置文件位置,如果您有单独的 MyBatis 配置,请将其路径配置到 configLocation 中.MyBatis Configuration
mapperLocations
默认值:["classpath*:/mapper/**/*.xml"]
MyBatis Mapper 所对应的 XML 文件位置,如果您在 Mapper 中有自定义方法(XML 中有自定义实现),需要进行该配置,告诉 Mapper 所对应的 XML 文件位置
Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)
typeAliasesPackage
默认值:null
MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 文件中可以直接使用类名,而不用使用全限定的类名(即 XML 中调用的时候不用包含包名)
typeAliasesSuperType
类型:Class<?>
默认值:null
该配置请和 typeAliasesPackage 一起使用,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
typeHandlersPackage
类型:String
默认值:null
TypeHandler 扫描路径,如果配置了该属性,SqlSessionFactoryBean 会把该包下面的类注册为对应的 TypeHandler
TypeHandler 通常用于自定义类型转换
typeEnumsPackage
类型:String
默认值:null
枚举类 扫描路径,如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
checkConfigLocation
Spring Boot Only
类型:boolean
默认值:false
启动时是否检查 MyBatis XML 文件的存在,默认不检查
executorType
Spring Boot Only
类型:ExecutorType
默认值:simple
通过该属性可指定 MyBatis 的执行器,MyBatis 的执行器总共有三种:
ExecutorType.SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句(PreparedStatement)
ExecutorType.REUSE:该执行器类型会复用预处理语句(PreparedStatement)
ExecutorType.BATCH:该执行器类型会批量执行所有的更新语句
configurationProperties
类型:Properties
默认值:null
指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
configuration
类型:Configuration
默认值:null
原生 MyBatis 所支持的配置
mapUnderscoreToCamelCase
类型:boolean
默认值:true
是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射。
此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body
如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
defaultEnumTypeHandler
类型:Class<? extends TypeHandler
默认值:org.apache.ibatis.type.EnumTypeHandler
默认枚举处理类,如果配置了该属性,枚举将统一使用指定处理器进行处理
aggressiveLazyLoading
类型:boolean
默认值:true
当设置为 true 的时候,懒加载的对象可能被任何懒属性全部加载,否则,每个属性都按需加载。需要和 lazyLoadingEnabled 一起使用。
autoMappingBehavior
类型:AutoMappingBehavior
默认值:partial
MyBatis 自动映射策略,通过该配置可指定 MyBatis 是否并且如何来自动映射数据表字段与对象的属性,总共有 3 种可选值:
AutoMappingBehavior.NONE:不启用自动映射
AutoMappingBehavior.PARTIAL:只对非嵌套的 resultMap 进行自动映射
AutoMappingBehavior.FULL:对所有的 resultMap 都进行自动映射
autoMappingUnknownColumnBehavior
类型:AutoMappingUnknownColumnBehavior
默认值:NONE
MyBatis 自动映射时未知列或未知属性处理策略,通过该配置可指定 MyBatis 在自动映射过程中遇到未知列或者未知属性时如何处理,总共有 3 种可选值:
AutoMappingUnknownColumnBehavior.NONE:不做任何处理 (默认值)
AutoMappingUnknownColumnBehavior.WARNING:以日志的形式打印相关警告信息
AutoMappingUnknownColumnBehavior.FAILING:当作映射失败处理,并抛出异常和详细信息
localCacheScope
类型:String
默认值:SESSION
Mybatis一级缓存,默认为 SESSION。
SESSION session级别缓存,同一个session相同查询语句不会再次查询数据库
STATEMENT 关闭一级缓存
单服务架构中(有且仅有只有一个程序提供相同服务),一级缓存开启不会影响业务,只会提高性能。 微服务架构中需要关闭一级缓存,原因:Service1先查询数据,若之后Service2修改了数据,之后Service1又再次以同样的查询条件查询数据,因走缓存会出现查处的数据不是最新数据
cacheEnabled
类型:boolean
默认值:true
开启Mybatis二级缓存,默认为 true。
callSettersOnNulls
类型:boolean
默认值:false
指定当结果集中值为 null 的时候是否调用映射对象的 Setter(Map 对象时为 put)方法,通常运用于有 Map.keySet() 依赖或 null 值初始化的情况。
通俗的讲,即 MyBatis 在使用 resultMap 来映射查询结果中的列,如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段,这就导致在调用到该字段的时候由于没有映射,取不到而报空指针异常。
当您遇到类似的情况,请针对该属性进行相关配置以解决以上问题。
configurationFactory
类型:Class<?>
默认值:null
指定一个提供 Configuration 实例的工厂类。该工厂生产的实例将用来加载已经被反序列化对象的懒加载属性值,其必须包含一个签名方法static Configuration getConfiguration()。
globalConfig
类型:com.baomidou.mybatisplus.core.config.GlobalConfig
默认值:GlobalConfig::new
MyBatis-Plus 全局策略配置
banner
类型:boolean
默认值:true
是否控制台 print mybatis-plus 的 LOGO
enableSqlRunner
类型:boolean
默认值:false
是否初始化 SqlRunner(com.baomidou.mybatisplus.extension.toolkit.SqlRunner)
sqlInjector
类型:com.baomidou.mybatisplus.core.injector.ISqlInjector
默认值:com.baomidou.mybatisplus.core.injector.DefaultSqlInjector
SQL注入器(starter 下支持@bean注入)
superMapperClass
类型:Class
默认值:com.baomidou.mybatisplus.core.mapper.Mapper.class
通用Mapper父类(影响sqlInjector,只有这个的子类的 mapper 才会注入 sqlInjector 内的 method)
metaObjectHandler
类型:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
默认值:null
元对象字段填充控制器(starter 下支持@bean注入)
identifierGenerator(since 3.3.0)
类型:com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator
默认值:com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator
Id生成器(starter 下支持@bean注入)
DbConfig
idType
类型:com.baomidou.mybatisplus.annotation.IdType
默认值:ASSIGN_ID
全局默认主键类型
tablePrefix
类型:String
默认值:null
表名前缀
schema
类型:String
默认值:null
logicDeleteField
类型:String
默认值:null
全局的entity的逻辑删除字段属性名,(逻辑删除下有效)
logicDeleteValue
类型:String
默认值:1
逻辑已删除值,(逻辑删除下有效)
logicNotDeleteValue
类型:String
默认值:0
逻辑未删除值,(逻辑删除下有效)
insertStrategy
类型:com.baomidou.mybatisplus.annotation.FieldStrategy
默认值:NOT_NULL
字段验证策略之 insert,在 insert 的时候的字段验证策略
字段上也可以配置验证策略,@TableField(strategy=FieldStrategy.NOT_EMPTY)
IGNORED:忽略,不进行验证,为null也会保存到数据库中
NOT_NULL:非 NULL,默认策略,验证字段为null不进行保存该字段
NOT_EMPTY:非空,验证字段非空时,保存该字段
updateStrategy
类型:com.baomidou.mybatisplus.annotation.FieldStrategy
默认值:NOT_NULL
字段验证策略之 update,在 update 的时候的字段验证策略
字段上也可以配置验证策略,@TableField(strategy=FieldStrategy.NOT_EMPTY)
IGNORED:忽略,不进行验证,为null也会保存到数据库中
NOT_NULL:非 NULL,默认策略,验证字段为null不进行保存该字段
NOT_EMPTY:非空,验证字段非空时,保存该字段
selectStrategy
类型:com.baomidou.mybatisplus.annotation.FieldStrategy
默认值:NOT_NULL
字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
常用注解
MyBatisPlus提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹配。
@TableName 实体类的类名和数据库表名不一致时指定数据库表名,标注在类上
@TableId
实体类的主键名称和表中主键名称不一致
type属性指定主键策略,MyBatisPlus在IdType类中定义了主键策略可选的值,标注在类的主键属性上
@TableField 实体类中的成员名称和表中字段名称不一致,
这种配置方式的主键策略只能在该表中生效,但是其他表还需要进行配置,为了避免冗余,麻烦,MybatisPlus提供了全局配置,在配置文件中配置主键策略即可实现
mybatis-plus:
mapper-locations: mapper/*.xml
global-config:
db-config:
id-type: auto
如果全局策略和局部策略全都设置,局部策略优先。
@Data
@TableName("t_user")
public class User {
@TableId("user_id")
private Long id;
@TableField("real_name")
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
排除实体类中非表字段
使用@TableField(exist = false)注解
@TableName
value 表名( 默认空 )
resultMap xml 字段映射 resultMap ID
@TableId
value 字段值(驼峰命名方式,该值可无)
type 主键 ID 策略类型( 默认 INPUT ,全局开启的是 ID_WORKER )
值 |
描述 |
AUTO |
自增(1,2,3,…)数据库中需要设置主键自增 |
NONE |
该类型为未设置主键类型 |
INPUT |
手动录入 |
ID_WORKER |
默认主键类型,全局唯一ID,Long类型的主键 |
UUID |
自动生成uuid 插入 |
ID_WORKER_STR |
字符串全局唯一ID |
@TableField
字段注解(非主键)
mybatis-plus默认开启自动驼峰命名规则(即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射。),如果数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
属性 |
类型 |
必须指定 |
默认值 |
描述 |
value |
String |
否 |
"" |
数据库字段名 |
el |
String |
否 |
"" |
映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... } 部分 |
exist |
boolean |
否 |
true |
是否为数据库表字段 |
condition |
String |
否 |
"" |
字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考(opens new window) |
update |
String |
否 |
"" |
字段 update set 部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于 el 属性) |
insertStrategy |
Enum |
N |
DEFAULT |
举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) |
updateStrategy |
Enum |
N |
DEFAULT |
举例:IGNORED: update table_a set column=#{columnProperty} |
whereStrategy |
Enum |
N |
DEFAULT |
举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> |
fill |
Enum |
否 |
FieldFill.DEFAULT |
字段自动填充策略 |
select |
boolean |
否 |
true |
是否进行 select 查询 |
keepGlobalFormat |
boolean |
否 |
false |
是否保持使用全局的 format 进行处理 |
jdbcType |
JdbcType |
否 |
JdbcType.UNDEFINED |
JDBC类型 (该默认值不代表会按照该值生效) |
typeHandler |
Class<? extends TypeHandler> |
否 |
UnknownTypeHandler.class |
类型处理器 (该默认值不代表会按照该值生效) |
numericScale |
String |
否 |
"" |
指定小数点后保留的位数 |
value说明:
指定数据库字段名,作用是让数据库的字段名和实体类的字段名能够对应的上
@TableField(“CASE_NO”)等同于@TableField(value=“CASE_NO”)
exist说明:
如果数据库没有实体类中的字段,指定为false,避免执行sql报错
condition说明:
@TableField(condition = SqlCondition.LIKE):表示该属性可以模糊搜索。
strategy说明:
insertStrategy,updateStrategy,whereStrategy;默认值DEFAULT,代表跟随全局配置默认是NOT NULL;代表字段为 null是不进行保存或查询条件
其他值:IGNORED-不进行判断,任何值都进行保存或作为查询条件;NOT_EMPTY-对字段进行非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断);NEVER-不加入sql
fill说明:
表示进行数据库操作时自动填充预先设置的值
如:
/**
* 记录创建人标识,记录用户的UUID
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 记录创建日期
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 记录最后更新人标识,记录用户的UUID
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 记录最后更新日期
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 删除标识,1:是,0:否
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
这样我们在具体业务中对实体类进行赋值就可以不用对这些公共字段进行赋值,在执行插入或者更新时就能自动赋值并插入数据库。
可以使用的值
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
那么要自动赋的值在哪里配置?
在项目的config包下新建自动填充处理类使其实现接口MetaObjectHandler
并重写其方法:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* create_time自动填充
*/
@Override
public void insertFill(MetaObject metaObject) {
Object testType = getFieldValByName("createTime", metaObject);
if(testType == null) {
setFieldValByName("createTime", new Date(), metaObject);
}
Object updateTime = getFieldValByName("updateTime", metaObject);
if(updateTime == null) {
setFieldValByName("updateTime", new Date(), metaObject);
}
}
/**
* updateTime 自动填充
*/
@Override
public void updateFill(MetaObject metaObject) {
Object testType = getFieldValByName("updateTime", metaObject);
if(testType == null) {
setFieldValByName("updateTime", new Date(), metaObject);
}
}
}
有些时候我们已经设置了属性的值。不想让mybatisPlus再自动填充,也就是说我们没有设置属性的值,mybatisPlus进行填充,如果设置了那么就用我们设置的值。这种情况我们只需要在填充类中提前获取默认值,然后使用该默认值就可以了。
public void updateFill(MetaObject metaObject) {
if(metaObject.hasSetter("updateTime")) {
Object updateTime = getFieldValByName("updateTime", metaObject);
if(Objects.nonNull(updateTime)) {
setUpdateFieldValByName("updateTime", updateTime, metaObject);
} else {
setUpdateFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
}
}
service查询
查询方法
// 查询所有
List < T > list();
// 查询列表
List < T > list(Wrapper < T > queryWrapper);
// 查询(根据ID 批量查询)
Collection < T > listByIds(Collection < ? extends Serializable > idList);
// 查询(根据 columnMap 条件)
Collection < T > listByMap(Map < String, Object > columnMap);
// 查询所有列表
List < Map < String, Object >> listMaps();
// 查询列表
List < Map < String, Object >> listMaps(Wrapper < T > queryWrapper);
// 查询全部记录
List < Object > listObjs();
// 查询全部记录
< V > List < V > listObjs(Function < ? super Object, V > mapper);
// 根据 Wrapper 条件,查询全部记录
List < Object > listObjs(Wrapper < T > queryWrapper);
// 根据 Wrapper 条件,查询全部记录
< V > List < V > listObjs(Wrapper < T > queryWrapper, Function < ? super Object, V > mapper);
// 无条件分页查询
IPage < T > page(IPage < T > page);
// 条件分页查询
IPage < T > page(IPage < T > page, Wrapper < T > queryWrapper);
// 无条件分页查询
IPage < Map < String, Object >> pageMaps(IPage < T > page);
// 条件分页查询
IPage < Map < String, Object >> pageMaps(IPage < T > page, Wrapper < T > queryWrapper);
// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper < T > queryWrapper);
QueryWrapper
条件构造器可以提供模糊查询,区间查询,分组查询,多表连接查询等
函数对应表
基本查询
QueryWrapper < User > wrapper = new QueryWrapper < User > ()
.eq(StringUtils.isNotBlank(user.getNickName()), "nick", user.getNickName())
.eq(user.getId() != null, "id", user.getId());
List < User > userList = userDao.selectList(wrapper);
in查询:
public void selectWrapper6() {
QueryWrapper < User > queryWrapper = new QueryWrapper < > ();
queryWrapper.in("age", Arrays.asList(30, 31, 34, 35));
List < User > userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
}
and和or合并写法
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.and(wrapper - > wrapper.isNull(“sim”).or().eq(“sim”, “”));
queryWrapper.eq(“is_delete”, “0”);
limit查询
QueryWrapper < CmsBanner > wrapper = new QueryWrapper < > ();
wrapper.orderByDesc("view_count");
//使用last方法拼接sql语句
wrapper.last("limit 8");
LambdaQueryWrapper
Lambda条件构造器
LambdaQueryWrapper < User > wrapper = new LambdaQueryWrapper < User > ()
.eq(StringUtils.isNotBlank(user.getNickName()), User::getNickName, user.getNickName())
.eq(user.getId() != null, User::getId, user.getId());
List < User > userList = userDao.selectList(wrapper);
使用区别
QueryWrapper 的列名匹配使用的是 “数据库中的字段名(一般是下划线规则)”
LambdaQueryWrapper 的列名匹配使用的是“Lambda的语法,偏向于对象”
LambdaQueryWrapper的优势:
不同写“列名”,而是使用纯java的方式,避免了拼写错误(LambdaQueryWrapper的写法如果有错,则在编译期就会报错,而QueryWrapper需要运行的时候调用该方法才会报错)
示例:
QueryWrapper<PbListBlack> sectionQueryWrapper = new QueryWrapper<>();
sectionQueryWrapper.eq("OPTYPE", 1);
sectionQueryWrapper.eq("BLTYPE", 1);
List<PbListBlack> pbListBlacks = iPbListBlackMapper.selectList(sectionQueryWrapper);
自定义sql,如果要使用自定义sql,就可以编写mapper.xml文件,之后再mapper文件中编写方法
排序查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.eq(StringUtils.isNotBlank(user.getNickName()), User::getNickName, user.getNickName())
.eq(user.getId() != null, User::getId, user.getId())
.orderByDesc( User::getId);
List<User> userList = userDao.selectList(wrapper);
分页查询
PageHelper.startPage(orderRefundQueryPageAO.getPageNo(),orderRefundQueryPageAO.getPageSize());
LambdaQueryWrapper<TradeRefundDO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TradeRefundDO::getApplyId,userId)
.like(StringUtils.isNotBlank(orderRefundQueryPageAO.getPlateNumber()),TradeRefundDO::getPlateNumber,orderRefundQueryPageAO.getPlateNumber())
.like(StringUtils.isNotBlank(orderRefundQueryPageAO.getContactInformation()),TradeRefundDO::getContactInformation,orderRefundQueryPageAO.getContactInformation())
.eq(StringUtils.isNotBlank(orderRefundQueryPageAO.getOrderCode()),TradeRefundDO::getOrderCode,orderRefundQueryPageAO.getOrderCode());
List<TradeRefundDO> tradeRefunds = this.baseMapper.selectList(queryWrapper);
PageInfo<TradeRefundDO> pageInfo = new PageInfo<>(tradeRefunds);
List<TradeRefundPageVO> collect = tradeRefunds.stream().map(tradeRefund -> {
TradeRefundPageVO vo = new TradeRefundPageVO();
BeanUtils.copyProperties(tradeRefund, vo);
return vo;
}).collect(Collectors.toList());
PageInfoVO<TradeRefundPageVO> pageInfoVO = new PageInfoVO();
pageInfoVO.setData(collect);
pageInfoVO.setTotal(pageInfo.getTotal());
limit查询
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getStatus,3)..last("limit 500");
and中套or查询
LambdaQueryWrapper<TradeDO> queryWrapper1 = new LambdaQueryWrapper<>();
queryWrapper1.and(i->i.eq(TradeDO::getChargeId,"").or().isNull(TradeDO::getChargeId));
指定返回字段查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id","name").like("name","张三").lt("age",40);
return userMapper.selectList(wrapper);
//查询所有用户
LambdaQueryWrapper<PhotoUser> wrapperUser = Wrappers.lambdaQuery();
wrapperUser.select(PhotoUser::getNickname,PhotoUser::getAppellation).or().in(PhotoUser::getId,userIdList);
List<PhotoUser> photoUsers = userService.list(wrapperUser);
service修改
修改方法
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper < T > updateWrapper);
// 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper < T > whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection < T > entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection < T > entityList, int batchSize);
// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper < T > updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection < T > entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection < T > entityList, int batchSize);
部分方法说明:
updateById:update……where id=?根据对象中的id进行保存,具体字段为null或空时是否报错跟字段的updateStrategy配置有关,如果全局配置和字段上配置都是默认值,为null时不进行保存字段值。
updateWrapper
可以用于查询条件构建,也可以用于更新设置值
提供查询条件,和更新内容
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name","shimin").set("age", 35);
Integer rows = userMapper.update(null, updateWrapper);
LambdaUpdateWrapper
提供查询条件,和更新内容
LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.eq(User::getRealName, "shimin").set(User::getAge, 34);
Integer rows = userMapper.update(null, lambdaUpdateWrapper);
其他总结
更新字段为null
方式一:调整全局的验证策略
注入配置 GlobalConfiguration 属性 fieldStrategy
方式二:调整字段验证注解
根据具体情况,在需要更新的字段中调整验证注解,如验证非空:
@TableField(strategy=FieldStrategy.NOT_EMPTY)
方式三:使用 UpdateWrapper (3.x)
使用以下方法来进行更新操作:
mapper.update(
new User().setName("mp").setAge(3),
Wrappers.<User>lambdaUpdate()
.set(User::getEmail, null) //把email设置成null
.eq(User::getId, 2)
);
//也可以参考下面这种写法
mapper.update(
null,
Wrappers.<User>lambdaUpdate()
.set(User::getAge, 3)
.set(User::getName, "mp")
.set(User::getEmail, null) //把email设置成null
.eq(User::getId, 2)
);
为null时设置为null
@TableField(strategy = FieldStrategy.IGNORED)
private String name;
update 时 column=column+1
@TableField(update="%s+1",updateStrategy=FieldStrategy.IGNORED)
private Integer column;
启动 mybatis 本身的 log 日志
# 方式一
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 方式二 application.yml 中增加配置,指定 mapper 文件所在的包
logging:
level:
com.baomidou.example.mapper: debug
使用mybatis-plus还是使用xml文件
-
SQL 生成时机:
- MyBatis-Plus:它为常见的 CRUD 操作提供了内置的方法,并在运行时为这些方法生成 SQL。这意味着对于这些常见操作,你不需要自己写 SQL。
- Mapper.xml:使用 Mapper.xml,你提前定义好了 SQL。这意味着 SQL 在应用启动时已经准备好了,不需要额外的运行时解析和生成。
-
性能差异:
- 实际上,两者之间的性能差异很小。对于简单的 CRUD 操作,MyBatis-Plus 可能会产生微小的额外开销,因为它需要在运行时生成 SQL。但这个开销是可以忽略不计的,特别是在大型应用中,与数据库查询时间相比,这个时间开销几乎可以忽略不计。
- 当处理复杂的 SQL 时,手写的 SQL(在 Mapper.xml 中)可能更有优势,因为你可以优化特定的查询。
-
开发效率和维护性:
- MyBatis-Plus:它的自动 CRUD 生成可以大大提高开发速度,特别是对于简单的数据库操作。此外,它还提供了其他有用的功能,如条件构造器,可以使代码更加整洁。
- Mapper.xml:虽然需要手写 SQL,但这为开发人员提供了更大的灵活性。对于需要手动优化的复杂查询,这可能是更好的选择。
结论: 从性能的角度看,两者之间的差异微乎其微。主要的差异在于开发效率、功能和灵活性。根据项目的具体需求选择合适的工具是关键。如果项目中包含大量的标准 CRUD 操作,那么 MyBatis-Plus 可能更有优势。而对于需要手动优化和高度定制的查询,手写 SQL 和使用 Mapper.xml 可能是更好的选择。
多数据源支持
-
引入相关依赖:首先,确保你的项目中引入了 MyBatis-Plus 和数据库驱动的依赖。
-
配置数据源:在你的 Spring Boot 配置文件(通常是
application.properties
或application.yml
)中,配置多个数据源。每个数据源都需要配置数据源类型、连接信息等。例如,在
application.yml
中配置两个数据源:
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/primary_db
username: primary_user
password: primary_password
driver-class-name: com.mysql.cj.jdbc.Driver
secondary:
url: jdbc:mysql://localhost:3306/secondary_db
username: secondary_user
password: secondary_password
driver-class-name: com.mysql.cj.jdbc.Driver
配置数据源连接池:为每个数据源配置连接池,例如使用 HikariCP 或其他连接池库。
配置 MyBatis-Plus:在 Spring Boot 配置类中配置 MyBatis-Plus 的 SqlSessionFactory
和 SqlSessionTemplate
,并将数据源作为参数传递。
@Bean
@Primary
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// 配置 MyBatis 相关设置
return bean.getObject();
}
@Bean
@Primary
public SqlSessionTemplate primarySqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
// 配置第二个数据源的 SqlSessionFactory 和 SqlSessionTemplate 类似
使用 @MapperScan
注解:在你的 Spring Boot 主应用程序类上使用 @MapperScan
注解来指定 MyBatis Mapper 接口的包路径。
@SpringBootApplication
@MapperScan(basePackages = "com.example.primary.mapper", sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
创建 Mapper 接口:创建 MyBatis Mapper 接口,并在方法上使用 @Mapper
注解指定数据源,或者在方法中使用 @Select
注解来指定 SQL 语句的数据源。
@Mapper
@Primary // 指定数据源
public interface PrimaryMapper {
@Select("SELECT * FROM primary_table")
List<PrimaryEntity> findAll();
}
remove方法
常用的有removeById,remove,removeByIds方法,默认是进行物理删除
如果要全局设置逻辑删除,可以在配置文件中进行配置如:
mybatis-plus:
global-config:
db-config:
#update-strategy: IGNORED
logic-delete-field: status
logic-delete-value: 0
logic-not-delete-value: 1