Mybatis-Plus使用手册

一、Mybatis-Plus

持久化框架

程序分为了持久化和非持久化的两个层次
持久层负责的是数据的持久化处理,将数据存储起来,例如Mybatis,hibernate
非持久层负责的是业务上的处理,流程处理,用户界面等,例如springFramwork等

1 . 快速使用

官网有快速开始实例,导入依赖,配置类开启MapperScanner配置类并设置扫描包路径,编写yml配置文件,导入database,以及mybatis-plus的sql显示配置

mybatis-plus:
  configuration:
 # 想在运行框查看sql语句结果需要添加的配置文件
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

创建Mapper接口,继承BaseMapper,基础的BaseMapper就包含了基础的CRUD操作。在需要与数据库映射的包上面添加**@TableName等注解**

2. 条件构造器(Wrapper)

当自带的CRUD不能满足操作的时候,需要进行一些条件操作的时候,所以需要条件构造器。条件构造器有一个公共的抽象接口------AbstractWrapper类。下面有三个实现大类,QueryWrapper,UpdateWrapper,AbstractLambdaWrapper,分别对查询,更新等条件进行补充。

条件表达式中的条件连接都是可以使用链式进行表示

a. 自定义sql

有时候sql的前半部分语句比较复杂,而且不推荐写在service层面,所以需要自定义sql进行书写

首先自定义的sql有一个参数名param,要么叫ew,要么加上注解**@Param(Constants.WRAPPER)**

三种方式:

1)注解SQL
直接使用mybatis的注解方式进行
2)Wrapper传参 + 注解SQL
不适合多表关联查询,查询字段过多
@Select("select * from car ${ew.customSqlSegment}")
Page<Car> selectByPrimaryKey(Page<Car> page, @Param(Constants.WRAPPER) QueryWrapper<Car> queryWrapper);

**优点:**可以动态的查询SQL,然后根据传输的数据动态进行查询操作,boolean condition

**缺点:**不适用于多表关联查询,查询字段过多代码比较凌乱

3)Wrapper传参+xml文件SQL
//(推荐方式)
1. 将条件部分通过Wrapper进行设设置,然后进行传递,后面xml文件中使用{ew.customerSqlSegment}进行添加
2. 设置xxxMapper对xxxXML文件进行绑定,最后通过XXXMapper实体进行调用

3. Lambda表达式

本质就是匿名方法,将函数方法作为参数传递到方法中,可以简化我们的代码,使代码更加灵活简洁

表达式分为两个部分: 左侧 参数部分 -> 右侧 逻辑方法部分

参数部分:要求和实现接口中的方法参数一致,包括参数的数量和类型

方法体:注意是否有返回值

a. 函数式接口

如果接口Interface中需要实现的抽象方法只有一个,这样的接口就是函数式接口.

//有且只有一个实现类必须要实现的抽象方法,所以是函数式接口
@FunctionalInterface  //可以判断是否是一个函数式子接口
interface Test{
    public void test();
}

@FunctionalInterface //这个也算函数式接口, 没有override注解
public interface IFunctionMulti<T extends Number> {
    void multi(List<T> numbers); // 抽象方法
    
    boolean equals(Object obj);  // Object中的方法,不算在这个接口里面
}

b. 参数和方法体的使用:

  • 当接口中参数只有一个的时候,可以不加小括号,如果没有参数或者多个参数必须加小括号
  • 当方法体只有一句的时候,可以省略大括号

4. IsService接口

a. 接口的方法图

image-20231031091824378

ServiceImpl类的使用

image-20231031091926792

1)使用步骤

//创建接口,继承Iservice方法,填写泛型
public interface UserService {
    // 添加一些 客制化 的方法
}

//创建一个service接口实现类,实现接口,并且继承ServiceImpl类,并填写泛型
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

b.lambda方法

// 1. service中的lambda快速查询
     @Test
    public void lambdaQueryTest() {
        String s = service.lambdaQuery()
                .eq(User::getId, 1)
                .one() // 最后需要添加的执行语句,有了它才会执行
                .toString();
        System.out.println(s);
    }

//2. service中的lambda的更新方法
    @Test
    public void lambdaUpdateTest() {
        boolean flag = true;
        service.lambdaUpdate()
                .set(flag, User::getName, "lgz")
                .eq(User::getId, 1)
                .update(); // 前面的都是条件和设置语句,这一句才是执行语句
    }

     

c. 程序中的乐观锁和悲观锁

乐观锁就是compare,在修改的时候查询一下现在的值是不是和上一次的值一样
悲观锁就是直接在方法上加@transactional
// 增加年纪
this.lambdaUpdate()
    .set(user.getEmail() != null, User::getEmail, newEmail)
    .eq(User::getEmail, user.getEmail()) //乐观锁,修改之前判断一下现在的和刚刚查到的是否一致
    .update();

d. 批处理的三种方式

  • 第一种就是for遍历一条一条插入的方式

  • 第二种使用batch自带的批处理,然后分成几次提交

  • 第三种利用sql的预编译机制,通过将所有的语句拼接起来,然后一次提交就可以完成插入

    1. 可以在service层中,使用for循环,将sql 的语句代码进行拼接起来,或者使用mysql的参数配置
    2. 特别还是网络中,将所有的语句拼接起来,然后统一的 返回到了后端,一次的请求就可以完成插入操作
    //这个需要在数据库中开启允许拼接sql的功能,在yaml文件的url后面加上一条属性 &rewriteBatchedStatements=true
    @Test
        public void batchInsertTest() {
            long start = System.currentTimeMillis();
            ArrayList<User> list = new ArrayList<>();
            for (int i = 6; i < 10000; i++) {
                User user = new User(Long.parseLong(i+""), "name-i"+i, i, null);
                list.add(user);
                if ( i % 1000 == 0) {
                    System.out.println(list.size());
                    service.saveBatch(list);
                    list.clear();
                }
            }
            long end = System.currentTimeMillis();
            System.out.println(start - end);
        }
    

5. 代码生成

使用工具自动生成我们对应数据库表中的entity,mapper,Service,xml文件

使用步骤:

下载Mybatisplus插件,然后填写mysql的url等信息,最后直接点击自动生成代码工具

image-20231030142247841

6. 静态工具

image-20231030142543083

其实就是静态生成的代码块,功能就是和前面的IService一样,但是就是需要多传一个参数泛型过去进行实现。

为什么使用:因为会涉及到多个表之间循环依赖,所以用这个就可以直接进行查询,避免循环依赖

image-20231030143009720
//使用:
Db.method() 使用

7.逻辑删除

a. 使用

重要的信息都不是真正的将数据进行删除,而是将表中多加一个字段isDeleted,然后删除Delete -> Update,查询需要加一个条件 isDeleted == 1

  • 配置yaml文件
image-20231030154134283
  • 可以在bean中设置**@TableLogic**注解,设置删除的信号等

b.逻辑删除失效的场景

逻辑删除在直接操作数据库的时候会失效,比如直接在mapper中定义Sql查询则会失效,或者直接在XML文件设置的查询操作

@Select("select * from user")
List<User> selectAllUser();

**避免:**应该避免直接操作数据库的操作,mybatis类似于在代码层面给平时的sql语句进行切片操作以后在末尾加上了逻辑删除的条件

8. 枚举处理器

数据表中的多种状态用数字表示缺乏可读性,不便于观看,所以定义一个枚举处理器来处理

BaseTypeHandler的类中有很多继承类,然后可以对数据库和PO类进行映射

image-20231030161353125

使用:

  1. 配置类configuration开启default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

    // 1. 创建枚举类
    public enum UserStatus {
        NNORMAL(1, "正常"),
        FREZEE(0, "冻结");
        @EnumValue
        private final int value;
        private final String des;
        UserStatus(int value, String des) {
            this.value = value;
            this.des = des;
        }
    }
    
    // 2.修改User的bean类,修改对应的属性为UserStatus类
    // 3.可以直接使用
        @Test
        public void testLogicDeleted() {
            service.lambdaQuery().eq(User::getIsDeleted, UserStatus.NNORMAL).list().forEach(System.out::println);
        }
    

9.JSON处理器

前端或者数据库中的字段有些是JSON格式存储的,如果想获取某一部分的的时候则可以定义一个UserInfo类去获取JSON的数据,然后进行使用

第一步:开启JSON映射

image-20231030164747227

第二步:做一个resultMap映射

image-20231030164857233

10. 分页插件

image-20231030165403810

使用

a . 创建配置类,添加分页插件

image-20231030165713546 image-20231030165453748

b.通用的分页实体

1 . 编写UserQuery 实体,保存用户的信息

@Data
@ApiModel(description = "用户查询实体")
public class UserQuery extends PageQuery{
    @ApiModelProperty("用户名关键字")
    private String name;
    @ApiModelProperty("用户状态")
    private UserStatus status;
}

2 . 定义一个统一共同的分页查询语句,包括了页码,页码大小,排序字段,是否升序

@Data
@ApiModel(description = "分页查询实体")
@AllArgsConstructor
@NoArgsConstructor
public class PageQuery {
    @ApiModelProperty("页码")
    private Integer pageNo;
    @ApiModelProperty("页码大小")
    private Integer pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private boolean isAsc;
 //然后UserQuery进行继承扩展

3 . 创建一个分页的信息DTO

@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {
    @ApiModelProperty("总条数")
    private Integer total;
    @ApiModelProperty("总页数")
    private Integer pages;
    @ApiModelProperty("集合")
    private List<T> list;
}

4 .分页

    @Test
    public void testPage() {
        UserQuery query = new UserQuery("lgz", UserStatus.NNORMAL);
        query.setPageNo(3); //查询第几页
        query.setPageSize(2); //页面大小
        String name = query.getName();
        Page<User> page = Page.of(query.getPageNo(), query.getPageSize()); //设置分页参数
        Page<User> p = service.lambdaQuery()       //查询
                .ge(User::getId, 1)
                .page(page);
        System.out.println("records total nums :" + p.getTotal());
        System.out.println("pages nums:" + p.getPages());
        List<User> user = page.getRecords();
        List<UserDTO> dtos = BeanUtil.copyToList(user, UserDTO.class);
        dtos.forEach(System.out::println);
        user.forEach(System.out::println);
    }

c. lambda分页查询

可以使用LambdaQuery表达式来直接进行page查询。

d .创建通用的方法

  • 创建QueryToPage方法,将前端传过来的Query对象转换为Page对象
//放到PageQuery类中
public <T> Page<T> toPage(OrderItem... items) {
	Page<T> page = Page.of(pageNo, pageSize);
	if (StrUtil.isNotBlank(sortBy)) {
		page.addOrder(new OrderItem(sortBy, isAsc));
	} else if (items != null) {
		page.addOrder(items);
	}
	return page;
}

public <T> Page<T> toPageDefaultSortByCreatedTime() {
	return toPage(new OrderItem("cre_time", false));
}
  • Page对象转化为DTO对象发给前端
public static <VO, PO> PageDTO<VO> of(Page<PO> p, Class<VO> clazz) {
	PageDTO<VO> dto = new PageDTO<>();
	dto.setPages(p.getPages());
	dto.setTotal(p.getTotal());
	List<PO> list = p.getRecords(); //设置PageDto对象返回
	if (CollUtil.isEmpty(list)) {  //糊涂包的功能
		dto.setList(Collections.emptyList());
		return dto;
	}
    dto.setList(BeanUtil.copyToList(list, clazz));  //hutool包转化为list对象
    return dto;
}

11 .乐观锁

a. 添加乐观锁插件

@Configuration
public class MybatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 注册乐观锁插件
        OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor = new OptimisticLockerInnerInterceptor();

        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor);
        return interceptor;
    }
}

b. 配置entity

添加@Version注解

如果需要修改的时候顺带修改其他的表单项,需要实现一个meta

@Component
public class MetaObjectHandler implements com.baomidou.mybatisplus.core.handlers.MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "version", ()->1, Integer.class);
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        
    }
}
  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值