欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 明星 > MybatisPlus

MybatisPlus

2024/10/25 3:19:59 来源:https://blog.csdn.net/bossDDYY/article/details/142900740  浏览:    关键词:MybatisPlus

文章目录

  • 简介
  • 快速入门
    • 简单程序
    • 常用注解
    • 常见配置
  • 核心功能
    • 条件构造器
    • 自定义SQL
    • Service接口
      • 基本的使用
      • Lambda操作
      • 批量新增
  • 扩展功能
    • 代码生成
      • 代码
      • 插件
    • 静态工具
    • 逻辑删除
    • 枚举处理器
    • json处理器
  • 插件功能
    • 分页插件
    • 乐观锁

简介

MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率

特征

  • 无入侵
  • 强大的CRUD
  • 支持lambda
  • 支持主键自动生成
  • 内置分页插件

快速入门

简单程序

boot3的pom

<!--        <dependency>-->
<!--            <groupId>org.mybatis.spring.boot</groupId>-->
<!--            <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!--            <version>3.0.3</version>-->
<!--        </dependency>--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.7</version></dependency>

mapper继承mp提供的类

// 即制定了实体类,又制定了表的名字就是book 可以通过注解指定 @TableName()
public interface BookMapper extends BaseMapper<Book> {
}

测试案例

List<Book> books =bookMapper.selectList(null);System.out.println(books);

常用注解

MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息

实体类转换数据库表的规则(约定):

  • 类名驼峰转下划线作为表名
  • 名为id的字段作为主键
  • 变量名驼峰转下划线作为表的字段名

常见注解

  • @TableName:用来指定表名

  • @TableField:指定表的普通字段信息

    • 成员变量和数据库字段不一致
    • 成员变量以is开头,且是布尔值
    • 成员变量和数据库字段关键字冲突
    • 成员变量不是数据库字段

    在这里插入图片描述

  • @TableId:指定表中主键字段信息

    • type:设置主键属性的生成策略,参照IdType枚举,默认为雪花算法生成的id

      在这里插入图片描述

    • value:设置数据库主键的名称

常见配置

    # mybatis-plus继承了mybaits原生配置和一些自己独有的配置
mybatis-plus:mapper-locations: classpath*:/mapper/**/*.xml # Mapper.xml的文件地址 默认值type-aliases-package: com.itheima.mp.domain.po # 别名扫描包configuration:map-underscore-to-camel-case: true # 是否开启驼峰到下划线的映射,默认值cache-enabled: false # 是否开启二级缓存,默认值global-config:db-config:id-type: assign_id # id为雪花算法生成,默认值update-strategy: not_null # 更新策略:只更新非空字段,默认值table-prefix: # 表的前缀 比如指定了为tb_ 我们实体类为User 那么查询的数据库的表就是tb_user# 逻辑删除配置logic-delete-value: xxx # 指定代表逻辑删除的字段名logic-not-delete-value: 0 # 0代表没有被删除logic-delete-field: 1 # 1代表被删除

核心功能

条件构造器

mp支持各种复杂的where条件,可以满足日常开发的需求

在这里插入图片描述

//查询名字带着o,并且存款大于1000的LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.select(User::getId,User::getUsername,User::getInfo,User::getBalance).like(User::getUsername,"o").ge(User::getBalance,1000);List<User> users = userMapper.selectList(lqw);users.forEach(System.out::println);//将用户id为1 2 4的余额扣除200LambdaUpdateWrapper<User> luw = new LambdaUpdateWrapper<>();luw.setSql("balance = balance - 200").in(User::getId,1L,2L,4L);userMapper.update(null,luw);/*
QueryWrapper和LambdaQueryWrapper的区别就是后者使用Lambda语法,防止硬编码
原理还是利用反射机制获取对应的字段名
*/

自定义SQL

什么时候使用:where条件之外的部分使用mp不方便直接实现,需要拼接这样违背的企业开发的规范

利用mp的wrapper来构建复杂的where条件,然后自己定义SQL语句的剩下的部分

where条件交付给mp,其他的sql我们来自定义

操作步骤:

1、基于wrapper构建where条件

2、自定义SQL方法调用

		LambdaUpdateWrapper<User> lqw = new LambdaUpdateWrapper<User>().in(User::getId,1L,2L,4L);int amount = 200;userMapper.updateBalanceByIds(lqw,amount);

3、在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew

    void updateBalanceByIds(@Param("ew") LambdaUpdateWrapper<User> lqw, @Param("amount") int amount);

4、书写sql语句,使用wrapper条件

    <update id="updateBalanceByIds">update user<set>balance = balance - #{amount}</set>${ew.customSqlSegment}</update>

Service接口

在这里插入图片描述

基本的使用

  1. 自定义接口继承IService接口
  2. 自定义接口实现继承ServiceImpl
  3. 注意:指定泛型(mapper、entity)
/*接口继承IService
需要指定一个泛型:操作的实体类
*/
public interface IUserService extends IService<User> {
}/*实体类继续ServiceImpl
指定两个泛型
第一个:需要操作的mapper
第二个:对应的实体类*/
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}

Lambda操作

    //lambda的查找操作public List<User> queryUsers(UserQuery query) {return lambdaQuery().like(query.getName() != null, User::getUsername, query.getName()).eq(query.getStatus() != null, User::getStatus, query.getStatus()).gt(query.getMinBalance() != null, User::getBalance, query.getMinBalance()).lt(query.getMaxBalance() != null, User::getBalance, query.getMaxBalance()).list();}//lambda的更新操作int remainBalance = user.getBalance() - money;lambdaUpdate().set(remainBalance==0,User::getStatus,2).set(User::getBalance,remainBalance).eq(User::getId,id).eq(User::getBalance,user.getBalance())//防止并发安全问题.update();

批量新增

要在yml文件中增加参数rewriteBatchedStatements=true

jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true

在这里插入图片描述

扩展功能

代码生成

代码

模板:MyBatisPlus提供

数据库相关配置:读取数据库获取信息

开发者自定义配置:手工配置

导入依赖

        <!--代码生成器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.3.0</version></dependency><!--模板技术引擎--><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version></dependency>

在这里插入图片描述
在这里插入图片描述

插件

mybatisplus

静态工具

在这里插入图片描述

和IService相同,但是需要多传一个class字节码

使用场景:需要service之间相互注入使用Db,防止循环注入

比如

        lambdaUpdate().set(remainBalance==0,User::getStatus,2).set(User::getBalance,remainBalance).eq(User::getId,id).eq(User::getBalance,user.getBalance())//防止并发安全问题.update();//=>Db.lambdaUpdate(User.class).set(remainBalance==0,User::getStatus,2).set(User::getBalance,remainBalance).eq(User::getId,id).eq(User::getBalance,user.getBalance())//防止并发安全问题.update();

逻辑删除

逻辑删除就是基于代码的逻辑模拟删除效果,并不是真正删除数据

  • 在表中添加一个字段标记数据是否被删除
  • 当删除数据时把标记置为1
  • 查询时只查询标记为0的数据

MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

mybatis-plus:global-config:db-config:# 逻辑删除配置logic-delete-value: deleted # 指定代表逻辑删除的字段名,类型可以时Boolean或者Integerlogic-not-delete-value: 0 # 0代表没有被删除(默认为0)logic-delete-field: 1 # 1代表被删除(默认为1)

在这里插入图片描述

逻辑删除也存在一些问题:

  • 逻辑删除会导致数据库数据越来越多,影响查询效率
  • SQL中全都需要对逻辑删除字段做判断,影响查询效率

因此,不太推荐采用逻辑删除功能。如果数据不能删除,可以采用把数据迁移到其它表的办法

枚举处理器

在这里插入图片描述

1、配置枚举处理器

mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

2、配置枚举

public enum UserStatus {NORMAL(1,"正常"),FROZEN(2,"冻结"),;@EnumValue //代表value属性和数据库对应@JsonValue //设置给前端返回的值,默认时枚举的名字private final Integer value;private final String desc;UserStatus(Integer value, String desc) {this.value = value;this.desc = desc;}
}

json处理器

数据库保存的为json,但是我们java没有对应的json类型,我们希望自定义一个对象,让数据库的json自动对应,这就使用到json处理器

package com.itheima.mp.domain.po;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.itheima.mp.enums.UserStatus;
import lombok.Data;import java.time.LocalDateTime;@Data
@TableName(value = "user",autoResultMap = true)//自动结果集映射
public class User {@TableId(type = IdType.AUTO)private Long id;private String username;private String password;private String phone;//json和对象的转换@TableField(typeHandler = JacksonTypeHandler.class)private UserInfo info;private UserStatus status;private Integer balance;private LocalDateTime createTime;private LocalDateTime updateTime;
}

插件功能

在这里插入图片描述

分页插件

1、配置插件

    @Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){//1、初始化核心插件MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();//2、配置分页插件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();paginationInnerInterceptor.setMaxLimit(1000L);//设置分页上限//3、添加分页插件mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);//4、返回核心插件return mybatisPlusInterceptor;}

2、使用分页插件

    //简单的分页查询 void testPage(){Page<User> page = Page.of(1, 2);//页码 每页2个userService.page(page);System.out.println(page.getPages());//多少页System.out.println(page.getTotal());//多少数据page.getRecords().forEach(System.out::println);//数据}//带过滤条件的分页查询 使用lambdaQuery

对于 PageQuery和 PageDTO 的封装

@Data
public class PageQuery {private Integer pageNo;private Integer pageSize;private String sortBy;private Boolean isAsc;public <T>  Page<T> toMpPage(OrderItem ... orders){// 1.分页条件Page<T> p = Page.of(pageNo, pageSize);// 2.排序条件// 2.1.先看前端有没有传排序字段if (sortBy != null) {p.addOrder(new OrderItem(sortBy, isAsc));return p;}// 2.2.再看有没有手动指定排序字段if(orders != null){p.addOrder(orders);}return p;}public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc){return this.toMpPage(new OrderItem(defaultSortBy, isAsc));}public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {return toMpPage("create_time", false);}public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {return toMpPage("update_time", false);}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageDTO<V> {private Long total;private Long pages;private List<V> list;/*** 返回空分页结果* @param p MybatisPlus的分页结果* @param <V> 目标VO类型* @param <P> 原始PO类型* @return VO的分页对象*/public static <V, P> PageDTO<V> empty(Page<P> p){return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList());}/*** 将MybatisPlus分页结果转为 VO分页结果* @param p MybatisPlus的分页结果* @param voClass 目标VO类型的字节码* @param <V> 目标VO类型* @param <P> 原始PO类型* @return VO的分页对象*/public static <V, P> PageDTO<V> of(Page<P> p, Class<V> voClass) {// 1.非空校验List<P> records = p.getRecords();if (records == null || records.size() <= 0) {// 无数据,返回空结果return empty(p);}// 2.数据转换List<V> vos = BeanUtil.copyToList(records, voClass);// 3.封装返回return new PageDTO<>(p.getTotal(), p.getPages(), vos);}/*** 将MybatisPlus分页结果转为 VO分页结果,允许用户自定义PO到VO的转换方式* @param p MybatisPlus的分页结果* @param convertor PO到VO的转换函数* @param <V> 目标VO类型* @param <P> 原始PO类型* @return VO的分页对象*/public static <V, P> PageDTO<V> of(Page<P> p, Function<P, V> convertor) {// 1.非空校验List<P> records = p.getRecords();if (records == null || records.size() <= 0) {// 无数据,返回空结果return empty(p);}// 2.数据转换List<V> vos = records.stream().map(convertor).collect(Collectors.toList());// 3.封装返回return new PageDTO<>(p.getTotal(), p.getPages(), vos);}
}

乐观锁

拦截器

    @BeanMybatisPlusInterceptor mpInterceptor(){//定义mp拦截器MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();//添加乐观锁的拦截器mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mybatisPlusInterceptor;}

属性配置

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("book")
public class Book {@TableId(type = IdType.AUTO)private Integer id;private String name;//乐观锁 需要加拦截器@Versionprivate Integer version;@TableField(select = false)private Integer online;
}

测试

    @Testvoid testS(){Book user1 = bookMapper.selectById(1);//version=1Book user2 = bookMapper.selectById(1);//version=1user1.setName("平凡的世界  111");bookMapper.updateById(user1); //version=2user2.setName("平凡的世界  222");bookMapper.updateById(user2); //此时的version=1,修改失败}//并发操作可以进行控制

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com