MyBatisPlus(MP)

MyBatisPlus(MP)

第二部分承接上一篇关于dql编程控制)

2.查询投影-设置【查询字段、分组、分页】

2.1 查询结果包含模型类中部分属性
/*LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId, User::getName, User::getAge);*/
//或者
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("id", "name", "age", "tel");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
2.2 查询结果包含模型类中未定义的属性
QueryWrapper<User> lqw = new QueryWrapper<User>();
//起个别名
lqw.select("count(*) as count, tel");
//分组groupBy()
lqw.groupBy("tel");
//值得注意的就是这个selectMaps();
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);

其次注意的是count(*) as count,如果你使用的是LambdaQueryWrapper其实有些函数不支持的,怎么办了?先以前写sql语句一样

3. 查询条件设定

问题导入

多条件查询有哪些组合?

  • 范围匹配(> 、 = 、between)
  • 模糊匹配(like)
  • 空判定(null)
  • 包含性匹配(in)
  • 分组(group)
  • 排序(order)
  • ……
3.1 查询条件
  • 用户登录(eq匹配)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//等同于=
lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
User loginUser = userDao.selectOne(lqw);
System.out.println(loginUser);
  • 购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)

    between适合范围查询,而且这个不能具有适配性

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//范围查询 lt le gt ge eq between
lqw.between(User::getAge, 10, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
  • 查信息,搜索新闻(非全文检索版:like匹配)

    模糊匹配 like

    likeleft:

    likeright:

    这两个实际上指的是%的位置,left值的是j%,right值的是%j

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//模糊匹配 like
lqw.likeLeft(User::getName, "J");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
  • 统计报表(分组查询聚合函数)
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.select("gender","count(*) as nums");
qw.groupBy("gender");
List<Map<String, Object>> maps = userDao.selectMaps(qw);
System.out.println(maps);
3.2 查询API

更多查询条件设置参看 https://mybatis.plus/guide/wrapper.html#abstractwrapper

多查文档,观察参数

4. 字段映射与表名映射

问题导入

思考表的字段和实体类的属性不对应,查询会怎么样?

4.1 问题一:表字段与编码属性设计不同步
  • 在模型类属性上方,使用**@TableField**属性注解,通过==value==属性,设置当前属性对应的数据库表中的字段关系。
@TableField(value = "pwd")
    private String password;
4.2 问题二:编码中添加了数据库中未定义的属性
  • 在模型类属性上方,使用**@TableField注解,通过exist**属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。
@TableField(exist = false) //表示online字段不参与CRUD操作
4.3 问题三:采用默认查询开放了更多的字段查看权限
  • 在模型类属性上方,使用**@TableField注解,通过select**属性:设置该属性是否参与查询。此属性与select()映射配置不冲突。

    @TableField(value = "pwd",select = false)
        private String password;
    
4.4 问题四:表名与编码开发设计不同步
  • 模型类上方,使用**@TableName注解,通过value**属性,设置当前类对应的数据库表名称。

    其实就是相当于做映射关系

@Data
@TableName("tbl_user")
public class User {
    /*
        id为Long类型,因为数据库中id为bigint类型,
        并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型
     */
    private Long id;
    private String name;
    @TableField(value = "pwd",select = false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist = false) //表示online字段不参与CRUD操作
    private Boolean online;
}

4.DML编程控制

问题导入

主键生成的策略有哪几种方式?

不同的表应用不同的id生成策略

  • 日志:自增(1,2,3,4,……)
  • 购物订单:特殊规则(FQ23948AK3843)
  • 外卖单:关联地区日期等信息(10 04 20200314 34 91)
  • 关系表:可省略id
  • ……
1.1 id生成策略控制(@TableId注解)
  • 名称:@TableId

  • 位置:模型类中用于表示主键的属性定义上方

  • 作用:设置当前类中主键属性的生成策略

  • 相关属性

    type:设置主键属性的生成策略,值参照IdType枚举值

    • ID生成策略控制

    • AUTO():使用数据库id自增策略控制id生成

    • NONE(1):不设置id生成策略

    • INPUT(2):用户手工输入id

    • ASSIGN_ID(3):雪花算法生成id(可兼容数值型与字符串型)

    • ASSIGN_UUID(4):以UUID生成算法作为id生成策略

    import lombok.Getter;
    
    /**
     * 生成ID类型枚举类
     *
     * @author hubin
     * @since 2015-11-10
     */
    @Getter
    public enum IdType {
        /**
         * 数据库ID自增
         * <p>该类型请确保数据库设置了 ID自增 否则无效</p>
         */
        AUTO(0),
        /**
         * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
         */
        NONE(1),
        /**
         * 用户输入ID
         * <p>该类型可以通过自己注册自动填充插件进行填充</p>
         */
        INPUT(2),
    
        /* 以下2种类型、只有当插入对象ID 为空,才自动填充。 */
        /**
         * 分配ID (主键类型为number或string),
         * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
         *
         * @since 3.3.0
         */
        ASSIGN_ID(3),
        /**
         * 分配UUID (主键类型为 string)
         * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
         */
        ASSIGN_UUID(4);
    
        private final int key;
    
        IdType(int key) {
            this.key = key;
        }
    }
    

了解一下雪花算法

雪花算法(Snowflake Algorithm)是一种用于生成唯一ID的算法,由Twitter开发。它能够保证在分布式系统中生成的ID是唯一的,并且具有自增的特性。这种算法生成的ID是一个64位的长整型数字,具有以下特点:

  1. 时间戳:占用41位,可以表示从2010年到2286年的时间,大约140亿毫秒。
  2. 数据中心ID:占用5位,可以表示最多32个数据中心。
  3. 机器ID:占用5位,可以表示最多32台机器。
  4. 序列号:占用12位,是同一毫秒内生成的序列号,可以生成最多4096个ID。
  5. 特殊位:有1位特殊位,用于区分ID的正负。

雪花算法的工作原理是将时间戳、数据中心ID、机器ID和序列号组合起来生成一个唯一的ID。由于时间戳是递增的,所以生成的ID也是递增的,这对于某些需要按时间顺序排序的场景非常有用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果 每个实体类都写就很烦,所以采用配置

1.2 全局策略配置
mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id
      table-prefix: tbl_
表名前缀全局配置
table-prefix: tbl_

就是英语

2. 多记录操作(批量Delete/Select)

问题导入

MyBatisPlus是否支持批量操作?

2.1 按照主键删除多条记录
//删除指定多条数据
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);

userDao.deleteBatchIds(list);
2.2 根据主键查询多条记录
//查询指定多条数据
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
userDao.selectBatchIds(list);

3. 逻辑删除(Delete/Update)

逻辑删除实际上并没有被删除,微数据设置是否可用的字段,删除是设置状态为不可用,数据保留在数据库中

步骤

①:数据库表中添加逻辑删除标记字段

②:实体类中添加对应字段,并设定当前字段为逻辑删除标记字段

package com.itheima.domain;

import com.baomidou.mybatisplus.annotation.*;

import lombok.Data;

@Data
public class User {
    //逻辑删除字段,标记当前记录是否被删除
    @TableLogic
    private Integer deleted;
}

③:配置逻辑删除字面值

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tbl_
      # 逻辑删除字段名
      logic-delete-field: deleted
      # 逻辑删除字面值:未删除为0
      logic-not-delete-value: 0
      # 逻辑删除字面值:删除为1
      logic-delete-value: 1

逻辑删除本质:逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。

update tb1_user set deleted = 1 where id = ? and deleted = 0

如果你想查看全部的话就写sql语句

4.乐观锁(Update)

既然要说乐观锁,那就简单了解一下锁:

锁是一种同步机制,用于控制对共享资源的访问,以防止多个进程或线程同时修改资源,从而避免数据竞争和一致性问题

为了解决并发的问题

public class User {
    private Long id;
    private String name;
    private Integer version; // 版本号字段

    // 省略其他字段和getter/setter方法

    // 更新方法,包含乐观锁的检查
    public boolean updateUser(User user) {
        User dbUser = userRepository.findById(user.getId());
        if (dbUser.getVersion() == user.getVersion()) {
            // 执行更新操作
            user.setVersion(dbUser.getVersion() + 1);
            userRepository.save(user);
            return true;
        } else {
            // 版本冲突,更新失败
            return false;
        }
    }
}
1.乐观锁

概念:乐观锁是一种并发控制的方法,它适用于写入冲突较少的场景,其核心思想是在数据发生变更时,检测在读取数据后是否有其他事务已经修改了该数据。如果没有,那么继续执行变更操作;如果有,则放弃当前操作,重试或返回错误信息。乐观锁通常通过数据版本控制来实现

优点

  • 减少了锁的开销,提高了系统的并发性能。
  • 适用于写冲突较少的场景。

缺点

  • 可能会因为版本冲突导致更新失败,需要重试机制。
乐观锁案例
①:数据库表中添加锁标记字段
②:实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
package com.itheima.domain;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

@Data
public class User {

	private Long id;
	
    @Version
    private Integer version;
}

③:配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装
package com.itheima.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定义Mp拦截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();

        //2.添加乐观锁拦截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        
        return mpInterceptor;
    }
}

④:使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行
@Test
public void testUpdate() {
    /*User user = new User();
    user.setId(3L);
    user.setName("Jock666");
    user.setVersion(1);
    userDao.updateById(user);*/
    
    //1.先通过要修改的数据id将当前数据查询出来
    //User user = userDao.selectById(3L);
    //2.将要修改的属性逐一设置进去
    //user.setName("Jock888");
    //userDao.updateById(user);
    
    //1.先通过要修改的数据id将当前数据查询出来
    User user = userDao.selectById(3L);     //version=3
    User user2 = userDao.selectById(3L);    //version=3
    user2.setName("Jock aaa");
    userDao.updateById(user2);              //version=>4
    user.setName("Jock bbb");
    userDao.updateById(user);               //verion=3?条件还成立吗?不成立了,这条语句直接失效
    
} 

五、快速开发-代码生成器

1.基本

1.1.创建SpringBoot工程,添加代码生成器相关依赖,其他依赖自行添加

<!--代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>

<!--velocity模板引擎-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

1.2.编写代码生成器类

package com.itheima;

import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;

public class Generator {
    public static void main(String[] args) {
        //1. 创建代码生成器对象,执行生成代码操作
        AutoGenerator autoGenerator = new AutoGenerator();

        //2. 数据源相关配置:读取数据库中的信息,根据数据库表结构生成代码
        DataSourceConfig dataSource = new DataSourceConfig();
        dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        autoGenerator.setDataSource(dataSource);

         //3. 执行生成操作
        autoGenerator.execute();
    }
}

2. 开发者自定义配置
  • 设置全局配置
//设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java");    //设置代码生成位置
globalConfig.setOpen(false);    //设置生成完毕后是否打开生成代码所在的目录
globalConfig.setAuthor("zhouzhou");    //设置作者
globalConfig.setFileOverride(true);     //设置是否覆盖原始生成的文件
globalConfig.setMapperName("%sDao");    //设置数据层接口名,%s为占位符,指代模块名称
globalConfig.setIdType(IdType.ASSIGN_ID);   //设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
  • 设置包名相关配置
//设置包名相关配置
PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.aaa");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageInfo.setEntity("domain");    //设置实体类包名
packageInfo.setMapper("dao");   //设置数据层包名
autoGenerator.setPackageInfo(packageInfo);
  • 策略设置
//策略设置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tbl_user");  //设置当前参与生成的表名,参数为可变参数
strategyConfig.setTablePrefix("tbl_");  //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名  例如: User = tbl_user - tbl_
strategyConfig.setRestControllerStyle(true);    //设置是否启用Rest风格
strategyConfig.setVersionFieldName("version");  //设置乐观锁字段名
strategyConfig.setLogicDeleteFieldName("deleted");  //设置逻辑删除字段名
strategyConfig.setEntityLombokModel(true);  //设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);

说明:在资料中也提供了CodeGenerator代码生成器类,根据实际情况修改后可以直接使用。

内部的底层原理其实就是io操作
//设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);


- 设置包名相关配置

```java
//设置包名相关配置
PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.aaa");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageInfo.setEntity("domain");    //设置实体类包名
packageInfo.setMapper("dao");   //设置数据层包名
autoGenerator.setPackageInfo(packageInfo);
  • 策略设置
//策略设置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tbl_user");  //设置当前参与生成的表名,参数为可变参数
strategyConfig.setTablePrefix("tbl_");  //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名  例如: User = tbl_user - tbl_
strategyConfig.setRestControllerStyle(true);    //设置是否启用Rest风格
strategyConfig.setVersionFieldName("version");  //设置乐观锁字段名
strategyConfig.setLogicDeleteFieldName("deleted");  //设置逻辑删除字段名
strategyConfig.setEntityLombokModel(true);  //设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);

说明:在资料中也提供了CodeGenerator代码生成器类,根据实际情况修改后可以直接使用。

内部的底层原理其实就是io操作

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值