【学习】MyBatis

Mybatis(JDBC)

image-20210420192859586

1.什么是MyBatis?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

如何获取MyBatis?

  • Maven仓库:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

  • GitHub:https://github.com/mybatis/mybatis-3
  • 中文文档:https://mybatis.org/mybatis-3/zh/index.html

2.第一个MyBatis程序

思路:搭建环境–>导入MyBatis–>编写代码–>测试

2.1、搭建环境

搭建数据库

CREATE DATABASE `mybatis`;
 
USE `mybatis`;
 
DROP TABLE IF EXISTS `user`;
 
CREATE TABLE `user` (
  `id` int(20) NOT NULL,
  `name` varchar(30) DEFAULT NULL,
  `pwd` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
insert  into `user` (`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654')

新建项目

  1. 新建一个普通的Maven项目

  2. 删除src目录

  3. 导入maven依赖

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    <!--父工程-->
        <groupId>org.example</groupId>
        <artifactId>MyBatis</artifactId>
        <version>1.0-SNAPSHOT</version>
    <!--导入依赖-->
        <dependencies>
    <!--        mybatis\mysql\junit-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.6</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
    

2.2创建一个模块

  • 编写配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
	<mappers>
        <mapper resource="com/dao/UserMapper.xml"/>
    </mappers>
</configuration>
  • 编写MyBatis工具类
package com.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

//sqlSessionFactory
public class MyBatisUtils {
    //提升作用域
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {//这3句必须具备
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
//        SqlSession sqlSession = sqlSessionFactory.openSession();
//        return sqlSession;
        //上面两句等于下面一句
        return sqlSessionFactory.openSession();
    }
}

2.3编写代码

  • 实体类
package com.pojo;

public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

  • Dao接口
public interface UserDao {
    List<User> getUserList();
}
  • 接口实现类由原来的UserDaoImpl转变为一个Mapper配置文件
<?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.dao.UserDao">
    <select id="getUserList" resultType="com.pojo.User">
        select * from mybatis.user
    </select>
</mapper>

2.4测试

  • junit测试
public class UserDaoTest {
    @Test
    public void test(){
        //第一步:获取SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        //方式一:getMapper
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();

        for (User user : userList) {
            System.out.println(user);
        }

        sqlSession.close();
    }
}

注意点:

  • Maven里的xml文件无法被过滤识别导出
    <build>
       <!-- 资源目录 -->    
        <resources>    
            <resource>    
                <!-- 设定主资源目录  -->    
                <directory>src/main/java</directory>    
 
                <!-- maven default生命周期,process-resources阶段执行maven-resources-plugin插件的resources目标处理主资源目下的资源文件时,只处理如下配置中包含的资源类型 -->     
				 <includes>
					  <include>**/*.xml</include>
				 </includes>  
                     
                <!-- maven default生命周期,process-resources阶段执行maven-resources-plugin插件的resources目标处理主资源目下的资源文件时,不处理如下配置中包含的资源类型(剔除下如下配置中包含的资源类型)-->      
				<excludes>  
					<exclude>**/*.yaml</exclude>  
				</excludes>  
 
                <!-- maven default生命周期,process-resources阶段执行maven-resources-plugin插件的resources目标处理主资源目下的资源文件时,指定处理后的资源文件输出目录,默认是${build.outputDirectory}指定的目录-->      
                <!--<targetPath>${build.outputDirectory}</targetPath> -->      
   
                <!-- maven default生命周期,process-resources阶段执行maven-resources-plugin插件的resources目标处理主资源目下的资源文件时,是否对主资源目录开启资源过滤 -->    
                <filtering>true</filtering>     
            </resource>  			
        </resources> 	
  </build>

3.CRUD(增删改查)

  • namespace包名与mapper接口一致
  • id:方法名
  • resultType:Sql语句执行的返回值
  • parameterType:参数类型

方法

public interface UserMapper {
    //查询全部用户
    List<User> getUserList();

    //根据ID查询用户
    User getUserById(int id);

    //增加用户
    int addUser(User user);

    //修改用户
    int updateUser(User user);

    //删除用户
    int deleteUser(int id);
}

xml配置

<?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.dao.UserMapper">
    <select id="getUserList" resultType="com.pojo.User">
        select * from mybatis.user
    </select>

    <select id="getUserById" resultType="com.pojo.User" parameterType="int">
        select * from mybatis.user where id=#{id}
    </select>

    <insert id="addUser" parameterType="com.pojo.User" >
        insert into mybatis.user (id,name,pwd) values (#{id},#{name}, #{pwd})
    </insert>

    <update id="updateUser" parameterType="com.pojo.User">
        update mybatis.user set name=#{name}, pwd=#{pwd}  where id=#{id}
    </update>

    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id=#{id}
    </delete>
</mapper>

测试类

public class UserMapperTest {
    @Test
    //查全部id
    public void test(){
        //第一步:获取SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        //方式一:getMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.getUserList();

        //方式二:
//        List<User> userList = sqlSession.selectList("com.dao.UserMapper.getUserList");
        for (User user : userList) {
            System.out.println(user);
        }

        sqlSession.close();
    }

    @Test
    //查一个id
    public void getUserById(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = mapper.getUserById(1);

        System.out.println(user);

        sqlSession.close();
    }

    @Test
    //增
    public void addUser(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        mapper.addUser(new User(4, "mike", "1231231"));


        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    //改
    public void updateUser(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        mapper.updateUser(new User(4,"john","213232131"));

        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    //删
    public void deleteUser(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        mapper.deleteUser(4);

        sqlSession.commit();
        sqlSession.close();
    }
}

1.万能的Map

当数据库中数据字段太多的时候,使用map阔以随意插入。(项目中,大量运用)

//map
    int addUser2(Map<String,Object> map);
<insert id="addUser2" parameterType="map" >
        insert into mybatis.user (id,name,pwd) values (#{id},#{name}, #{pwd})
</insert>
@Test
    public void addUser2(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Map<String,Object> map = new HashMap<String, Object>();

        map.put("id",5);
        map.put("name","Lily");
        map.put("pwd","112233");

        mapper.addUser2(map);


        sqlSession.commit();
        sqlSession.close();
    }

2.模糊查询

就是数据库的模糊查询

  1. Java代码执行的时候,传递通配符 % %

    List<User> userList = mapper.getUserLike("%李%");
    
  2. 在sql拼接中使用通配符

    select * from mybatis.user where name "%"like#{value}"%"
    

4.配置解析

1、核心配置文件

configuration(配置)

properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

2、环境配置(environments)

MyBatis 可以配置成适应多种环境

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

  • MyBatis默认的事务管理器就是JDBC,连接池:POOLED
  • 可以配置多套环境
<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

3、属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=root
<!--    引入外部配置文件-->
    <properties resource="db.properties">

    </properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

4、类型别名(typeAliases)

  • 作用:缩短包名,减少冗余
    <typeAliases >
        <typeAlias type="com.demo.pojo.User" alias="User"></typeAlias>
    </typeAliases>
    <typeAliases >
        <package name="com.demo.pojo"/>
    </typeAliases>

实体类少时用第一种,多时用第二种

注解别名

@Alias("author")
public class Author {
    ...
}

5、设置(settings)

6、其他配置

7、映射器(mappers)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BtkK2V3e-1619524785827)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20210422162627186.png)]

8、生命周期和作用域

理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。

作用域理解

  • SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

  • SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。

  • 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。

  • 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。

  • 如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。

  • 所以 SqlSession 的最佳的作用域是请求或方法作用域。

5.ResultMap及分页

要解决的问题:属性名和字段名不一致

方案一:为列名指定别名 , 别名和java实体类的属性名一致 .

select id , name , pwd as password from user where id = #{id} 方案二:使用结果集映射->ResultMap 【推荐】 select id , name , pwd from user where id = #{id}

上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。虽然在大部分情况下都够用,但是 HashMap 不是一个很好的模型。你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 对象)作为模型。

ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。

手动映射

1、返回值类型为resultMap

select id , name , pwd from user where id = #{id} 2、编写resultMap,实现手动映射! 如果世界总是这么简单就好了。但是肯定不是的,数据库中,存在一对多,多对一的情况,我们之后会使用到一些高级的结果集映射,association,collection这些,我们将在之后讲解,今天你们需要把这些知识都消化掉才是最重要的!理解结果集映射的这个概念!

6.日志

6.1、日志工厂

思考:我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率?

如果一个 数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。

对于以往的开发过程,我们会经常使用到debug模式来调节,跟踪我们的代码执行过程。但是现在使用Mybatis是基于接口,配置文件的源代码执行过程。因此,我们必须选择日志工具来作为我们开发,调节程序的工具。

Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:

  • SLF4J

  • Apache Commons Logging

  • Log4j 2

  • Log4j

  • JDK logging

具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。

标准日志实现

指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。

测试,可以看到控制台有大量的输出!我们可以通过这些输出来判断程序到底哪里出了Bug

6.2、Log4j

简介:

  • Log4j是Apache的一个开源项目

  • 通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件…

  • 我们也可以控制每一条日志的输出格式;

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

使用步骤:

1、导入log4j的包

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2、配置文件编写

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
 
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
 
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
 
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3、settings设置日志实现

<settings>
    <setting name="logImpl" value="Log4j"/>
</settings>

4、在程序中使用Log4j进行输出!

//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(MyTest.class);
 
@Test
public void selectUser() {
    logger.info("info:进入selectUser方法");
    logger.debug("debug:进入selectUser方法");
    logger.error("error: 进入selectUser方法");
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    List<User> users = mapper.selectUser();
    for (User user: users){
        System.out.println(user);
    }
    session.close();
}

5、测试,看控制台输出!

  • 使用Log4j 输出日志
  • 可以看到还生成了一个日志的文件 【需要修改file的日志级别】

7、分页

在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。

使用Limit实现分页

具体看:https://blog.csdn.net/qq_33369905/article/details/106647326?spm=1001.2014.3001.5501

8、注解开发

8.1、面向接口编程

大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程

根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好

在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;

而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

关于接口的理解

接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。

接口的本身反映了系统设计人员对系统的抽象理解。

接口应有两类:

第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);

第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);

一个体有可能有多个抽象面。抽象体与抽象面是有区别的。

三个面向区别

面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .

面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .

接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构

8.2、利用注解开发

更多详情:https://blog.csdn.net/qq_33369905/article/details/106647290?spm=1001.2014.3001.5501

9、Lombok

介绍:

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
  1. 先在IDEA上下载lombok的插件

  2. 添加maven-lombok

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
    
  3. 开始使用,一般在实体类添加getter、setter方法

    import lombok.Data;
    
    @Data
    public class User {
        private int id;
        private String name;
        private String pwd;
    }
    

10、一对多和多对一处理

详情:https://blog.csdn.net/qq_33369905/article/details/106647325?spm=1001.2014.3001.5501

11、动态SQL

详情:https://blog.csdn.net/qq_33369905/article/details/106647370?spm=1001.2014.3001.5501

12、缓存

详情:https://blog.csdn.net/qq_33369905/article/details/106647328?spm=1001.2014.3001.5501

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值