深入源码分析mybatis查询原理(一)

   MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简单、优雅。我主要是结合mybatis CRUD实例,深入代码,来探究MyBatis的实现。

前言:这篇文章的案例来源于 ArryLuo123 的案例,我主要是想跟踪源码,分析mybatis一次查询都是怎么实现的,因此就找了案例,不过这个案例数据库是由oracle实现的,而我的机子没有安装oracle,所以我修改为mysql的实现,好了,闲话少说。

这篇博文主要是把项目构建出来
由于那位哥们并没有把数据库表的设计整理出来,那我整理成sql语句吧:
CREATE TABLE `emp` (
  `EMPNO` int(11) NOT NULL AUTO_INCREMENT,
  `ENAME` varchar(36) DEFAULT NULL,
  `JOB` varchar(255) DEFAULT NULL,
  `MGR` int(11) DEFAULT NULL,
  `HIREDATE` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `SAL` int(11) DEFAULT NULL,
  `COMM` int(11) DEFAULT NULL,
  `DEPTNO` int(11) DEFAULT NULL,
  PRIMARY KEY (`EMPNO`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;


项目结构如下:


以下把代码粘贴下来:

Dept.java

package com.shandian.bean;


public class Dept {
	private Integer deptno;
	private String dname;
	private String loc;


	public Integer getDeptno() {
		return deptno;
	}


	public void setDeptno(Integer deptno) {
		this.deptno = deptno;
	}


	public String getDname() {
		return dname;
	}


	public void setDname(String dname) {
		this.dname = dname;
	}


	public String getLoc() {
		return loc;
	}


	public void setLoc(String loc) {
		this.loc = loc;
	}

	@Override
	public String toString() {
		return "Dept [deptno=" + deptno + ", dname=" + dname + ", loc=" + loc + "]";
	}
}
Emp.java
package com.shandian.bean;


import java.sql.Timestamp;


public class Emp {


	private Integer empno;
	private String ename;
	private String job;
	private Integer mgr;
	private Timestamp hiredate;
	private Integer sal;
	private Integer comm;
	private Integer Integerno;


	public Integer getEmpno() {
		return empno;
	}


	public void setEmpno(Integer empno) {
		this.empno = empno;
	}


	public String getEname() {
		return ename;
	}


	public void setEname(String ename) {
		this.ename = ename;
	}


	public String getJob() {
		return job;
	}


	public void setJob(String job) {
		this.job = job;
	}


	public Integer getMgr() {
		return mgr;
	}


	public void setMgr(Integer mgr) {
		this.mgr = mgr;
	}


	public Timestamp getHiredate() {
		return hiredate;
	}


	public void setHiredate(Timestamp hiredate) {
		this.hiredate = hiredate;
	}


	public Integer getSal() {
		return sal;
	}


	public void setSal(Integer sal) {
		this.sal = sal;
	}


	public Integer getComm() {
		return comm;
	}


	public void setComm(Integer comm) {
		this.comm = comm;
	}


	public Integer getIntegerno() {
		return Integerno;
	}


	public void setIntegerno(Integer Integerno) {
		this.Integerno = Integerno;
	}


	@Override
	public String toString() {
		return "Emp [empno=" + empno + ", ename=" + ename + ", job=" + job + ", mgr=" + mgr + ", hiredate=" + hiredate
				+ ", sal=" + sal + ", comm=" + comm + ", Integerno=" + Integerno + "]";
	}


}

empmapper.java

package com.shandian.mapper;

import java.util.List;

import com.shandian.bean.Emp;

//注意,这里的名字必须与com.shandian.mapper.empmapper,中empmapper的名字一致且必须在同一包中,不然就不能映射
public interface empmapper {
	// 这个方法名必须与empmaper中进行添加操作的名字一样的.
	// 参数的类型也要一致
	void add(Emp emp);


	// 查询全部
	List<Emp> queryall();


	// 进行删除
	void delete(int id);


	// 修改
	void update(Emp emp);
}

empmapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "--mybatismapper--" "mybatis-3-mapper.dtd" >
<mapper namespace="com.shandian.mapper.empmapper">
	<!--resultType的值引用了config中typeAliases> <typeAlias type="com.shandian.bean.Emp" 
		alias="emp" /> </typeAliases>的alias的值 -->
	<select id="queryall" resultType="emp" resultMap="empResult">
		select *from
		emp
	</select>
	<!-- 添加 -->
	<!-- 添加操作并没有结果集,结果类型也没有 -->
	<insert id="add" parameterType="emp" useGeneratedKeys="true" keyProperty="empno"  keyColumn = "EMPNO">
		insert into emp(ename,job)
		values(#{ename},#{job})
	</insert>
	<!-- 进行删除操作 -->
	<!-- id的意思表示命名空间的唯一标识,与映射接口中的方法一致 -->
	<delete id="delete" parameterType="int">
		delete from emp where
		empno=#{empno}
	</delete>
	<!-- 修改 -->
	<update id="update" parameterType="emp">
		update emp
		<!-- 动态的sql修改 -->
		<set>
			<!--test里面的值是你bean里面的变量名,而不是数据库中的字段名 -->
			<if test="ename!=null">


				ENAME=#{ename},
			</if>
			<if test="job!=null">
				JOB=#{job},
			</if>
			<if test="mgr!=null">
				MGR=#{mgr},
			</if>
			<if test="hiredate!=null">
				HIREDATE=#{hiredate},
			</if>
			<if test="sal!=null">
				SAL=#{sal},
			</if>
			<if test="comm!=null">
				COMM=#{comm},
			</if>
			<if test="Integerno!=null">
				DEPTNO=#{Integerno}
			</if>
		</set>
		where empno=#{empno}
	</update>
	<resultMap type="emp" id="empResult">
		<id column="EMPNO" property="empno" />
		<result column="ENAME" property="ename" />
		<result column="JOB" property="job" />
		<result column="MGR" property="mgr" />
		<result column="HIREDATE" property="hiredate" />
		<result column="SAL" property="sal" />
		<result column="COMM" property="comm" />
		<result column="DEPTNO" property="Integerno" />
	</resultMap>
</mapper>

mybatisconfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "--mybatisconfig--" "mybatis-3-config.dtd" >
<configuration>
	<typeAliases>
		<typeAlias type="com.shandian.bean.Emp" alias="emp" />
	</typeAliases>
	<environments default="development">


		<environment id="development">


			<transactionManager type="JDBC">
			</transactionManager>
			<!--连接池 -->
			<dataSource type="POOLED">
				<!--连接数据库驱动 -->
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<!-- URL -->
				<property name="url" value="jdbc:mysql://localhost:3306/mytest" />
				<!--用户名 -->
				<property name="username" value="root" />
				<property name="password" value="" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
	<!--引用你的empmapper.xml文件  -->
		<mapper resource="com/shandian/mapper/empmapper.xml" />
	</mappers>
</configuration>

EmpTexst.java

package com.shandian.texst;

import java.io.IOException;
import java.io.Reader;
import java.util.List;
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 com.shandian.bean.Emp;
import com.shandian.mapper.empmapper;


public class EmpTexst {
	// 测试
	public static void main(String[] args) {
		// 查询全部
        t1();
		// 添加
        //t2();
		// 删除
		// t3();
		// 修改
        //t4();
	}

	private static void t4() {
		// TODO Auto-generated method stub
		try {
			Reader reader = Resources.getResourceAsReader("mybatisconfig.xml");
			SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
			SqlSessionFactory factory = builder.build(reader);
			SqlSession session = factory.openSession();
			Emp emp = new Emp();
			emp.setEmpno(7720);
			emp.setEname("java");
			emp.setJob("java");
			empmapper empmapper = session.getMapper(empmapper.class);
			empmapper.update(emp);
			session.commit();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	private static void t3() {
		// TODO Auto-generated method stub
		try {
			Reader reader = Resources.getResourceAsReader("mybatisconfig.xml");
			SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
			SqlSessionFactory factory = builder.build(reader);
			SqlSession session = factory.openSession();
			empmapper empmapper = session.getMapper(empmapper.class);
			empmapper.delete(7717);
			session.commit();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	private static void t2() {
		// TODO Auto-generated method stub
		try {
			// 加载配置
			Reader reader = Resources.getResourceAsReader("mybatisconfig.xml");
			// 创建
			SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
			SqlSessionFactory factory = builder.build(reader);
			SqlSession session = factory.openSession();
			Emp emp = new Emp();
			emp.setEname("admin");
			emp.setJob("Android33");
			// 添加1操作
			/*
			 * session.insert("com.shandian.mapper.empmapper.add", emp);
			 * session.commit();
			 */
			// 添加2操作,这种做法是官方推荐的
			empmapper empMapper = session.getMapper(empmapper.class);
			empMapper.add(emp);
			session.commit();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	private static void t1() {
		// TODO Auto-generated method stub
		// 加载配置
		try {
			Reader reader = Resources.getResourceAsReader("mybatisconfig.xml");
			// 创建
			SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
			// 解析资源
			SqlSessionFactory factory = builder.build(reader);
			// 打开session
			SqlSession session = factory.openSession();
			// 1传统写法,不推荐
			/*
			 * List<Emp> list =
			 * session.selectList("com.shandian.mapper.empmapper.queryall"); for
			 * (Emp emp : list) { System.out.println(emp); }
			 */
			// 2,用接口映射的形式进行查询,官方推荐
			empmapper empmapper = session.getMapper(empmapper.class);
			List<Emp> list = empmapper.queryall();
			for (Emp emp : list) {
				System.out.println(emp);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

pom.xml 依赖

<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>com.mybatis</groupId>
	<artifactId>mybatisDemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
	<dependencies>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.1</version>
		</dependency>
		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.31</version>
	     </dependency>
	      <dependency>
	       <groupId>junit</groupId>
	       <artifactId>junit</artifactId>
	       <version>4.10</version>
	      </dependency>
	</dependencies>
   
</project>

还有两个约束:

mybatis-3-config.dtd

<?xml version="1.0" encoding="UTF-8" ?>
<!--


       Copyright 2009-2016 the original author or authors.


       Licensed under the Apache License, Version 2.0 (the "License");
       you may not use this file except in compliance with the License.
       You may obtain a copy of the License at


          http://www.apache.org/licenses/LICENSE-2.0


       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       See the License for the specific language governing permissions and
       limitations under the License.


-->
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>


<!ELEMENT databaseIdProvider (property*)>
<!ATTLIST databaseIdProvider
type CDATA #REQUIRED
>


<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>


<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>


<!ELEMENT settings (setting+)>


<!ELEMENT setting EMPTY>
<!ATTLIST setting
name CDATA #REQUIRED
value CDATA #REQUIRED
>


<!ELEMENT typeAliases (typeAlias*,package*)>


<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
type CDATA #REQUIRED
alias CDATA #IMPLIED
>


<!ELEMENT typeHandlers (typeHandler*,package*)>


<!ELEMENT typeHandler EMPTY>
<!ATTLIST typeHandler
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
handler CDATA #REQUIRED
>


<!ELEMENT objectFactory (property*)>
<!ATTLIST objectFactory
type CDATA #REQUIRED
>


<!ELEMENT objectWrapperFactory (property*)>
<!ATTLIST objectWrapperFactory
type CDATA #REQUIRED
>


<!ELEMENT reflectorFactory EMPTY>
<!ATTLIST reflectorFactory
type CDATA #REQUIRED
>


<!ELEMENT plugins (plugin+)>


<!ELEMENT plugin (property*)>
<!ATTLIST plugin
interceptor CDATA #REQUIRED
>


<!ELEMENT environments (environment+)>
<!ATTLIST environments
default CDATA #REQUIRED
>


<!ELEMENT environment (transactionManager,dataSource)>
<!ATTLIST environment
id CDATA #REQUIRED
>


<!ELEMENT transactionManager (property*)>
<!ATTLIST transactionManager
type CDATA #REQUIRED
>


<!ELEMENT dataSource (property*)>
<!ATTLIST dataSource
type CDATA #REQUIRED
>


<!ELEMENT mappers (mapper*,package*)>


<!ELEMENT mapper EMPTY>
<!ATTLIST mapper
resource CDATA #IMPLIED
url CDATA #IMPLIED
class CDATA #IMPLIED
>


<!ELEMENT package EMPTY>
<!ATTLIST package
name CDATA #REQUIRED
>

mybatis-3-mapper.dtd


<?xml version="1.0" encoding="UTF-8" ?>
<!--


       Copyright 2009-2011 The MyBatis Team


       Licensed under the Apache License, Version 2.0 (the "License");
       you may not use this file except in compliance with the License.
       You may obtain a copy of the License at


          http://www.apache.org/licenses/LICENSE-2.0


       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       See the License for the specific language governing permissions and
       limitations under the License.


-->


<!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select* )+>
<!ATTLIST mapper
xmlns:fo CDATA #IMPLIED
namespace CDATA #IMPLIED
>


<!ELEMENT cache-ref EMPTY>
<!ATTLIST cache-ref
namespace CDATA #REQUIRED
>


<!ELEMENT cache (property*)>
<!ATTLIST cache
type CDATA #IMPLIED
eviction CDATA #IMPLIED
flushInterval CDATA #IMPLIED
size CDATA #IMPLIED
readOnly CDATA #IMPLIED
>


<!ELEMENT parameterMap (parameter+)?>
<!ATTLIST parameterMap
id CDATA #REQUIRED
type CDATA #REQUIRED
>


<!ELEMENT parameter EMPTY>
<!ATTLIST parameter
property CDATA #REQUIRED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
mode (IN | OUT | INOUT) #IMPLIED
resultMap CDATA #IMPLIED
scale CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>


<!ELEMENT resultMap (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST resultMap
id CDATA #REQUIRED
type CDATA #REQUIRED
extends CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
>


<!ELEMENT constructor (idArg*,arg*)>


<!ELEMENT id EMPTY>
<!ATTLIST id
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>


<!ELEMENT result EMPTY>
<!ATTLIST result
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>


<!ELEMENT idArg EMPTY>
<!ATTLIST idArg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
>


<!ELEMENT arg EMPTY>
<!ATTLIST arg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
>


<!ELEMENT collection (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST collection
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
ofType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
>


<!ELEMENT association (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST association
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
>


<!ELEMENT discriminator (case+)>
<!ATTLIST discriminator
column CDATA #IMPLIED
javaType CDATA #REQUIRED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>


<!ELEMENT case (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST case
value CDATA #REQUIRED
resultMap CDATA #IMPLIED 
resultType CDATA #IMPLIED
>


<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>


<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
alias CDATA #REQUIRED
type CDATA #REQUIRED
>


<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST select
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
useCache (true|false) #IMPLIED
databaseId CDATA #IMPLIED
>


<!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST insert
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
>


<!ELEMENT selectKey (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST selectKey
resultType CDATA #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
order (BEFORE|AFTER) #IMPLIED
databaseId CDATA #IMPLIED
>


<!ELEMENT update (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST update
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
databaseId CDATA #IMPLIED
>


<!ELEMENT delete (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST delete
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
databaseId CDATA #IMPLIED
>


<!-- Dynamic -->


<!ELEMENT include EMPTY>
<!ATTLIST include
refid CDATA #REQUIRED
>


<!ELEMENT sql (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST sql
id CDATA #REQUIRED
>


<!ELEMENT trim (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST trim
prefix CDATA #IMPLIED
prefixOverrides CDATA #IMPLIED
suffix CDATA #IMPLIED
suffixOverrides CDATA #IMPLIED
>
<!ELEMENT where (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ELEMENT set (#PCDATA | include | trim | where | set | foreach | choose | if)*>


<!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST foreach
collection CDATA #REQUIRED
item CDATA #IMPLIED
index CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
separator CDATA #IMPLIED
>


<!ELEMENT choose (when* , otherwise?)>
<!ELEMENT when (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST when
test CDATA #REQUIRED
>
<!ELEMENT otherwise (#PCDATA | include | trim | where | set | foreach | choose | if)*>


<!ELEMENT if (#PCDATA | include | trim | where | set | foreach | choose | if)*>
<!ATTLIST if
test CDATA #REQUIRED
>
好了,以上是项目需要的所有文件


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值