mybatis plus

mybatis plus



mybatis 快速实现

导入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>

在 dao 层,继承 BaseMapper 加泛型

@Mapper
public interface UserDao extends BaseMapper<User> {

}

新增

User user = new User();
int i = userDao.insert(user);

根据 ID 删除

int i = userDao.deleteById(-306130942);

根据 ID 修改

必须传入 ID 字段
它会根据 ID 修改,对象的数值
对象的空字段,不修改

@Test
void testUpdate(){
    User user = new User();
    user.setId(1);
    user.setName("Tom");
    int i = userDao.updateById(user);
}

根据 ID 查数据

@Test
void testById() {
    User user = userDao.selectById(2);
    System.out.println(user);
}

查全部数据

@Test
void contextLoads() {
    List<User> users = userDao.selectList(null);
    System.out.println(users);
}

service 层创建dao层接口,进行调用
QueryWrapper<planClient> 可以做过滤条件
创建这个方法指定

@Override
public List<planClient> getPlanClient() {
	// 
    QueryWrapper<planClient> wrapper = new QueryWrapper<>();
    return planClientDao.selectList(wrapper);
}

分页实现

先创建 Page 对象,两个属性,看第几页,看几条数据

@Test
void testGetByPage() {
    Page page = new Page(2, 2);
    userDao.selectPage(page, null);
    System.out.println("当前页码值:" + page.getCurrent());
    System.out.println("每页显示数:" + page.getSize());
    System.out.println("一共多少页:" + page.getPages());
    System.out.println("共多少数据:" + page.getTotal());
    System.out.println("数据:" + page.getRecords());
}

这个时候打印发现,数据查询的是全部

添加一个拦截器,就好了。拦截对象是 Page lei

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        // 1. 创建 MybatisPlusInterceptor 拦截器对象
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 2. 添加分页拦截器
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

Mybatis 日志打印

在 yml 配置文件里

#打印SQL日志到控制台
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

DQL 编程控制

条件查询(lt, gt)

mybatisPlus 里有一个 wrapper 类,这个就是构建查询条件的

第一种方式
容易出错,因为字段是字符串形式的

@Test
void contextLoads() {
    // 创建对象
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // lt 是小于,表示小于十八的
    wrapper.lt("age", 18);
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

第二种方式

@Test
void contextLoads() {
    // 创建对象
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // lt 是小于,表示小于十八的,使用 lambda 表示字段
    wrapper.lambda().lt(User::getAge, 10);
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

第三种方式
简化上面的

@Test
void contextLoads() {
    // 创建lambda对象
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // lt 是小于,表示小于十八的
    wrapper.lt(User::getAge, 10);
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

多条件查询 and or

and 关系
大于10岁并且小于16岁

@Test
void contextLoads() {
    // 创建对象
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // 查询 大于10岁小于16岁
    wrapper.lt(User::getAge, 16).gt(User::getAge, 10);
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

加括号的方式 and

  • ne 不等于
  • .and(wr -> 等于里面的条件加括号
// 判断是否存在重复的
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.and(wr -> wr.ne(SysUser::getId, sysUser.getId()))
.and(wr -> wr.eq(SysUser::getUserName, sysUser.getUserName()).or().eq(SysUser::getNickName, sysUser.getNickName()));

or 关系 or()
小于 10岁 或者 大于30岁

@Test
void contextLoads() {
    // 创建对象
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // 查询 大于30岁或者小于10岁
    wrapper.lt(User::getAge, 10).or().gt(User::getAge, 30);
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

处理空值

如果表达式为 true 才进行该字段查询

wrapper.lt (布尔表达式, 字段, 值)

在这里插入图片描述

需要加入模型类继承domain 类,表示上限和下限,一般是数值类型

wrapper 后面的条件如果不等于空才会进行条件查询

@Test
void contextLoads() {
    UserQuery query = new UserQuery();
    /*模拟页面传递的数据*/
    query.setAge(10);
    query.setAge2(30);
    // 创建对象
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // 查询 大于30岁或者小于10岁
    wrapper.lt(query.getAge() != null, User::getAge, query.getAge())
            .or().gt(query.getAge2() != null, User::getAge, query.getAge2());
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

查询投影

用法1:我们只查询某些字段

方式一:

@Test
void contextLoads() {
    // 创建对象
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // 只查询id,name,age
    wrapper.select(User::getId,User::getName,User::getAge);
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

方式二:

@Test
void contextLoads() {
    // 创建对象
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // 只查询id,name,age
    wrapper.select("id","name","age");
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

用法2:我们查询总数

@Test
void contextLoads() {
    // 创建对象
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // 查询总数
    wrapper.select("count(*) as count");
    // 调用 selectMaps转换 map
    List<Map<String, Object>> maps = userDao.selectMaps(wrapper);
    System.out.println(maps);
}

用法3:还可以分组

@Test
void contextLoads() {
    // 创建对象
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // 查询总数
    wrapper.select("count(*) as count, tel");
    // 分组
    wrapper.groupBy("tel");
    // 调用 selectMaps转换 map
    List<Map<String, Object>> maps = userDao.selectMaps(wrapper);
    System.out.println(maps);
}

查询条件符号

查询单个(selectOne)

@Test
void contextLoads() {
    // 创建对象
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // eq 等同于 ==
    wrapper.eq(User::getName, "Jerry").eq(User::getPassword, "Jerry");
    // 调用 selectOne 查询单个用户
    User user = userDao.selectOne(wrapper);
    System.out.println(user);
}

范围查询(between)

@Test
void contextLoads() {
    // 创建对象
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // between(10,30) 10到30之间
    wrapper.between(User::getAge, 10, 30);
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

模糊匹配(like)

其中 like 是前后都有百分号,likeLeft 和 likeRight 是一边有

@Test
void contextLoads() {
    // 创建对象
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // like 模糊查询
    wrapper.like(User::getName,"j");
    List<User> users = userDao.selectList(wrapper);
    System.out.println(users);
}

映射关系

表名对应不上时

需要在类上面加 @TableName

@TableName("数据库表名")
public class User {
    private Integer id;
    private String name;
    private String password;

字段名对应不上时

@TableName("tb_user")
public class User {
    private Integer id;
    private String name;
    @TableField(value = "数据库表中字段")
    private String password;

javaBean 表字段多于数据库表时

因为这个字段是新增的,数据库表并没用,会报错

不知道这个列,未知,找不到
Unknown column ‘online’ in ‘field list’

加上 @TableField(exist = false),表示不存在

@TableName("tb_user")
public class User {
    private Integer id;
    private String name;
    @TableField(value = "pwd")
    private String password;
    // 不存在的字段
    @TableField(exist = false)
    private Integer online;
}

不参与查询

给字段加 select = false

@TableField(value = "pwd", select = false)
private String password;

因为不参与查询,数据将为空

DML编程控制

在这里插入图片描述

id 生成策略

配置 主键自增,默认数据库的形式
@TableId(type = IdType.AUTO)

@TableName("tb_user")
public class User {
	// 数据库默认 id 自增
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
    @TableField(value = "pwd", select = false)
}

设置完上面的,会发现数据库表 ID 数据有数据了

自己生成 ID (INPUT)

@TableId(type = IdType.INPUT)
需要关闭数据库自增
当新增数据的时候,对象里ID字段必须合法,有数据

@TableName("tb_user")
public class User {
    @TableId(type = IdType.INPUT)
    private Integer id;
}

雪花算法 (ASSIGN_ID)

同自己生成

  1. 如果自己传 ID 了,那么使用自己的 ID
  2. 如果没有传递,他自动生成了一个 ID
@Test
void contextLoads() {
    User user = new User();
	// user.setId(667);
    user.setName("高");
    user.setAge(17);
    user.setPassword("123");
    user.setTel("13633103713");
    int insert = userDao.insert(user);
    System.out.println(insert);
}

==> Preparing: INSERT INTO tb_user ( id, name, pwd, age, tel ) VALUES ( ?, ?, ?, ?, ? )

可以看出下面这个ID 太长了,怎么生成的呢

==> Parameters: -796807167(Integer), 高(String), 123(String), 17(Integer), 13633103783(String)

雪花算法生成原理

生成一个串,64 的二进制,也就是一个 long 的值
在这里插入图片描述
时间戳并不是当前时间的算法

全局设置

ID 生成策略

在 yml 里

mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id
表名前缀
mybatis-plus:
  global-config:
    db-config:
      # ID 生成策略
      id-type: assign_id
      # 表名前缀自动添加
      table-prefix: tb_

批量删除(deleteBatchIds)

@Test
void contextLoads() {
    List<Integer> list = new ArrayList<>();
    // 添加数据
    Collections.addAll(list, -1279160319, -796807167, -624852991);
    // 以集合方式删除数据
    int i = userDao.deleteBatchIds(list);
    System.out.println(i);
}

批量查询(selectBatchIds)

@Test
void contextLoads() {
    List<Integer> list = new ArrayList<>();
    // 添加数据
    Collections.addAll(list, 2, 1, 3);
    // 以集合方式查询数据
    List<User> users = userDao.selectBatchIds(list);
    System.out.println(users);
}

Map方式新增数据

逻辑删除(@TableLogic)

为数据设置是否可同状态字段,删除时状态为不可用状态,数据保留在数据库中

  1. 在数据库里添加字段,deleted
  2. 在JavaBean 里加字段 deleted 保持和数据库一致

注:必须和数据库完全保持一致,数据库 1 是删除,这里就是 1

// value 表示未删除,正常,delval 表示删除
@TableLogic(value = "0", delval = "1")
private Integer deleted;

此时:在运行删除命令,修改的将是字段了,不会删除啦

@Test
void contextLoads() {
    int i = userDao.deleteById(1);
    System.out.println(i);
}

可以看到执行的命令,是 update
==> Preparing: UPDATE tb_user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 1(Integer)

细节:如果修改了删除的话,查找的时候默认会不查询已经逻辑删除的字段

全局修改逻辑删除字段
mybatis-plus:
  global-config:
    db-config:
      # 表示删除字段
      logic-delete-field: deleted
      # 表示没有删除的值
      logic-not-delete-value: 0
      # 表示已经删除的值
      logic-delete-value: 1

优化日志

关闭 logback 启动信息

创建一个 logback.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration></configuration>

关闭 mybatis banner信息

mybatis-plus:
  global-config:
    banner: false

关闭 boot banner信息

spring:
  main:
    banner-mode: off

乐观锁(并发量不超过2000)

  1. 通常我们会在数据库里加字段,表示当前是否加锁,version
  2. 如果version = 1,别人拿到的也是 = 1,那么就不能修改,修改完并加1
  3. 这个锁是累加的,需要拦截器拦截一下
@Version
private Integer version;

拦截器设置

@Configuration
public class mybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        // 创建拦截器对象
        MybatisPlusInterceptor mpi = new MybatisPlusInterceptor();
        // 添加乐观锁拦截器
        mpi.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mpi;
    }
}

执行

@Test
void contextLoads() {
	// 先把version 信息 查询出来
    User user = userDao.selectById(2);
    // 更新
    user.setName("Jerry888");
    int insert = userDao.updateById(user);
    System.out.println(insert);
}

==> Preparing: UPDATE tb_user SET name=?, age=?, tel=?, version=? WHERE id=? AND version=? AND deleted=0

==> Parameters: Jerry888(String), 4(Integer), 16688886666(String), 2(Integer), 2(Integer), 1(Integer)
<== Updates: 1
可以看到我们更新的时候,第一个线程会把version 加一
当第二个线程再更新的时候,就查询不出来了,会更新失败

注意:有时候写了这个字段就要使用它,否则会报异常,也看不出来是啥错误,只写着正在关闭spring事务

代码生成器

新增两个坐标

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

建立一个代码生成类

执行此类,即可快速生成

public class CodeGenerator {
    // 代码生成类
    public static void main(String[] args) {
        // 配置数据库信息
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatisplus_db", "root", "1234")
                //全局配置
                .globalConfig((scanner, builder) -> {
                    // 设置作者
                    builder
                            // 开启 swagger 模式
//                            .enableSwagger()
                            // 覆盖但是已经过时,已经失效 建议使用3.5.1
                            .fileOverride()
                            //指定输出目录
                            .outputDir(System.getProperty("user.dir") + "/src/main/java")
                            // 默认打开目录,加上不打开
                            .disableOpenDir()
                            // 设置作者
                            .author("作者");
                })
                .packageConfig((scanner, builder) -> {
                    // 设置包名, 数据层和实体层
                    builder.parent("com.aaa").mapper("dao").entity("domain");
                })
                // 策略配置
                .strategyConfig((scanner, builder) -> {
                    // 设置生成ID策略
                    builder.entityBuilder().idType(IdType.ASSIGN_ID)
                            // 乐观锁字段
                            .versionPropertyName("version")
                            // 逻辑删除字段
                            .logicDeletePropertyName("deleted")
                            // 开启Lombok
                            .enableLombok();
                    // 设置数据接口名,%s为占位符
                    builder.mapperBuilder().formatMapperFileName("%sDao");
                    // 开启 rest风格
                    builder.controllerBuilder().enableRestStyle();
                    // 指定打印表
                    builder.addInclude("menu");
                })
                .execute();

    }
}

业务层简便写法

查询单个

CommunicationTerminal terminal = query().select("terminal_number").eq("id", id).one();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值