MyBatis-Plus知识点(一)

一、MyBatis-Plus简介

MyBatis-Plus (简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为 简化开发、提高效率而生。Mybatis-Plus提供了通用的Mapper和Service,可以在不编写任何SQL语句的前提下,快速的实现单表的增删改查(CURD),批量,逻辑删除,分页等操作。只要把MyBatis-Plus的特性到优秀插件,以及多数据源的配置进行详细讲解。

特性:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

代码及文档地址

官方地址: Redirect

代码发布地址:

Github: GitHub - baomidou/mybatis-plus: An powerful enhanced toolkit of MyBatis for simplify development

Gitee: mybatis-plus: mybatis 增强工具包,简化 CRUD 操作。 文档 http://baomidou.com低代码组件库 http://aizuda.com

文档发布地址: 简介 | MyBatis-Plus

二、入门案例

1、创建数据库和表

CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTERSET utf8mb4 */; use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名 ',
`age` int(11) DEFAULT NULL COMMENT '年龄 ',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱 ',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

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');

2、创建spring boot工程,引入依赖,pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.qcby</groupId>
    <artifactId>mybatisPlus</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatisPlus</name>
    <description>mybatisPlus</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3、idea中安装lombok插件

4、编写代码

①编写application.xml文件(配置连接数据库信息和日志信息)

spring:
  # 配置数据源信息
  datasource:
    # 配置数据源类型 spring默认的连接池
    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: 123456

# 配置MyBatis日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

注意:

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 timezone value'Öйú±ê׼ʱ¼ä' is unrecognized or representsmore

②在Spring Boot启动类中添加@MapperScan注解,扫描mapper包

@SpringBootApplication
@MapperScan("com.qcby.mybatisplus.mapper")
public class MybatisPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }
}

 ③添加实体

package com.qcby.mybatisplus.model;

import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//Alt+7  查看类的所有方法和属性
@Data //lombok注解
@AllArgsConstructor    //有参构造器
@NoArgsConstructor    //无参构造器
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

④添加mapper

BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的 实体类型

/**
 * 继承baseMapper中的方法
 * 共19个方法,9个跟条件构造无关
 */
@Repository   //用于标识数据访问对象
public interface UserMapper extends BaseMapper<User> {
    }

@Repository注解的主要功能和特点包括:

  1. 异常转换:Spring框架会自动将DAO层抛出的运行时异常转换为DataAccessException,这是一种Spring特有的运行时异常,用于封装底层数据库访问错误。这有助于上层应用程序或服务层处理这些异常,而不需要了解底层数据访问的详细错误。
  2. 事务管理:通过Spring的事务管理支持,你可以为使用@Repository注解的类中的方法自动添加事务支持。这意味着你可以在这些方法上添加@Transactional注解,以便自动进行事务管理,例如开始事务、提交或回滚事务。
  3. 组件扫描:@Repository注解使得Spring框架能够通过组件扫描(component scanning)自动检测到该类,并将其注册为Spring应用上下文中的一个bean。这样,你就可以在其他地方通过自动装配(autowiring)来注入和使用这个DAO。
  4. 集成测试:在集成测试中,你可以使用@Repository注解来模拟或替换实际的数据库访问逻辑,以便在测试环境中更容易地控制数据访问行为

⑤测试

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectList(){
        //selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
        //userMapper.selectList(null).forEach(System.out::println);
        List<User> users = userMapper.selectList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }
}

三、基本CRUD操作(创建(Create)、读取(Retrieve)、更新(Update)和删除(Delete))

1、BaseMapper

BaseMapper中的方法,大多方法中都有Wrapper类型的形参,此为条件构造器,可针 对于SQL语句设置不同的条件,若没有条件,则可以为该形参赋值null,即查询(删除/修改)所 有数据

MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现,我们可以直接使用,接口如 下:

package com.baomidou.mybatisplus.core.mapper;

public interface BaseMapper<T> extends Mapper<T> {

    /**
    * 插入一条记录
    * @param entity 实体对象 */
    int insert(T entity);
    
    /**
    * 根据 ID 删除
    * @param id 主键ID */
    int deleteById(Serializable id);
    
    /**
    * 根据实体(ID)删除
    * @param entity 实体对象
    * @since 3.4.4 */
    int deleteById(T entity);
    
    /**
    * 根据 columnMap 条件,删除记录
    * @param columnMap 表字段 map 对象 */
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
    /**
    * 根据 entity 条件,删除记录
    * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where
    语句)
    */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
    * 删除(根据ID 批量删除)
    * @param idList 主键ID列表(不能为 null 以及 empty) */
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    
    /**
    * 根据 ID 修改
    * @param entity 实体对象 */
    int updateById(@Param(Constants.ENTITY) T entity);
    
    /**
    * 根据 whereEntity 条件,更新记录
    * @param entity        实体对象 (set 条件值 ,可以为 null)
    * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成
    where 语句)
    */
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
    
    /**
    * 根据 ID 查询
    * @param id 主键ID */
    T selectById(Serializable id);
    
    /**
    * 查询(根据ID 批量查询)
    * @param idList 主键ID列表(不能为 null 以及 empty) */
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    
    /**
    * 查询(根据 columnMap 条件)
    * @param columnMap 表字段 map 对象 */
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
    /**
    * 根据 entity 条件,查询一条记录
    *<p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录 , 注意:多条数据会报异常 </p>
    * @param queryWrapper 实体对象封装操作类(可以为 null)
    */
    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) { List<T> ts = this.selectList(queryWrapper);
    if (CollectionUtils.isNotEmpty(ts)) {
    if (ts.size() != 1) {
    throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
    }
    return ts.get(0);
    }
    return null;
    }
    
    /**
    * 根据 Wrapper 条件,查询总记录数
    * @param queryWrapper 实体对象封装操作类(可以为 null) */
    Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
    * 根据 entity 条件,查询全部记录
    * @param queryWrapper 实体对象封装操作类(可以为 null) */
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
    * 根据 Wrapper 条件,查询全部记录
    * @param queryWrapper 实体对象封装操作类(可以为 null) */
    List<Map<String, Object>>selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
    * 根据 Wrapper 条件,查询全部记录
    * <p>注意: 只返回第一个字段的值</p>
    * @param queryWrapper 实体对象封装操作类(可以为 null) */
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
    * 根据 entity 条件,查询全部记录(并翻页)
    * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
    * @param queryWrapper 实体对象封装操作类(可以为 null) */
    <P extends IPage<T>>P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
    * 根据 Wrapper 条件,查询全部记录(并翻页)
    * @param page         分页查询条件
    * @param queryWrapper 实体对象封装操作类 */
    <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}

2、插入

@Test
public void testInsert(){
    User user = new User(null, "张三", 23, "zhangsan@atguigu.com",0);
    //INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
    int result = userMapper.insert(user);
    System.out.println("受影响行数:"+result);
    //1765621679929593857
    System.out.println("id自动获取:"+user.getId());
}

最终执行的结果,所获取的id为1765621679929593857

这是因为MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id

3、删除

①通过id删除记录

@Test
public void testDeleteById(){
    //通过id删除用户信息
    //DELETE FROM user WHERE id=?
    int result = userMapper.deleteById(1765621679929593857L);
    System.out.println("受影响行数:"+result);
}

②通过id批量删除记录

@Test
public void testDeleteBatchIds(){
    //通过多个id批量删除
    //DELETE FROM user WHERE id IN ( ? , ? , ? )
    List<Long> idList = Arrays.asList(1L, 2L, 3L);
    int result = userMapper.deleteBatchIds(idList);
    System.out.println("受影响行数:"+result);
}

③通过map条件删除记录

@Test
public void testDeleteByMap(){
    //根据map集合中所设置的条件删除记录
    //DELETE FROM user WHERE name = ? AND age = ?
    Map<String, Object> map = new HashMap<>();
    map.put("age", 23);
    map.put("name", "张三");
    int result = userMapper.deleteByMap(map);
    System.out.println("受影响行数:"+result);
}

4、修改

@Test
public void testUpdateById(){
    User user = new User(4L, "admin", 22, null,0);
    //UPDATE user SET name=?, age=? WHERE id=?
    int result = userMapper.updateById(user);
    System.out.println("受影响行数:"+result);
}

5、查询

①根据id查询用户信息

@Test
public void testSelectById(){
    //根据id查询用户信息
    //SELECT id,name,age,email FROM user WHERE id=?
    User user = userMapper.selectById(4L);
    System.out.println(user);
}

②根据多个id查询多个用户信息

@Test
public void testSelectBatchIds(){
    //根据多个id查询多个用户信息
    //SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
    List<Long> idList = Arrays.asList(4L, 5L);
    List<User> list = userMapper.selectBatchIds(idList);
    list.forEach(System.out::println);
}

③通过map条件查询用户信息

@Test
public void testSelectByMap(){
    //通过map条件查询用户信息
    //SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
    Map<String, Object> map = new HashMap<>();
    map.put("age", 22);
    map.put("name", "admin");
    List<User> list = userMapper.selectByMap(map);
    list.forEach(System.out::println);
}

④查询所有数据

@Test
public void testSelectList(){
    //selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
    //查询所有用户信息
    //SELECT id,name,age,email FROM user
    List<User> list = userMapper.selectList(null);
    list.forEach(System.out::println);
}

6、通用Service

官网地址:  CRUD 接口 | MyBatis-Plus A3

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

②创建Service接口和实现类

/**
* UserService继承IService模板提供的基础功能
*/
@Repository
public interface UserService extends IService<User> {
}

/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现
* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implementsUserService {
}

③测试查询记录数

@Autowired
private UserService userService;

@Test
public void testGetCount(){
    long count = userService.count();
    System.out.println("总记录数:" + count);
}

④测试批量插入

@Test
public void testSaveBatch(){
    // SQL长度有限制,海量数据插入单条SQL无法实行,
    // 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
    ArrayList<User> users = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        User user = new User();
        user.setName("ybc" + i);
        user.setAge(20 + i);
        users.add(user);
    }
    //SQL:INSERT INTO user ( name, age ) VALUES ( ?, ? )
    userService.saveBatch(users);
}

四、常用注解

1、@TableName:用于指定实体类(Java Bean)与数据库中的表之间的映射关系。

 MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决定,且默认操作的表名和实体类型的类名一致,如果表名与实体类名不一致,有两种解决方法。假如数据库表名为t_user,实体类名为user

①添加@TableName注解

@TableName("t_user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

②通过全局配置解决问题

如果实体类所对应的表都有固定的前缀,例如t_或tb_此时,可以使用MyBatis-Plus提供的全局配置,为实体类所对应的表名设置默认的前缀,那么就不需要在每个实体类上通过@TableName标识实体类对应的表

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

①默认主键策略

如果数据库表的主键是自增的,并且 Java 类的属性名与数据库表的列名一致,你可以简单地使用 @TableId 注解,无需指定任何属性。

②指定主键生成策略

你可以使用 type 属性来指定主键的生成策略。IdType.ASSIGN_ID (默 认),基于雪花算法的策略生成数据id,与数据库id是否设置自增无关; IdType.AUTO 表示主键自增,使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,否则无效。

public class User {  
    @TableId(value="uid",type = IdType.AUTO)  
    private Long id;  
    private String name;
    private Integer age;
    private String email; 
}

③指定列名

如果 Java 类的属性名与数据库表的列名不一致,你可以使用 value 属性来指定数据库中的列名。

④配置全局主键策略

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:用于标识实体类中的非主键属性与数据库表中的列之间的映射关系。

当实体类的属性名与数据库表的列名不一致时,可以使用这个注解来指定正确的映射关系。

①实体类的驼峰命名与数据表的下划线会自动转化

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

②指定列名

当 Java 类的属性名与数据库表的列名不一致时,可以使用 value 属性来指定正确的列名。

public class User {  
    private Long id;  
    @TableField("user_name")  
    private String name;  
    // ... 其他属性和方法 ...  
}

③排除某个字段

如果不希望某个字段被 MyBatis-Plus 自动映射到数据库的列,可以使用 exist 属性设置为 false。

public class User {  
    private Long id;  
    @TableField(exist = false)  
    private transient String tempField; // 用于临时存储,不映射到数据库列  
    // ... 其他属性和方法 ...  
}

④控制字段的插入和更新行为

可以使用 insertStrategy 和 updateStrategy 属性来控制字段在插入和更新操作时的行为。例如,你可以设置某个字段在插入时忽略,但在更新时包含。

public class User {  
    private Long id;  
    @TableField(insertStrategy = FieldStrategy.IGNORED)  
    private String createTime; // 插入时忽略此字段  
    // ... 其他属性和方法 ...  
}

 ⑤填充策略

可以使用 fill 属性配合 MyBatis-Plus 的 MetaObjectHandler 来实现自动填充策略,比如自动填充创建时间和更新时间。

public class User {  
    private Long id;  
    @TableField(fill = FieldFill.INSERT)  
    private Date createTime; // 插入时自动填充  
    @TableField(fill = FieldFill.UPDATE)  
    private Date updateTime; // 更新时自动填充  
    // ... 其他属性和方法 ...  
}

4、@TableLogic:用于实现逻辑删除的功能

逻辑删除并非真正从数据库中删除数据,而是在数据表中添加一个标识字段(通常为 deleted 或 is_deleted),通过修改这个标识字段的值来表示数据是否被“删除”。这样做的好处是保留了数据,同时又能够通过查询条件方便地排除掉被“删除”的数据。

public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    @TableLogic
    private Integer isDeleted;
}

 默认情况下,MyBatis-Plus 会认为 deleted 字段的值为 1 表示记录已被逻辑删除,值为 0 表示记录未被删除。但你也可以通过 @TableLogic 注解的 value 和 delval 属性来自定义这些值。

import com.baomidou.mybatisplus.annotation.TableLogic;  
  
public class User {  
    private Long id;  
    private String name;  
      
    // 逻辑删除字段,当值为true时表示该记录已被逻辑删除,否则表示未删除  
    @TableLogic(value = "is_deleted", delval = "true")  
    private Boolean isDeleted;  
      
    // ... 其他属性和方法 ...  
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值