mybatis-plus学习笔记

mybatisPlus

官网

框架结构

image-20220602172948967

mybatis-plus快速开始

雪花算法

数据模型

在IDEA里使用

image-20220602180756682

Schema脚本
DROP TABLE IF EXISTS user;

CREATE TABLE user
(
    //雪花算法的19位id,所以bigint
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);

Data脚本
DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

创建项目

包名com.xiaoxin就行

image-20220602174507423

添加依赖

image-20220602175228519

image-20220602175518968

未勾选任何东西,手动添加mp、mysql驱动、lombok;以及lombok插件下载 
<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
//未指定,使用的便是parent里的版本
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
配置.yml

(特点:若前缀相同,省略不写,注意空格)

注意点

1、驱动类driver-class-name

spring boot 2.0(内置jdbc5驱动),驱动类使用:

driver-class-name: com.mysql.jdbc.Driver

spring boot 2.1及以上(内置jdbc8驱动),驱动类使用: driver-class-name: com.mysql.cj.jdbc.Driver

否则运行测试用例的时候会有 WARN 信息

2、连接地址url MySQL5.7版本的url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false

MySQL8.0版本的url: jdbc:mysql://localhost:3306/mybatis_plus? serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false

否则运行测试用例报告如下错误: java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more

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

启动类添加@MapperScan注解,扫描Mapper文件夹

@MapperScan("com.xiaoxin.mpquick1.mapper")
编码
  • 实体类

    compile后,查看 ctrl+f12

    image-20220602181949588

    import lombok.Data;
    @Data注解
    
  • UserMapper接口 继承BaseMapper

    大傻逼!!!!!!继承别忘了泛型 2022.6.2

  • 在springboot test测试

  @SpringBootTest
  class Mpquick1ApplicationTests {
  
      @Autowired
      private UserMapper userMapper;
      @Test
      void testSelectList() {
  //selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有;后面是lambda表达式
          userMapper.selectList(null).forEach(System.out::println);
      }
  
  }
  IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确
  的执行。
  为了避免报错,可以在mapper接口上添加 @Repository 注解
  • 添加日志

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

常用注解

官网

  • @TableName

    MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决定,且默认操作的表名和实体类型的类名一致
    
    不一致时,使用注解指定表明,或者在yml文件配置表头
    	global-config:
    		db-config:
    		# 配置MyBatis-Plus操作表的默认前缀
    			table-prefix: t_
    
  • @Tableld

    属性对应的字段声明为主键;(表示主键的字段不叫id是使用)

    • value(属性名和字段名不同时,指定字段名)只有一个属性可省略value

    • ldType(id为空时才用,自己设置优先级更高)

    描述
    AUTO数据库 ID 自增;注意,该类型请确保数据库设置了id自增, 否则无效
    NONE无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
    INPUTinsert 前自行 set 主键值
    ASSIGN_ID分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
    ASSIGN_UUID分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法)
  • @TableField

    情况1

    若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格

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

    情况2

    若实体类中的属性和表中的字段不满足情况1

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

  • @TableLogic

    • 逻辑删除

    物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
    逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库
    中仍旧能看到此条数据记录
    使用场景:可以进行数据恢复

    • 测试

    测试删除功能,真正执行的是修改 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

  //实际sql语句
  Preparing: UPDATE user SET is_deleted=1 WHERE id IN ( ? , ? , ? ) AND is_deleted=0
  
      @Test
      void logicDelete(){
          List<Long> list = Arrays.asList(1L,2L,3L);
          int i = userMapper.deleteBatchIds(list);
          System.out.println(i);
      }
  //删除之后用户便无法查询
  这是实际查询所有的sql语句
  Preparing: SELECT id,name,email,age,is_deleted FROM user WHERE is_deleted=0
  • @VersionEnumValue

  • @TableLogicSqlParser

  • @KeySequence

  • @lnterceptorlgnore

  • @OrderBy

代码生成器

配置

依赖
        <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.30</version>
        </dependency>
配置
public class MybatisGeneratorTest {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/reggie?characterEncoding=utf-8&userSSL=false", "root", "1234")
                .globalConfig(builder -> {
                    builder.author("xiaoxin") // 设置作者
                            //.enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("E://soft//md-code//mybatis__plus//mb_mpcode//mp2//src//main//java"); // 指定输出目录
        //System.out.println(System.getProperty("user.dir"));
        //E:\soft\md-code\mybatis__plus\mb_mpcode\mp1
                })
                .packageConfig(builder -> {
                    builder.parent("com.xiaoxin") // 设置父包名
                            .moduleName("reggie") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "E://soft//md-code//mybatis__plus//mb_mpcode//mp2//src//main//resources//mapper")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("address_book","category","dish","employee","orders","setmeal","shopping_cart","user") // 设置需要生成的表名  所有为all
                            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }
}

基本CRUD

BaseMapper

image-20220602192306449

插入

删除

  • byId
  • byId批量
  • map条件

修改

查询

  • byId
  • byId批量
  • map条件
  • 查询所有

测试自定义功能*

  • 设置映射文件,默认在这

    image-20220603081341235

  • 代码

  //接口方法
  Map<String,Object> selectMapById(Long id);
  
  //UserMapper.xml sql语句
   <select id="selectMapById" resultType = "map">
   	select id,name,age.emai from user where id = #{id}   
   </select>
      
  //调用
      Map<Stirng,Object> map = userMapper.selectMapById(1L);

Service CRUD 接口

说明:

  • MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑

  • 通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,

  • 泛型 T 为任意实体对象

  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类

  • 对象 Wrapper 为 条件构造器

通用方法

image-20220602193939465

image-20220602194013085

image-20220603084746536

创建service接口和实现类

注意泛型 <UserMapper, User>

/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现

* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
*/
@Service
//只实现接口不行,要重写所有方法
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements
UserService {
}
批量添加saveBatch(批量)

saveOrUpdateBatch 添加或修改,实体类中有id就是修改,没有就是添加

实体类数据类型用包装类

image-20220603092147151

  @Test
    void    insertMore(){
        List<User> list = new ArrayList<>();
        for (int i = 1; i < 10; i++) {
            User user = new User();
            user.setName("xiaoxin"+i);
            user.setEmail("xiaoxin"+i);
            user.setAge(i);
            list.add(user);
        }

        boolean b = userService.saveBatch(list);
        System.out.println(b);
    }

条件构造器

官网!!从这学习,下面只是入门了解

wapper体系

image-20220603101213615

由返回值判断可以链式调用

QueryWrapper

  • 组装查询条件
    @Test
    void selectList(){
//查询用户名包含a,年龄在10到20间,并且邮箱不为null的用户信息
//SELECT 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)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","j")
            				  //字段名 object类型
                        .between("age",10,20)
                        .isNotNull("email");


        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }
}

  • 组装排序条件

  • 组装删除条件

  • 条件的优先级

    consumer为lambda中的消费者接口;下载源代码才有注释

    方法中的内容是对参数的操作方式

@Test
public void test04() {
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND (age > ? ORemail IS NULL))
//lambda表达式内的逻辑优先运算
	queryWrapper.like("username", "a")
      		//此处param i是具体需要运行函数的类(也是Wrapper的子类);即构造。 流式写法
		.and(i -> i.gt("age", 20).or().isNull("email"));
	User user = new User();
	user.setAge(18);
	user.setEmail("user@atguigu.com");
	int result = userMapper.update(user, queryWrapper);
	System.out.println("受影响的行数:" + result);
}
  • 组装select子句
  • 实现子查询

UpdateWrapper

@Test
public void test07() {
	//将(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息修改
	//组装set子句以及修改条件
	UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
	//lambda表达式内的逻辑优先运算
		updateWrapper
			.set("age", 18)
			.set("email", "user@atguigu.com")
			.like("username", "a")
			.and(i -> i.gt("age", 20).or().isNull("email"));
	//这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
	//UPDATE t_user SET username=?, age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
		//User user = new User();
		//user.setName("张三");
		//int result = userMapper.update(user, updateWrapper);
		//UPDATE t_user SET age=?,email=? WHERE (username LIKE ? AND (age > ? ORemail IS NULL))
		int result = userMapper.update(null, updateWrapper);
		System.out.println(result);
}

condition

StringUtils 字符串工具类,用的是myabtis-plus里面的

@Test
public void test08() {
//定义查询条件,有可能为null(用户未输入或未选择)
	String username = null;
	Integer ageBegin = 10;
	Integer ageEnd = 24;
	QueryWrapper<User> queryWrapper = new 	QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
	if(StringUtils.isNotBlank(username)){
		queryWrapper.like("username","a");
	}
	if(ageBegin != null){
		queryWrapper.ge("age", ageBegin);
	}
	if(ageEnd != null){
		queryWrapper.le("age", ageEnd);
	}
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=? AND age <= ?)
	List<User> users = 	userMapper.selectList(queryWrapper);
	users.forEach(System.out::println);
}

建造者模式(链式调用)

@Test
public void test08UseCondition() {
//定义查询条件,有可能为null(用户未输入或未选择)
	String username = null;
	Integer ageBegin = 10;
	Integer ageEnd = 24;
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
	queryWrapper
		.like(StringUtils.isNotBlank(username), "username", "a")
        .ge(ageBegin != null, "age", ageBegin)
		.le(ageEnd != null, "age", ageEnd);
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=? AND age <= ?)
	List<User> users = userMapper.selectList(queryWrapper);
	users.forEach(System.out::println);
}

LambdaQueryWrapper

第二个参数是函数式接口

防止写错字段名,通过过的实体类的属性名找到对应的 类::非静态方法

image-20220603122717560

image-20220603122903222

@Test
public void test09() {
//定义查询条件,有可能为null(用户未输入)
	String username = "a";
	Integer ageBegin = 10;
	Integer ageEnd = 24;
	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> users = 	userMapper.selectList(queryWrapper);
	users.forEach(System.out::println);
}

LambdaUpdateWrapper

@Test
public void test10() {
//组装set子句
	LambdaUpdateWrapper<User> updateWrapper = new 	LambdaUpdateWrapper<>();
	updateWrapper
		.set(User::getAge, 18)
		.set(User::getEmail, "user@atguigu.com")
		.like(User::getName, "a")
		.and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail));
 	   //lambda表达式内的逻辑优先运算
	User user = new User();
	int result = userMapper.update(user, updateWrapper);
	System.out.println("受影响的行数:" + result);
}

主键策略

数据库拓展(优化)方式:业务分库、主从复制、数据库分表

  • 背景

    需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。 数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。

  • 数据库分表

    将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务 继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如,淘宝的几亿用户数据, 如果全部存放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进 行拆分。

  • 单表数据拆分有两种方式:垂直分表和水平分表。

  • 水平分表相比垂直分表,会引入更多的复杂性,例如要求全局唯一的数据id该如何处理

    • 主键自增
    • 取模
    • 雪花算法——核心思想

自定义ID生成器

自动填充功能

瑞吉外卖里好像有用到

通用枚举

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

  • 创建枚举类 (复习一下)
    在这里插入图片描述
@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;
    }
}
  • mybatisplus配置

    • @EunmValue注解 将注解所标识的属性的值存储到数据库中

    • 扫描通用枚举的包 (在全局配置中设置)

      # 扫描通用枚举的包
      type-enums-package: com.atguigu.mybatisplus.enums
      
  • 测试 (数据库添加字段sex int)

    @Test
    public void test(){
        User user = new User();
        user.setName("admin");
        user.setAge(33);
        user.setSex(SexEnum.MALE);
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
    }

多数据源

主从复制 读写分离

  • 依赖
  <dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>3.5.0</version>
  </dependency>
  • yaml配置 (模拟)
  spring:
  # 配置数据源信息
  datasource:
  dynamic:
  # 设置默认的数据源或者数据源组,默认值即为master
  primary: master
  # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
  strict: false
  datasource:
  master:
  url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf8&useSSL=false
  driver-class-name: com.mysql.cj.jdbc.Driver
  username: root
  password: 1234
  slave_1:
  url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf8&useSSL=false
  driver-class-name: com.mysql.cj.jdbc.Driver
  username: root
  password: 1234
  • @DS(“master”) 指定数据源

插件

分页

1.插件
  • 配置类
@Configuration
@MapperScan("com.xiaoxin.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.xml自定义
  • UserMapper接口方法 (Ipage 接口 page 实现类)

@Param指定参数对应

/**
* 根据年龄查询用户列表,分页显示
* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位
* @param age 年龄
* @return
*/
IPage<User> selectPageVo(@Param("page") Page<User> page, @Param("age")
Integer age);
  • UserMapper.xml编写SQL
<!--SQL片段,记录基础字段-->
<sql id="BaseColumns">id,username,age,email</sql>

<!--IPage<User> selectPageVo(Page<User> page, Integer age);-->
<select id="selectPageVo" resultType="User">
	SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > #
{age}
</select>
  • 注意,上述编写xml时

    # 配置类型别名所对应的包
    type-aliases-package: com.xiaoxin.pojo
    
  • 测试

@Test
public void testSelectPageVo(){
	//设置分页参数
	Page<User> page = new Page<>(1, 5);
	userMapper.selectPageVo(page, 20);
	//获取分页数据
	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());
}

乐观锁与悲观锁

问题:和事务隔离 Spring AOP事务管理有什么区别

  • 悲观锁

    一个在读取的时候,另一个不允许读取,处于阻塞状态

    容易引起死锁,了解一下

  • 乐观锁

    • 实现流程

    数据库中添加version字段 取出记录时,获取当前version

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

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

    UPDATE product SET price=price+50, version=version + 1 WHERE id=1 AND
    version=1
    
  • mybatisplus实现乐观锁

    • 实体类添加

      @Version
      private Integer version;
      
    • 添加乐观锁插件配置(和分页插件在一个地方)

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

  @Test
      public void testProduct01(){
          //小李查询商品价格
          Product productLi = productMapper.selectById(1);
          System.out.println("小李查询的商品价格:"+productLi.getPrice());
          //小王查询商品价格
          Product productWang = productMapper.selectById(1);
          System.out.println("小王查询的商品价格:"+productWang.getPrice());
          //小李将商品价格+50
          productLi.setPrice(productLi.getPrice()+50);
          productMapper.updateById(productLi);
          //小王将商品价格-30
          productWang.setPrice(productWang.getPrice()-30);
          int result = productMapper.updateById(productWang);
          if(result == 0){
              //操作失败,重试
              Product productNew = productMapper.selectById(1);
              productNew.setPrice(productNew.getPrice()-30);
              productMapper.updateById(productNew);
          }
          //老板查询商品价格
          Product productLaoban = productMapper.selectById(1);
          System.out.println("老板查询的商品价格:"+productLaoban.getPrice());
      }

mybatisX

学习文档

在真正开发过程中,MyBatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表 联查,我们就需要自己去编写代码和SQL语句,我们该如何快速的解决这个问题呢,这个时候可 以使用MyBatisX插件

生成代码

image-20220603162612344

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值