MyBatis面试题

目录

硅谷

一 . MyBatis中 #{}和${}的区别是什么?

二 .Mybatis 结果集的映射方式有几种,并分别解释每种映射方式如何使用。

三 .简述MyBatis的单个参数、多个参数如何传递及如何取值。

四 .MyBatis如何获取自动生成的(主)键值?  

 五. 简述Mybatis的动态SQL,列出常用的6个标签及作用

六 . Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

七 . Mybatis 如何完成MySQL的批量操作,举例说明

八 .简述Mybatis提供的两级缓存,以及缓存的查找顺序

九 .简述如何在myBatis中的增删改操作获取到对数据库的影响条数

十 .解释MyBatis中 @Param注解的作用

十一 .简述Mybatis中使用Mapper接口开发,如何完成Mapper接口与SQL映射文件、方法与SQL语句的绑定

十二 .JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?

十三 .简单的说一下MyBatis的一级缓存和二级缓存?

十四.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

黑马

一 .MyBatis 中#{}和${}的区别是什么?

二 .MyBatis 是如何进行分页的?分页插件的原理是什么?

三 .实体类属性与表字段不一致解决方法? 

 四 .ResultType 和 ResultMap 的区别?

五. MyBatis 中有哪些设计模式?

六 .谈一谈你对 Mybatis 框架的理解(了解)

七 .在 mybatis 中,${} 和 #{} 的区别是什么?(必会)

八  .MyBatis 编程步骤是什么样的?(了解)

九 .在 mybatis 中,resultType 和 ResultMap 的区别是什么?(必会)

十 .在 Mybatis 中你知道的动态 SQL 的标签有哪些?作用分别是什么?(必会)

十一  .谈一下你对 mybatis 缓存机制的理解?(了解)

动力2021

一 .什么是MyBatis

二 .  Mybatis架构或工作流程

三 .Mapper.xml中statement中属性含义

四 .简述MyBatis的单个参数、多个参数如何传递及如何取值。

 五 .Mapper接口开发方法

六 .动态sql

 七 .Mybatis 如何完成MySQL的批量操作,举例说明

 八 .Mybatis缓存机制

九 .如何开启 MyBatis 的延迟加载? 

十 .Mybatis是如何进行分页的?分页插件的原理是什么?


硅谷

一 . MyBatis#{}和${}的区别是什么?

#{}是预编译处理,${}是字符串替换;

Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;

Mybatis在处理${}时,就是把${}替换成变量的值;

使用#{}可以有效的防止SQL注入,提高系统安全性。

二 .Mybatis 结果集的映射方式有几种,并分别解释每种映射方式如何使用。

自动映射 ,通过resultType来指定要映射的类型即可。

自定义映射 通过resultMap来完成具体的映射规则,指定将结果集中的哪个列映射到对象的哪个属性。

三 .简述MyBatis的单个参数、多个参数如何传递及如何取值。

MyBatis传递单个参数,如果是普通类型(String+8个基本)的,取值时在#{}中可以任意指定,如果是对象类型的,则在#{}中使用对象的属性名来取值

MyBatis传递多个参数,默认情况下,MyBatis会对多个参数进行封装Map,取值时在#{}可以使用0 1 2 .. 或者是param1 param2..

MyBatis传递多个参数,建议使用命名参数,在Mapper接口的方法的形参前面使用

@Param() 来指定封装Map时用的key. 取值时在#{}中使用@Param指定的key

四 .MyBatis如何获取自动生成的(主)键值?  

在<insert>标签中使用 useGeneratedKeys   和  keyProperty 两个属性来获取自动生成的主键值。

示例:

 五. 简述Mybatis的动态SQL,列出常用的6个标签及作用

动态SQL是MyBatis的强大特性之一 基于功能强大的OGNL表达式。

动态SQL主要是来解决查询条件不确定的情况,在程序运行期间,根据提交的条件动态的完成查询

常用的标签:

<if> : 进行条件的判断

<where>:在<if>判断后的SQL语句前面添加WHERE关键字,并处理SQL语句开始位置的AND 或者OR的问题

<trim>:可以在SQL语句前后进行添加指定字符 或者去掉指定字符.

<set>:  主要用于修改操作时出现的逗号问题

<choose> <when> <otherwise>:类似于java中的switch语句.在所有的条件中选择其一

<foreach>:迭代操作

六 . Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复。

七 . Mybatis 如何完成MySQL的批量操作,举例说明

MyBatis完成MySQL的批量操作主要是通过<foreache>标签来拼装相应的SQL语句. 

八 .简述Mybatis提供的两级缓存,以及缓存的查找顺序

(1)MyBatis的缓存分为一级缓存和 二级缓存。

一级缓存是SqlSession级别的缓存,默认开启。

二级缓存是NameSpace级别(Mapper)的缓存,多个SqlSession可以共享,使用时需要进行配置开启。

(2)缓存的查找顺序:二级缓存 => 一级缓存 => 数据库

九 .简述如何在myBatis中的增删改操作获取到对数据库的影响条数

直接在Mapper接口的方法中声明返回值即可

十 .解释MyBatis中 @Param注解的作用

通过该注解来指定Mybatis底层在处理参数时封装Map使用的key,方便在SQL映射文件中取参数。

十一 .简述Mybatis中使用Mapper接口开发,如何完成Mapper接口与SQL映射文件、方法与SQL语句的绑定

Mapper接口与SQL映射文件绑定:SQL映射文件中的namespace的值指定成Mapper接口的全类名

接口中方法与SQL语句的绑定:SQL语句的id 指定成接口中的方法名。

十二 .JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?

1) 数据库连接的创建、释放频繁造成系统资源浪费从而影响了性能,如果使用数据库连接池就可以解决这个问题。当然JDBC同样能够使用数据源。

解决:在SQLMapConfig.xml中配置数据连接池,使用数据库连接池管理数据库连接。

2) SQL语句在写代码中不容易维护,事件需求中SQL变化的可能性很大,SQL变动需要改变JAVA代码。解决:将SQL语句配置在mapper.xml文件中与java代码分离。

3) 向SQL语句传递参数麻烦,因为SQL语句的where条件不一定,可能多,也可能少,占位符需要和参数一一对应。解决:Mybatis自动将java对象映射到sql语句。

4) 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。解决:Mbatis自动将SQL执行结果映射到java对象。

十三 .简单的说一下MyBatis的一级缓存和二级缓存?

Mybatis首先去缓存中查询结果集,如果没有则查询数据库,如果有则从缓存取出返回结果集就不走数据库。Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象

Mybatis的二级缓存即查询缓存,它的作用域是一个mapper的namespace,即在同一个namespace中查询sql可以从缓存中获取数据。二级缓存是可以跨SqlSession的。

十四.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

黑马

一 .MyBatis #{}${}的区别是什么?

#{} 是预编译处理, ${} 是字符串替换。 Mybatis 在处理 #{} 时,会将 sql 中的 #{} 替换为 ?
号,调用 PreparedStatement set 方法来赋值; Mybatis 在处理 ${} 时,就是把 {}
换成变量的值。

二 .MyBatis 是如何进行分页的?分页插件的原理是什么?

分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插
件的拦截方法内拦截待执行的 sql ,然后重写 sql ,根据 dialect 方言,添加对应
的物理分页语句和物理分页参数。
三. MyBatis 动态 SQL 传参,注解是什么?
Xml 方式:
1. MyBatis 动态 SQL 可以让我们在 XML 映射文件内,以标签的形式编写动态 SQL
完成逻辑判断和动态拼接 SQL 的功能;
2. MyBatis 提供了 9 种动态 SQL 标签: trim where set foreach if choose
when otherwise bind
注解方式:
mybatis 注解方式的最大特点就是取消了 Mapper XML 配置,具体的 SQL 脚本
直接写在 Mapper 类或是 SQLProvider 中的方法动态生成 。
mybatis 提供的常用注解有:
@Insert @Update @Select @Delete 等标签,
这些注解其实就是 MyBatis 提供的来取代其 XML 配置文件的
@Results 和 @Result 注解
@Results 和 @Result 主要作用是,当有一些特殊的场景需要处理,查询的返回结
果与期望的数据格式不一致时,可以将将数据库中查询到的数值自动转化为具体的属
性或类型,,修饰返回的结果集。比如查询的对象返回值属性名和字段名不一致,或
者对象的属性中使用了枚举等。如果实体类属性和数据库属性名保持一致,就不需要
这个属性来修饰。

传参方式:
1 、直接传参

2 、使用 @Param 注解

3 Map 传值

 

4 、使用 pojo 对象

三 .实体类属性与表字段不一致解决方法? 

 Mybatis+xml:

Mybatis 提供了一个自动驼峰命名规则的设置,但是默认是关闭的,所以当我们没
有设置的时候,这样也是对应不上的。我们就需要在 Mybatis 的配置文件中添加如
下配置
方法 1: 通过 sql 语句的字段起别名 , 别名和实体中的对象属性一致
方法 2: mybatis 最强大的地方 : reslutMap 对象
Mybatis Plus
@TableName value = …
当数据库名与实体类名不一致或不符合驼峰命名时,需要在此注解指定表名
@TableId type = …
1 、 主要用来解决实体类的字段名与数据库中的字段名不匹配的问题(数据库
user_addr ,字段 useraddr 未驼峰)
2 、 实体类中的属性字段在表中不存在的问题

 四 .ResultType ResultMap 的区别?

如果数据库结果集中的列名和要封装实体的属性名完全一致的话用 resultType
属性
如果数据库结果集中的列名和要封装实体的属性名有不一致的情况用 resultMap 属 性,通过 resultMap 手动建立对象关系映射, resultMap 要配置一下表和类的一一对应关系,所以说就算你的字段名和你的实体类的属性名不 一样也没关系,都会给你映射出来

五. MyBatis 中有哪些设计模式?

Mybatis 至少遇到了以下的设计模式的使用:
Builder 模式 ,例如 SqlSessionFactoryBuilder XMLConfigBuilder
XMLMapperBuilder XMLStatementBuilder CacheBuilder
工厂模式 ,例如 SqlSessionFactory ObjectFactory MapperProxyFactory
单例模式 ,例如 ErrorContext LogFactory
代理模式 Mybatis 实现的核心,比如 MapperProxy ConnectionLogger ,用的
jdk 的动态代理;还有 executor.loader 包使用了 cglib 或者 javassist 达到延迟加
载的效果;
组合模式 ,例如 SqlNode 和各个子类 ChooseSqlNode 等;
模板方法模式 ,例如 BaseExecutor SimpleExecutor ,还有 BaseTypeHandler
所有的子类例如 IntegerTypeHandler
适配器模式 ,例如 Log Mybatis 接口和它对 jdbc log4j 等各种日志框架的适
配实现;
装饰者模式 ,例如 Cache 包中的 cache.decorators 子包中等各个装饰者的实现;
迭代器模式 ,例如迭代器模式 PropertyTokenizer

六 .谈一谈你对 Mybatis 框架的理解(了解)

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

七 .在 mybatis 中,${} 和 #{} 的区别是什么?(必会)

#{} 是占位符,预编译处理,${}是字符串替换。
Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set
方法来赋值;
Mybatis 在处理${}时,就是把${}替换成变量的值。
使用#{}可以有效的防止 SQL 注入,提高系统安全性。

八  .MyBatis 编程步骤是什么样的?(了解)

1、 创建 SqlSessionFactory
2、 通过 SqlSessionFactory 创建 SqlSession
3、 通过 sqlsession 执行数据库操作
4、 调用 session.commit()提交事务
5、 调用 session.close()关闭会话

九 .在 mybatis 中,resultType 和 ResultMap 的区别是什么?(必会)

如果数据库结果集中的列名和要封装实体的属性名完全一致的话用 resultType 属性
如果数据库结果集中的列名和要封装实体的属性名有不一致的情况用 resultMap 属
性,通过 resultMap 手动建立对象关系映射,resultMap 要配置一下表和类的一一对应关
系,所以说就算你的字段名和你的实体类的属性名不一样也没关系,都会给你映射出来

十 .在 Mybatis 中你知道的动态 SQL 的标签有哪些?作用分别是什么?(必会)

1. <if>if 是为了判断传入的值是否符合某种规则,比如是否不为空.
2. <where> where 标签可以用来做动态拼接查询条件,当和 if 标签配合的时候,不
用显示的声明类型 where 1 = 1 这种无用的条件
3. <foreach> foreach 标签可以把传入的集合对象进行遍历,然后把每一项的内容
作为参数传到 sql 语句中.
4. <include> include 可以把大量的重复代码整理起来,当使用的时候直接 include
即可,减少重复代码的编写;
5. <set>适用于更新中,当匹配某个条件后,才会对该字段进行跟新操作

十一  .谈一下你对 mybatis 缓存机制的理解?(了解)

Mybatis 有两级缓存,一级缓存是 SqlSession 级别的,默认开启,无法关闭;二级缓
存是 Mapper 级别的,二级缓存默认是没有开启的,但是手动开启
1. 一级缓存:基础 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,
当 Session flush 或 close 之后,Session 中的所有 Cache 就将清空
2. 二级缓存其存储作用域为 Mapper(Namespace),使用二级缓存属性类需要实现
Serializable 序列化接口
3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存 Namespaces)
的进行了 C(增加)/U(更新)/D(删除)操作后,默认该作用域下所有 select 中的缓存将被 clear.
需要在 setting 全局参数中配置开启二级缓存,如下 conf.xml 配置:

当我们的配置文件配置了 cacheEnabled=true 时,就会开启二级缓存,二级缓存是
mapper 级别的,如果你配置了二级缓存,那么查询数据的顺序应该为:二级缓存→一级缓
存→数据库。

动力2021

一 .什么是MyBatis

MyBatis是一个半ORM(对象关系映射)框架,内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。通过直接编写原生态SQL,可以严格控制SQL语句的执行性能,灵活度高(支持动态SQL语句)。

MyBatis 使用XML或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了JDBC代码手动设置参数以及获取结果集的繁琐步骤。

MyBatis通过xml文件或注解的方式将要执行的各种 statement 配置起来,并通过Java对象和 statement中SQL的动态参数进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL语句,并将结果映射为Java对象并返回。

二 .  Mybatis架构或工作流程

每一个Mybatis的应用程序都以一个SqlSessionFactory对象的实例为核心。首先用字节流通过Resource将配置文件读入,然后通过SqlSessionFactoryBuilder().build方法创建SqlSessionFactory,然后再通过SqlSessionFactory.openSession()方法创建一个SqlSession为每一个数据库事务服务。

经历了Mybatis初始化 –>创建SqlSession –>运行SQL语句,返回结果三个过程

三 .Mapper.xml中statement中属性含义

  1. id:sql语句唯一标识
  2. parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼 接在sql中。
  3. resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。
  4. resultMap:resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 resultMap实质上还需要将查询结果映射到pojo对象中。

resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo 和list实现一对一查询和一对多查询。

  1. #{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。一般能用#的就别用$.
  2. ${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进 行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。

四 .简述MyBatis的单个参数、多个参数如何传递及如何取值。

MyBatis传递单个参数,如果是普通类型(String+8个基本)的,取值时在#{}中可以任意指定,如果是对象类型的,则在#{}中使用对象的属性名来取值

MyBatis传递多个参数,默认情况下,MyBatis会对多个参数进行封装Map. 取值时在#{}可以使用0 1 2 .. 或者是param1 param2..

 五 .Mapper接口开发方法

通常Mybatis开发Dao方法有两种。即原始Dao开发方法和Mapper接口开发方法。

Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

Mapper接口开发需要遵循以下规范:

  1. Mapper.xml文件中的namespace与mapper接口的类路径相同。
  2. Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
  3. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType类型相同
  4. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select@Update等注解里面包含Sql语句来绑定,另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.

Dao 接口的工作原理是 JDK 动态代理,MyBatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行MappedStatement所代表的 sql,然后将 sql 执行结果返回。

六 .动态sql

动态 SQL 是指可以根据不同的参数信息来动态拼接的不确定的SQL叫做动态 SQL。

MyBatis 动态SQL 的主要元素有:if、choose/when/otherwise、trim、where、set、foreach 等。

通过mybatis提供的各种标签方法实现动态拼接sql

foreach标签:循环传入的集合参数

collection:传入的集合的变量名称

item:每次循环将循环出的数据放入这个变量中

open:循环开始拼接的字符串

close:循环结束拼接的字符串

separator:循环中拼接的分隔符

where标签作用:会自动向sql语句中添加where关键字,会去掉第一个条件的and关键字。

include标签:调用sql条件

 七 .Mybatis 如何完成MySQL的批量操作,举例说明

MyBatis完成MySQL的批量操作主要是通过<foreache>标签来拼装相应的SQL语句.

       例如:

       <insert id="insertBatch" >

 insert into tbl_employee(last_name,email,gender,d_id) values

<foreach collection="emps" item="curr_emp" separator=",">

(#{curr_emp.lastName},#{curr_emp.email},#{curr_emp.gender},#{curr_emp.dept.id})

</foreach>

</insert>

 八 .Mybatis缓存机制

mybatis是自动开启一级缓存的,sqlsession级别

         mybatis的二级缓存需要手动开启的,mapper级别(application应用),

        通常项目中,对于字典表(大量查询,很少修改)的数据,可以使用mybatis的二级缓存机制,

        提高查询效率。比如省市县、汽车品牌、配件类别、企业信息等

九 .如何开启 MyBatis 的延迟加载? 

只需要在 mybatis-config.xml 设置即可打开延迟缓存功能

十 .Mybatis是如何进行分页的?分页插件的原理是什么?

逻辑分页,使用MyBatis自带的RowBounds进行分页,它是一次性查询很多数据,然后在数据中再进行检索;

物理分页,手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据形式。

1)Mybatis使用RowBounds对象进行分页,可以直接编写sql实现分页,也可以使用Mybatis的分页插件PageHelper。

2)分页插件的原理:实现Mybatis提供的接口Interceptor,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql。

举例:

select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10

  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
MyBatis是一个开源的持久层框架,它可以数据库操作与Java对象之间的映射关系进行配置,简化了数据库操作的编写过程。下面是一些常见的MyBatis面试题及其答案: 1. 什么是MyBatisMyBatis是一个持久层框架,它可以将数据库操作与Java对象之间的映射关系进行配置,简化了数据库操作的编写过程。 2. MyBatis的优点有哪些? - 简化了数据库操作的编写过程,提高了开发效率。 - 提供了灵活的SQL映射配置,可以满足各种复杂的查询需求。 - 支持动态SQL,可以根据不同的条件生成不同的SQL语句。 - 提供了缓存机制,可以提高查询性能。 - 与Spring等框架集成较为方便。 3. MyBatis的核心组件有哪些? MyBatis的核心组件包括: - SqlSessionFactory:用于创建SqlSession对象的工厂。 - SqlSession:用于执行SQL语句和管理事务。 - Mapper接口:定义了数据库操作的方法。 - Mapper XML文件:配置了SQL语句和结果映射关系。 4. MyBatis中的动态SQL是什么? 动态SQL是指根据不同的条件生成不同的SQL语句。MyBatis提供了一些标签(如if、choose、foreach等)来实现动态SQL的编写,可以根据条件判断、循环等来动态生成SQL语句。 5. MyBatis的一级缓存和二级缓存有什么区别? - 一级缓存是SqlSession级别的缓存,它默认开启且不可关闭。在同一个SqlSession中,如果执行了相同的查询语句,那么第二次以后的查询会直接从缓存中获取结果,而不会再去数据库查询。 - 二级缓存是Mapper级别的缓存,它可以跨SqlSession共享。当多个SqlSession执行相同的查询语句时,如果开启了二级缓存,那么第二次以后的查询会直接从缓存中获取结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值