MyBatisPlus

MyBatisPlus

@TableName

@TableId

@TableField

@TableLogic

QuerryWapper

枚举

多数据源

MyBatisX插件

1、简介

MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为 简化开发、提高效率而生。

2、常用注解

1、@TableName

如果所建实体类的类名小写与操作的表名一样时候不需要设置。否侧得设置@TableName

否则会出现异常Table 'mybatis_plus.user' doesn't exist

设置表的默认前缀
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_

表的列名同理

2、@TableId

MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认 基于雪花算法的策略生成id

但是实体类和表中表示主键的不是id,而是其他字段,例如uid,MyBatis-Plus不会自动识别uid为主 键列

通过@TableId解决问题

在实体类中uid属性上通过@TableId将其标识为主键,即可成功执行SQL语句

@TableId的value属性

若实体类中主键对应的属性为id,而表中表示主键的字段为uid,此时若只在属性id上添加注解 @TableId,则抛出异常Unknown column ‘id’ in ‘field list’,即MyBatis-Plus仍然会将id作为表的 主键操作,而表中表示主键的是字段uid 此时需要通过@TableId注解的value属性,指定表中的主键字段,@TableId(“uid”)或 @TableId(value=“uid”)

@TableId的type属性

type属性用来定义主键策略

描述
IdType.ASSIGN_ID(默 认)基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
IdType.AUTO使用数据库的自增策略,注意,该类型请确保数据库设置了id自增, 否则无效
配置全局主键策略:
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
# 配置MyBatis-Plus的主键策略
id-type: auto

3、@TableField

MyBatis-Plus在执行SQL语句时,要保证实体类中的属性名和 表中的字段名一致

若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格 例如实体类属性userName,表中字段user_name 此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格 相当于在MyBatis中配置

若实体类中的属性和表中的字段不满足情况1 例如实体类属性name,表中字段username 此时需要在实体类属性上使用@TableField(“username”)设置属性所对应的字段名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LUwGXGrs-1668779953428)(D:\typora\java路线学习\SSM\图片\image-20221118203601017.png)]

###

4、@TableLogic

逻辑删除

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据

  • 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库 中仍旧能看到此条数据记录

  • 使用场景:可以进行数据恢复

操作:

1、数据库中创建逻辑删除状态列,设置默认值为0

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPHbKOLA-1668779953429)(D:\typora\java路线学习\SSM\图片\image-20221118203842224.png)]

2、实体类中添加逻辑删除属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JG0NUc3F-1668779953429)(D:\typora\java路线学习\SSM\图片\image-20221118203905554.png)]

3、测试

测试删除功能,真正执行的是修改

UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0

测试查询功能,被逻辑删除的数据默认不会被查询

SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0

3、条件构造器

1、wapper

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UOomKOwh-1668779953430)(D:\typora\java路线学习\SSM\图片\image-20221118204059543.png)]

2、QuerryWapper

组装查询条件

public void test01(){
//执行SQL:SELECT uid AS id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
    //查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("username","a").between("age",20,30).isNotNull("email");
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}

组装排序条件

public void test02(){
    //查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.orderByDesc("age").orderByAsc("id");
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}

组装删除条件

public void test03(){
    //删除邮箱地址为null的用户信息
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.isNull("email");
    int result = userMapper.delete(queryWrapper);
    System.out.println(result > 0 ? "删除成功!" : "删除失败!");
    System.out.println("受影响的行数为:" + result);
}

条件的优先级

**执行SQL:**UPDATE t_user SET username=?, email=? WHERE is_deleted=0 AND (username LIKE ? AND (age > ? OR email IS NULL))

public void test05(){
    //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.like("username","a").and(i->i.gt("age",20).or().isNull("email"));
    User user = new User();
    user.setName("Vz7797");
    user.setEmail("test@ss8o.com");

    int result = userMapper.update(user, updateWrapper);
    System.out.println(result > 0 ? "修改成功!" : "修改失败!");
    System.out.println("受影响的行数为:" + result);
}

组装select子句

**执行SQL:**SELECT username,age,email FROM t_user WHERE is_deleted=0

public void test06(){
    //查询用户的用户名、年龄、邮箱信息
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.select("username","age","email");
    List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
    maps.forEach(System.out::println);
}

实现子查询

**执行SQL:**SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (uid IN (select uid from t_user where uid <= 100)

public void test07(){
    //查询id小于等于100的用户信息
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.inSql("uid", "select uid from t_user where uid <= 100");
    List<User> list = userMapper.selectList(queryWrapper);
    list.forEach(System.out::println);
}

3.UpdateWrappe

UpdateWrapper不仅拥有QueryWrapper的组装条件功能,还提供了set方法进行修改对应条件的数据库信息

public void test08(){
    //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.like("username","a").and( i -> i.gt("age",20).or().isNull("email")).set("email","svip@qq.com");
    int result = userMapper.update(null, updateWrapper);
    System.out.println(result > 0 ? "修改成功!" : "修改失败!");
    System.out.println("受影响的行数为:" + result);
}

4.condition

在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则一定不能组装,以免影响SQL执行的结果

public void test10(){
    String username = "a";
    Integer ageBegin = null;
    Integer ageEnd = 30;
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like(StringUtils.isNotBlank(username), "user_name", username)
        .ge(ageBegin != null, "age", ageBegin)
        .le(ageEnd != null, "age", ageEnd);
    List<User> list = userMapper.selectList(queryWrapper);
    list.forEach(System.out::println);
}

5、LambdaQueryWrapper

避免使用字符串表示字段,防止运行时错误

public void test11(){
    String username = "a";
    Integer ageBegin = null;
    Integer ageEnd = 30;
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.like(StringUtils.isNotBlank(username), User::getName, username)
        .ge(ageBegin != null, User::getAge, ageBegin)
        .le(ageEnd != null, User::getAge, ageEnd);
    List<User> list = userMapper.selectList(queryWrapper);
    list.forEach(System.out::println);
}

6、LambdaUpdateWrapper

public void test12(){
    //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.like(User::getName, "a")
        .and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));
    updateWrapper.set(User::getName, "小黑").set(User::getEmail,"abc@atguigu.com");
    int result = userMapper.update(null, updateWrapper);
    System.out.println("result:"+result);
}

4、插件

1、分页插件

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

添加配置类

@Configuration
@MapperScan("com.atguigu.mybatisplus.mapper") //可以将主类中的注解移到此处
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//数据库的类型
		return interceptor;
	}
}

测试

@Test
public void testPage(){
//设置分页参数
    Page<User> page = new Page<>(1, 5);
    userMapper.selectPage(page, null);
//获取分页数据
    List<User> list = page.getRecords();
    list.forEach(System.out::println);
    System.out.println("当前页:"+page.getCurrent());
    System.out.println("每页显示的条数:"+page.getSize());
    System.out.println("总记录数:"+page.getTotal());
    System.out.println("总页数:"+page.getPages());
    System.out.println("是否有上一页:"+page.hasPrevious());
    System.out.println("是否有下一页:"+page.hasNext());
}

2、乐观锁

场景

一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太 高,可能会影响销量。又通知小王,你把商品价格降低30元。 此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王 也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就 完全被小王的覆盖了。 现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1 万多。

上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过 了,则重新取出的被修改后的价格,150元,这样他会将120元存入数据库。 如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证 最终的价格是120元

模拟修改冲突

@Test
public void testConcurrentUpdate() {
    //1、小李
    Product p1 = productMapper.selectById(1L);
    System.out.println("小李取出的价格:" + p1.getPrice());
    //2、小王
    Product p2 = productMapper.selectById(1L);
    System.out.println("小王取出的价格:" + p2.getPrice());
    //3、小李将价格加了50元,存入了数据库
    p1.setPrice(p1.getPrice() + 50);
    int result1 = productMapper.updateById(p1);
    System.out.println("小李修改结果:" + result1);
    //4、小王将商品减了30元,存入了数据库
    p2.setPrice(p2.getPrice() - 30);
    int result2 = productMapper.updateById(p2);
    System.out.println("小王修改结果:" + result2);
    //最后的结果
    Product p3 = productMapper.selectById(1L);
    //价格覆盖,最后的结果:70
    System.out.println("最后的结果:" + p3.getPrice());
}

乐观锁实现流程

数据库中添加version字段

取出记录时,获取当前version

SELECT id,name,price,version FROM product WHERE id=1

更新时,version + 1,如果where语句中的version版本不对,则更新失败

UPDATE product SET price=price+50, version=version + 1 WHERE id=1 AND version=1

Mybatis-Plus实现乐观锁

实体类

package com.atguigu.mybatisplus.entity;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;
    @Version
    private Integer version;
}

添加乐观锁插件配置

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    //添加分页插件
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    //添加乐观锁插件
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

上面的在小李更新后由于版本号不同小王更新失败。但并不是咱们想要的结果

所以在小王失败后在次进项修改

if(result2 == 0){
//失败重试,重新获取version并更新
    p2 = productMapper.selectById(1L);
    p2.setPrice(p2.getPrice() - 30);
    result2 = productMapper.updateById(p2);
}

5、通用枚举

表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用MyBatis-Plus的通用枚举 来实现

1、将数据库的性别字段设置为int型

2、创建通用枚举类型

@Getter
public enum SexEnum {
    MALE(1, "男"),
    FEMALE(2, "女");
    @EnumValue
    private Integer sex;
    private String sexName;
    SexEnum(Integer sex, String sexName) {
    this.sex = sex;
    this.sexName = sexName;
	}
}

3、配置扫描通用枚举

#MyBatis-Plus相关配置
mybatis-plus:
  #指定mapper文件所在的地址
  mapper-locations: classpath:mapper/*.xml
  configuration:
    #配置日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    banner: off
    db-config:
      #配置mp的主键策略为自增
      id-type: auto
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_
  #配置类型别名所对应的包
  type-aliases-package: com.atguigu.mybatisplus.pojo
  # 扫描通用枚举的包
  type-enums-package: com.atguigu.mybatisplus.enums

6、代码生成器

(感觉没太大用处)

1、引入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

2、快速生成

public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus?
characterEncoding=utf-8&userSSL=false", "root", "123456")
.globalConfig(builder -> {
builder.author("atguigu") // 设置作者
//.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://mybatis_plus"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.atguigu") // 设置父包名
.moduleName("mybatisplus") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));
// 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("t_user") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker
引擎模板,默认的是Velocity引擎模板
.execute();
}
}

##

7、多数据源

适用于多种场景:纯粹多库、 读写分离、 一主多从、 混合模式等

场景说明:

我们创建两个库,分别为:mybatis_plus(以前的库不动)与mybatis_plus_1(新建),将mybatis_plus库的product表移动到mybatis_plus_1库,这样每个库一张表,通过一个测试用例分别获取用户数据与商品数据,如果获取到说明多库模拟成功
导入依赖

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

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version>
</dependency>

编写配置文件

  # 配置数据源信息
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: 132537
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&useSSL=false
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: 132537

Service接口

@DS("master") //指定操作的数据源,master为user表
public interface UserService extends IService<User> {}
@DS("slave_1")
public interface ProductService extends IService<Product>{}

8、MyBatisX插件

可以快速生代码,嘎嘎好使

必须得配置数据源信息才能使用(已下载MybatisX插件)

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
    username: root
    password: 132537

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

MyBaitsX可以根据我们在Mapper接口中输入的方法名快速帮我们生成对应的sql语句

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值