MyBatis-Plus使用

目录

一、概念

1、Mybatis-Plus介绍

2、特性

二、基本使用

1、导入maven

2、yml或者properties配置

配置日志(可选)

3、使用

 三、CRUD

1、BaseMapper

 1.1、插入

 1.2、删除

 1.3、更新

1.4、 查询

 1.5、自定义功能

2、IService

 2.1、测试查询记录数

 2.2、测试批量插入

四、条件构造器

1、基本⽐较操作

 2、模糊查询

3、排序

4、逻辑查询

 5、select

五、ActiveRecord

1、查询

2、新增

3、更新操作

4、删除操作

六、自动填充

1、什么是自动填充

2、自动填充方式

七、插件

1、分页插件

 八、逻辑删除


一、概念

1、Mybatis-Plus介绍

官⽹: https://mybatis.plus/ 或 https://mp.baomidou.com/

MyBatis-Plus (简称 MP )是⼀个 MyBatis 的增强⼯具,在 MyBatis 的基础上只做增强不做改变,-为简化开发、提⾼效率⽽⽣。

2、特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗⼩:启动即会⾃动注⼊基本 CURD,性能基本⽆损耗,直接⾯向对象操作
  • 强⼤的 CRUD 操作:内置通⽤ Mapper、通⽤ Service,仅仅通过少量配置即可实现单表⼤部分CRUD 操作,更有强⼤的条件构造器,满⾜各类使⽤需求
  • ⽀持 Lambda 形式调⽤:通过 Lambda 表达式,⽅便的编写各类查询条件,⽆需再担⼼字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • ⽀持 ActiveRecord 模式:⽀持 ActiveRecord 形式调⽤,实体类只需继承 Model 类即可进⾏强⼤的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码⽣成器:采⽤代码或者 Maven 插件可快速⽣成 Mapper 、 Model 、 Service 、 Controller 层代码,⽀持模板引擎,更有超多⾃定义配置等您来使⽤
  • 内置分⻚插件:基于 MyBatis 物理分⻚,开发者⽆需关⼼具体操作,配置好插件之后,写分⻚等同于普通 List 查询
  • 分⻚插件⽀持多种数据库:⽀持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、 Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执⾏时间,建议开发测试时启⽤该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可⾃定义拦截规则,预防误操作

二、基本使用

1、导入maven

<dependencies>
        <!-- web启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 数据库链接 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>

注意:尽量不要同时导入mybatis 和 mybatis_plus,避免版本差异

2、yml或者properties配置

配置(连接数据库) 

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/employees?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
    username: root
    password: 123456
配置日志(可选)

所有的SQL都是不可见的,所以在后台是希望看到SQL是怎么执行的,就必须要配置日志。
在.yml配置文件中配置日志:

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

3、使用

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

@SpringBootApplication
@MapperScan("com.mp.mapper") //扫描mapper
public class MyMpApplication{
    public static void main(String[] args) {
        SpringApplication.run(MyMpApplication.class, args);
    }
}

在对应的 mapper 上面添加 @Mapper 注解,并继承 BaseMapper<> 类:

@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
       //所有的CRUD都已经完成
    //不需要像以前一样配置一大堆文件:pojo-dao(连接mybatis,配置mapper.xml文件)==>service-controller 
}

开始使用

编写单元测试:

    @Test
    public void selectByIdTest() {
        List<Employee> employees = employeeMapper.selectList(null);
        employees.forEach(System.out::println);
    }

 

 三、CRUD

实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("employee")
public class Employee {
    @TableId(type = IdType.AUTO)
    private Long empId;
    private String name;
    private String empGender;
    private Integer age;
    private String email;
}

如果实体类名称和表名称不⼀致,可以在实体类上添加注解 @TableName(" 指定数据库表名 ")

如果字段名称和属性名称不一致,可以在属性上添加注解@TableField(value = " ")指定字段名

自定义ID生成器:

@TableId(type = IdType)

  • AUTO:数据库ID自增
  • NONE:该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
  • INPUT:该类型可以通过自己注册自动填充插件进行填充
  • ASSIGN_ID:分配ID (主键类型为 number 或 string ) 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
  • ASSIGN_UUID:分配UUID (主键类型为 string)  默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace(“-”,“”))

1、BaseMapper

BaseMapper提供了许多公共的方法,需继承BaseMapper

public interface EmployeeMapper extends BaseMapper<Employee>
public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);
    int deleteById(Serializable id);
    int deleteByMap(@Param("cm") Map<String, Object> columnMap);
    int delete(@Param("ew") Wrapper<T> queryWrapper);
    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
    int updateById(@Param("et") T entity);
    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
    T selectById(Serializable id);
    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
    T selectOne(@Param("ew") Wrapper<T> queryWrapper);
    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);
    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
    <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);
    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}
 1.1、插入
    @Test
    public void insertTest() {
        Employee employee = new Employee();
        employee.setName("李振成");
        employee.setAge(23);
        employee.setEmail("asdsds");
        employee.setEmpGender("女");
        employeeMapper.insert(employee);
        System.out.println(employee.getEmpId());
    }
 1.2、删除
1.2.1、通过id删除记录
@Test
public void testDelete(){
    int result = employeeMapper.deleteById(3L);
    System.out.println("result = " + result);
}
 1.2.2、通过id批量删除记录
@Test
public void testDelete(){
    List<Long> list = Arrays.asList(1L, 2L, 3L);
    int result = employeeMapper.deleteBatchIds(list);
    System.out.println("result = " + result);
}
1.2.3、 通过map条件删除记录
@Test
public void testDelete(){
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","张三");
    map.put("age",23);
    int result = employeeMapper.deleteByMap(map);
    System.out.println("result = " + result);
}
 1.3、更新
1.3.1、根据 id 更新
   @Test
    public void updateByIdTest() {
        Employee employee = new Employee();
        employee.setEmpId(1l);
        employee.setName("kawasaki");
        employee.setAge(18);
        employee.setEmail("chuanqi");
        employee.setEmpGender("女");
        employeeMapper.updateById(employee);
    }
1.3.2 、根据条件更新
    @Test
    public void updateTest() {
        Employee employee = new Employee();
        employee.setName("王蔡蔡");
        employee.setAge(18);
        employee.setEmail("chuanqi");
        employee.setEmpGender("男");
        employeeMapper.update(employee,new UpdateWrapper<Employee>().eq("name","王彩彩"));
    }
1.4、 查询
1.4.1、根据Id查询用户信息
    @Test
    public void selectByIdTest() {
        Employee employee = employeeMapper.selectById(1l);
        System.out.println(employee);
    }
1.4.2、根据多个id查询多个用户信息
@Test
public void testSelect(){
    List<Long> list = Arrays.asList(1L, 2L, 3L);
    List<Employee> employees = employeeMapper.selectBatchIds(list);
    employees.forEach(System.out::println);
}
1.4.3、通过map条件查询用户信息
 @Test
 public void selectMapsTest() {
    Map<String,Object> map = new HashMap<>();
    map.put("name", "李振成");
    map.put("emp_gender", "男");
    List<Employee> employees = employeeMapper.selectByMap(map);
    System.out.println(employees);
}
 1.4.4、查询所有数据
@Test
public void testSelect(){
    List<Employee> employees = employeeMapper.selectList(null);
    employees.forEach(System.out::println);
}
 1.5、自定义功能

resource目录下新建mapper/EmployeeMapper.xml文件

  • UserMapper接口
/**
 * 根据 id 查询用户信息为 map 集合
 * @param id
 * @return
 */
Map<String, Object> selectMapById(Long id);
  •  UserMapper映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mp.mapper.EmployeeMapper">

    <!-- Map<String, Object> selectMapById(Long id); -->
    <select id="selectMapById" resultType="map">
        select emp_id,name,emp_gender,age,email from employee where emp_id = #{id}
    </select>
</mapper>
  •  测试
    @Test
    public void testSelect(){
        Map<String, Object> map = employeeMapper.selectMapById(1L);
        System.out.println(map);
    }

2、IService

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

/**
 *  EmployeeService继承IService模板提供的基础功能
 */
public interface EmployeeService extends IService<Employee> {
}
/**
 * EmployeeImpl实现了IService,提供了IService中基础功能的实现
 * 若ServiceImpl无法满足业务需求,则可以使用自定的EmployeeService定义方法,并在实现类中实现
 */
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
}
 2.1、测试查询记录数
@Autowired
private EmployeeService employeeService;

@Test
public void test(){
    long count = employeeService.count();
    System.out.println(count);
}
 2.2、测试批量插入
@Test
public void testInsertMore(){
    ArrayList<Employee> list = new ArrayList<>();
    for (int i = 1; i <= 10; i++) {
        Employee employee= new Employee();
        employee.setName("jingchao"+i);
        employee.setAge(18+i);
        employee.setEmail("jingchao"+i+"@gmail.com");
        list.add(employee);
    }
    boolean b = employeeService.saveBatch(list);
    System.out.println(b);
}

四、条件构造器

Wrapper:条件构造抽象类,最顶端父类

        AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件

                QueryWrapper : 查询条件封装

                UpdateWrapper : Update 条件封装

                AbstractLambdaWrapper : 使用Lambda 语法

                        LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper

                        LambdaUpdateWrapper : Lambda 更新封装Wrapper

1、基本⽐较操作

  • eq   等于 =
  • ne   不等于 <>
  • gt   ⼤于 >
  • ge   ⼤于等于 >=
  • lt   ⼩于 <
  • le   ⼩于等于 <=
  • between   BETWEEN 值 1 AND 值 2
  • notBetween   NOT BETWEEN 值 1 AND 值 2
  • in   字段 IN (value.get(0), value.get(1), ...)
  • notIn    字段 NOT IN (v0, v1, ...)

 测试:

    @Test
    public void testQuery(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        //SELECT emp_id,name,emp_gender,age,email FROM employee WHERE (email = ? AND age >= ? AND name IN (?,?,?))
        wrapper.eq("email", "asdasd")
                .ge("age", 15)
                .in("name", "jack", "zm", "mr");
        List<Employee> employees = this.employeeMapper.selectList(wrapper);
        for (Employee employee: employees) {
            System.out.println(employee);
        }
    }

 2、模糊查询

  • like   LIKE '%值%'
  • notLike   NOT LIKE '%值%'
  • likeLeft   LIKE '%值'
  • likeRight   LIKE '值%'

  测试:

    @Test
    public void testLike(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        //SELECT emp_id,name,emp_gender,age,email FROM employee WHERE (name LIKE ?)
                //Parameters: %sen%(String)
                wrapper.like("name", "sen");
        List<Employee> employees = this.employeeMapper.selectList(wrapper);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

3、排序

  • orderBy   ORDER BY 字段, ...
    • 例: orderBy(true, true, "emp_id", "name") ---> order by emp_id ASC,name ASC
  • orderByAsc   ORDER BY 字段, ... ASC
    • 例: orderByAsc("emp_id", "name") ---> order by emp_id ASC,name ASC
  • orderByDesc   ORDER BY 字段, ... DESC
    • 例: orderByDesc("emp_id", "name") ---> order by emp_id DESC,name DESC

  测试:

    @Test
    public void testOrder(){

        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        //SELECT emp_id,name,emp_gender,age,email FROM employee ORDER BY age DESC
        wrapper.orderByDesc("age");
        List<Employee> employees = this.employeeMapper.selectList(wrapper);
        for (Employee employee : employees) {
            System.out.println(employee);
        }

    }

4、逻辑查询

  • or   拼接 OR 
  • and   AND 嵌套

   测试:

    @Test
    public void testOr(){
        
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        //SELECT emp_id,name,emp_gender,age,email FROM employee WHERE (name = ? OR age = ?)
        wrapper.eq("name","jack").or().eq("age", 14);
        List<Employee> employees = this.employeeMapper.selectList(wrapper);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

 5、select

在 MP 查询中,默认查询所有的字段,如果有需要也可以通过 select ⽅法进⾏指定字段。

  测试:

    @Test
    public void testQuerySelect(){

        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        //SELECT emp_id,name,age FROM employee WHERE (name = ? OR age = ?)
        //jack(String), 14(Integer)
        wrapper.eq("name", "jack")
                .or()
                .eq("age", 14)
                .select("emp_id", "name", "age");
        List<Employee> employees = this.employeeMapper.selectList(wrapper);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

五、ActiveRecord

什么是 ActiveRecord ?
ActiveRecord 也属于 ORM (对象关系映射)层,由 Rails 最早提出,遵循标准的 ORM 模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很⼤程度的快速实现模型的操作,⽽且简洁易懂。
ActiveRecord 的主要思想是:

  • 每⼀个数据库表对应创建⼀个类,类的每⼀个对象实例对应于数据库中表的⼀⾏记录;通常 表的每个字段在类中都有相应的Field;
  • ActiveRecord同时负责把⾃⼰持久化,在ActiveRecord中封装了对数据库的访问,即 CURD;
  • ActiveRecord是⼀种领域模型(Domain Model),封装了部分业务逻辑;

 开启AR

  • 实体对象继承 Model
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("employee")
public class Employee extends Model<Employee> {
    @TableId(type = IdType.AUTO)
    private Long empId;
    private String name;
    private String empGender;
    private Integer age;
    private String email;
}

1、查询

 1.1、根据主键id查询
    // AR 根据id查询
    @Test
    public void testARById(){
        Employee employee = new Employee();
        employee.setEmpId(1l);
        Employee employee1 = employee.selectById();
        System.out.println(employee1);
    }
1.2、查询所有
    //查询所有
    @Test
    public void testARSelectAll(){
        Employee employee = new Employee();
        List<Employee> employees = employee.selectAll();
        employees.forEach(System.out::println);
    }
 1.3、查询计数
    //查询计数
    @Test
    public void testARSelectCount(){
        Employee employee = new Employee();
        Integer integer = employee.selectCount(null);
        System.out.println("总人数:"+integer);
    }

2、新增

    //新增数据
    @Test
    public void testARInsert(){
        Employee employee = new Employee();
        employee.setName("张三");
        employee.setEmail("123ASd@ww.com");
        employee.setAge(23);
        employee.setEmpGender("女");
        boolean insert = employee.insert();
        System.out.println(insert);
    }

3、更新操作

    //更新数据
    @Test
    public void testARUpdataById(){
        Employee employee = new Employee();
        employee.setEmpId(3l);
        employee.setName("李玉田");
        employee.setEmail("123ASd@ww.com");
        employee.setAge(23);
        employee.setEmpGender("女");
        boolean insert = employee.updateById();
        System.out.println(insert);
    }

4、删除操作

    //根据主键删除
    @Test
    public void testARDeleteById(){
        Employee employee = new Employee();
        employee.setEmpId(3l);
        boolean b = employee.deleteById();
        System.out.println(b);
    }

六、自动填充

1、什么是自动填充

在常用业务中有些属性需要配置一些默认值,MyBatis-Plus 提供了实现此功能的插件,也就是自动填充功能。
比如创建时间、修改时间这些操作一般都是自动化完成的,是不用去手动更新的。

2、自动填充方式

2.1、方式一:数据库级别(不建议)

2.1.1、添加时间字段

  • 在使用的时候该字段留空即可。 

 2.1.2、在实体类中添加创建时间(create_time)以及修改时间(update_time)

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("employee")
public class Employee extends Model<Employee> {
    @TableId(type = IdType.AUTO)
    private Long empId;
    private String name;
    private String empGender;
    private Integer age;
    private String email;

    private LocalDateTime createTime;
    private LocalDateTime updateTime;

}
 2.2、方式二:代码级别(建议)
  •  修改数据库字段

 数据库级别中的默认设置为null或者不写,还有去掉根据当前时间更新这个框

  • 在实体类的字段属性增加注解
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("employee")
public class Employee extends Model<Employee> {
    @TableId(type = IdType.AUTO)
    private Long empId;
    private String name;
    private String empGender;
    private Integer age;
    private String email;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

}
  • 编写处理器来处理注解
package com.kuang.handle;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

@Slf4j
@Component 
public class MyMetaObjectHander implements MetaObjectHandler {

    //插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill.....");
        //setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
        //第一个参数 字段名
        //第二个参数 值
        //第三个参数 metaObject
//        this.setFieldValByName("createTime",new Date(),metaObject);
//        this.setFieldValByName("updateTime",new Date(),metaObject);
        // 上面的方法是旧版本,新版本的是使用下面的strictInsertFill方法
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());

    }

    //更新时的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill.....");
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

测试:

    @Test
    public void testARInsert(){
        Employee employee = new Employee();
        employee.setName("aaaaaaaaa");
        employee.setEmail("123ASd@ww.com");
        employee.setAge(23);
        employee.setEmpGender("女");
        boolean insert = employee.insert();
        System.out.println(insert);
    }

 

七、插件

1、分页插件

1.1、添加配置类
@Configuration
@MapperScan("com.jingchao.mybatisplus.mapper")
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
 1.2、测试:
    //分页查询
    @Test
    public void testPage(){
        Page<Employee> page = new Page<>(2,2);
        employeeMapper.selectPage(page, null);
        //获取分页数据
        List<Employee> 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());
    }

 八、逻辑删除

  • 物理删除:从数据库中直接删除
  • 逻辑删除:在数据库中没有被删除,而是通过一个变量来让它失效
    • deleted=0 ——》deleted=1
  • 管理员可以查看被删除的记录,防止数据丢失,相当于回收站
  • 在数据表中增加一个 deleted 字段

  • 同步实体类,在实体类上加上 @TableLogic 注解

 

测试:

  •  测试删除:删除功能被转变为更新功能
-- 实际执行的SQL
update employee set is_deleted=1 where emp_id = 1 and is_deleted=0
  •  测试查询:被逻辑删除的数据默认不会被查询
-- 实际执行的SQL
select emp_id,name,is_deleted from user where is_deleted=0

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值