Mybatis

Mybatis

MybatisXML配置官网 https://mybatis.org/mybatis-3/zh/configuration.html#settings

三.mybatis中的增删改查

1.在父类工程的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mybatis_learn</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>mybitis-01</module>
        <module>mybatis-02</module>
        <module>mybatis-03</module>
        <module>mybatis-04</module>
    </modules>
    <!--    父工程-->
        <dependencies>
<!--            mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
<!--             mybatis-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
<!--            junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
<!--    在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>
2.在resources中添加配置文件(config.xml)
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--    configuration是核心配置文件-->
<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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
<!--    每一个Mapper.XML都需要在Mybatis核心配置文件中注册!!!-->
    <mappers>
        <mapper resource="com/xinzhi/Dao/UserMapper.xml"></mapper>
    </mappers>
</configuration>
3.编写util工具类
/**
 * @author 王皓皓
 * 读取配置文件
 */
public class MybatisUtil04 {
    private static SqlSessionFactory sqlSessionFactory ;
    static {
        try {
            // 使用Mybatis获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static SqlSession getSqlSession () {
        return sqlSessionFactory.openSession();
    }

}
4.在pojo文件下编写实体类
/**
 * 实体类
 * @author 王皓皓
 */
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 + '\'' +
                '}';
    }
}
5.在dao文件下编写接口UserMapper和其所对应的xml文件UserMapper.xml (命名必须相同)
public interface UserMapper {
    /**
     * 查询全部用户
     * @return  用户集合
     */
    List<User> getUserList();

    /**
     * 通过id查询用户信息
     * @param id   用户id
     * @return     用户对象
     */
    User getUserById (int id);

    /**
     * 万能Map
     * @param map
     * @return
     */
    User getUserById2 (Map <String,Object> map);

    /**
     * 添加一个新用户
     * @param user  用户信息
     * @return   受影响行数
     */
    int addUser (User user);

    /**
     * 万能map
     * @param map
     * @return
     */
    int addUser2 (Map<String,Object> map);

    /**
     * 修改用户
     * @param user  用户信息
     * @return  受影响行数
     */
    int changeUser (User user);

    /**
     * 删除用户
     * @param id  用户id
     * @return  受影响行数
     */
    int deleteUser (int id);

    /**
     * 模糊查询
     * @param value
     * @return
     */
    List<User> userList (String value);

}
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.xinzhi.Dao.UserMapper">
<!--    sql查询语句-->
<!--    查询全部用户-->
    <select id="getUserList" resultType="com.xinzhi.Pojo.User">
        select id,name,pwd from users
    </select>
<!--    查询用户id-->
    <select id="getUserById" parameterType="int" resultType="com.xinzhi.Pojo.User">
       select id,name,pwd from mybatis.users where id = #{id}
    </select>

<!--    添加新用户-->
    <insert id="addUser" parameterType="com.xinzhi.Pojo.User" >
        insert into mybatis.users  values (#{id}, #{name}, #{pwd} )
    </insert>
    
<!--    万能Map,对象中的属性可以直接取出来 传递map的key -->
    <insert id="addUser2" parameterType="map" >
        insert into mybatis.users(id,name,pwd)  values (#{userId}, #{userName}, #{userPwd} )
    </insert>

<!--        万能Map-->
    <select id="getUserById2" parameterType="map" resultType="com.xinzhi.Pojo.User">
        select id,name,pwd from mybatis.users where id = #{userId}  and name = #{userName}
    </select>
    
<!--    修改一个用户-->
    <update id="changeUser" parameterType="com.xinzhi.Pojo.User" >
        update mybatis.users set name=#{name}, pwd=#{pwd} where id = #{id}
    </update>

<!--    删除用户-->
    <delete id="deleteUser" parameterType="int">
        delete from mybatis.users where id = #{id}
    </delete>



<!--    模糊查询-->
    <select id="userList" resultType="com.xinzhi.Pojo.User" >
        select * from users where name like #{value}
    </select>

</mapper>
6.mybatis的util工具类
private static SqlSessionFactory sqlSessionFactory ;
static {
    try {
        // 使用Mybatis获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    } catch (IOException e) {
        e.printStackTrace();
    }

}

/**
 * 既然有了SqlSessionFactory,我们就可以从中获取SqlSession的实例
 * SqlSession 完全包含了面向数据库执行SQL 命令所需的所有方法
 * @return
 */
public static SqlSession getSqlSession () {
    return sqlSessionFactory.openSession();
}
7.万能Map

假设,实体类,或者数据库表,字段或者参数过多,应该考虑用Map(没有叫万能map这一说,但是比较偏门实用)

接口参数传Map

/**
 * 万能map
 * @param map
 * @return
 */
int addUser2 (Map<String,Object> map);
<!--    万能Map,对象中的属性可以直接取出来 传递map的key -->
    <insert id="addUser2" parameterType="map" >
        insert into mybatis.users(id,name,pwd)  values (#{userId}, #{userName}, #{userPwd} )
    </insert>
@Test
public void addUser2 () {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map = new HashMap<String, Object>();
    map.put("userId",5);
    map.put("userName","打酱油");
    map.put("userPwd","456654");
    userMapper.addUser2(map);
    // 提交事物
    sqlSession.commit();
    sqlSession.close();

}
/**
 * 万能Map
 * @param map
 * @return
 */
User getUserById2 (Map <String,Object> map);

-----------------------------------------------------------------------------------

    <select id="getUserById2" parameterType="map" resultType="com.xinzhi.Pojo.User">
        select id,name,pwd from mybatis.users where id = #{userId}  and name = #{userName}
    </select>
    
    
    
   -------------------------------------------------------------------------------- 
    
        @Test
    public void getUserById2 () {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        Map<String,Object> map = new HashMap<String, Object>();
        map.put("userId",2);
        map.put("userName","小李");

        User u =   userMapper.getUserById2(map);
        System.out.println( u.toString());
        sqlSession.commit();
        sqlSession.close();
    }


模糊查询怎么写? (一般用不到,用在业务里
代码执行的时候使用通配符%%
在sql中拼接使用通配符
select * from users where name like #{value} 传值 “%李%” 不能这么写容易sql注入
select * from users where name like %#{value}% 传值 " 李 " 不会造成sql注入

/**
 * 模糊查询
 * @param value
 * @return
 */
List<User> userList (String value);

-------------------------------------------------------------------------------------


    <select id="userList" resultType="com.xinzhi.Pojo.User" >
        select * from users where name like #{value}
    </select>
    
    
    ------------------------------------------------------------------------------
    
       // 模糊查询
    @Test
    public void userLike () {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        List<User> user = userMapper.userList("%李%");
        for (User user1 : user) {
            System.out.println(user1.toString());
        }
        sqlSession.commit();
        sqlSession.close();
    }

四.配置解析

1 核心配置文件
mybatis-config.xml

​ MyBatis 的配置文件包含了会深深影响MyBatis行为的设置和属性信息
​ configuration(配置)

  • properties(属性)

  • settings(设置)

  • typeAliases(类型别名)

  • typeHandlers(类型处理器)

  • objectFactory(对象工厂)

  • plugins(插件)

  • environments(环境配置)

    ​ environment(环境变量)

    ​ transactionManager(事务管理器)

    ​ dataSource(数据源)

  • databaseIdProvider(数据库厂商标识)

  • mappers(映射器)

2.环境配置(environments)

​ MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。 所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单: 每个数据库对应一个 SqlSessionFactory 实例
​ Mybatis默认的事物管理器就是jdbc,连接池POOLED

3.属性(properties)

我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
编写一个配置文件(db.properties)个人写在了resources

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
userName=root
userWord=root

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sx7LHZZF-1620185761288)(C:\Users\王皓皓\Desktop\Mybatis.assets\1620035420078.png)]
以上报错原因是 在xml中,所有的标签都可以规定其顺序
必须按照上面提示的顺序来写

在核心配置文件中引入

<properties resource="db.properties">
    <property name="userName" value="root"/>
    <property name="pwd" value="root"/>
</properties>

第一种引入方式(外部引入)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-srfHUNF0-1620185761291)(C:\Users\王皓皓\Desktop\Mybatis.assets\1620036175764.png)]

第二种引入方式(内部引入加外部 注:#在properties是注释)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ujGrZdFE-1620185761291)(C:\Users\王皓皓\Desktop\Mybatis.assets\1620036305700.png)]

  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置
  • 如果两个文件有同一个字段,优先使用外部配置文件!
4.类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。
它仅用于 XML 配置,意在降低冗余的全限定类名书写

<!--  给实体类起别名-->
    <typeAliases>
        <typeAlias type="com.xinzhi.pojo.User" alias="User"/>
    </typeAliases>

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
扫描实体类的包,它默认别名就为这个类的类名,首字母小写

<!--  给实体类起别名-->
    <typeAliases>
        <package name="com.xinzhi.pojo" />
    </typeAliases>

在实体类比较少的时候,用第一种方式
如果实体类非常多,建议第二种
第一张可以DIY(自定义)别名,第二种不行,如果非要改的话,需要在实体类上增加注解

package com.xinzhi.pojo;

import org.apache.ibatis.type.Alias;

/**
 * 实体类
 * @author 王皓皓
 */
@Alias("niHao")
public class User {

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator
5.设置(settings)

​ 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
6.其他

typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)

7.映射器(mappers)

​ MapperRegistry:绑定注册我们的Mapper文件

第一种:使用相对于类路径(推荐使用,用的多)

<!--    每一个Mapper.XML都需要在Mybatis核心配置文件中注册!!!-->
    <mappers>
        <mapper resource="com/xinzhi/Dao/UserMapper.xml"></mapper>
    </mappers>

第二种:使用class文件绑定注册

<mappers>
        <mapper class="com.xinzhi.dao.UserMapper"></mapper>
</mappers>

​ 使用class注册的注意点:
接口和它的Mapper配置文件必须同名
接口和它的Mapper配置文件必须在同一个包下

方式三:使用扫描包进行注入绑定

   <mappers>
<!--        <mapper resource="com/xinzhi/Dao/UserMapper.xml"></mapper>-->
<!--        <mapper class="com.xinzhi.dao.UserMapper"></mapper>-->
        <package name="com.xinzhi.dao"/>
    </mappers>

​ 使用扫描包注册的注意点:
接口和它的Mapper配置文件必须同名
接口和它的Mapper配置文件必须在同一个包下

会出现的报错信息未注册Mapper
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sJ8NAhWx-1620185761292)(C:\Users\王皓皓\Desktop\Mybatis.assets\屏幕截图 2021-05-03 192720.png)]

8.作用域(Scope)和生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKligo76-1620185761294)(C:\Users\王皓皓\Desktop\Mybatis.assets\1620091342636.png)]

作用域和生命周期是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了
最佳作用域是方法作用域(也就是局部方法变量)
SqlSessionFactory
说白了可以理解为数据库连接池
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
SqlSessionFactory 的最佳作用域是应用作用域
最简单的就是使用单例模式或者静态单例模式(保证全局只有一个变量)
SqlSession
连接到连接池的一个请求
每个线程都应该有它自己的 SqlSession 实例。SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
用完后要赶紧关闭,否则会造成资源的占用

五.解决属性名和字段名不一致的问题

1.问题

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jOGBhI6D-1620185761296)(C:\Users\王皓皓\Desktop\Mybatis.assets\Mybatis.assets\1620094468507.png)]数据库中的字段
新建项目,拷贝之前的,测试实体类字段不一致的情况(原来是pwd改为passWord)

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

测试出现问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9TN1s81I-1620185761297)(C:\Users\王皓皓\Desktop\Mybatis.assets\Mybatis.assets\1620094496779.png)]

解决方法:
起别名(没什么卵用)

<!--    查询用户id-->
    <select id="getUserById" parameterType="_int" resultType="com.xinzhi.pojo.User">
       select id,name,pwd as passWord from mybatis.users where id = #{id}
    </select>
2.resultMap

结果集映射

<!--    sql查询语句-->
<!--    查询用户id-->
<!--    结果集映射-->
  c
    <select id="getUserById"  resultMap="userMap">
       select id,name,pwd from mybatis.users where id = #{id}
    </select>
  • resultMap元素是Mybatis中最重要最强大的元素
  • ResultMap的设计思想,对于简单的语句根本不需要配置显示的结果映射,而对于一些复杂一点的语句只需描述它们的关系即可
  • ResultMap最优秀的地方在于,虽然你已经对他相当了解了,但是根本不需要显示地用到他们
  • 如果世界总是这么简单就好了

六.日志

6.1日志工厂

如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手
以前用:sout,debug
现在用:日志工厂
SLF4J
LOG4J (掌握)
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING (掌握)
NO_LOGGING
其他了解

STDOUT_LOGGING标准日志输出
在mybatis配置核心文件配置我们的日志

控制台输出以下信息
Opening JDBC Connection
Created connection 1291113768.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528]
> Preparing: select id,name,pwd from mybatis.users where id = ?
> Parameters: 2(Integer)
<
Columns: id, name, pwd
<
Row: 2, 小李, 111
<== Total: 1
User{id=2, name=‘小李’, passWord=‘null’}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528]
Returned connection 1291113768 to pool.

Process finished with exit code 0

6.2 Log4j

百度查用法

七.分页

7.1使用Limit分页
SELECT  * from user  limit   startIndex,pagesize

使用mybatis实现分页,核心sql
1.接口

/**
 * 分页
 * @param map
 * @return
 */
List<User> getUserByLimit(Map<String, Integer> map);

2.Mapper.xml

<resultMap id="userMap" type="u">
    <!-- column数据库中的字段,property实体类中的属性  -->
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="passWord"/>
</resultMap>

<select id="getUserByLimit" parameterType="map" resultMap="userMap">
    select * from users limit #{startIndex},#{pageSize}
</select>

3.测试

@Test
public void getUserByLimit () {
    SqlSession sqlSession = MybatisUtil04.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",0);
    map.put("pageSize",3);
    List<User> userList = userMapper.getUserByLimit(map);
    for (User uuu: userList) {
        System.out.println(uuu);
    }
    sqlSession.close();
}
7.2 RowBounds分页(简单了解)

​ 百度查就行一大堆

八.使用注解开发

8.1面向接口编程

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

关于接口的理解。

–接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
接口的本身反映了系统设计人员对系统的抽象理解。
接口应有两类:第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
一个体有可能有多个抽象面。
抽象体与抽象面是有区别的。

三个面向区别

面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题

8.2使用注解开发
1.注解在接口上实现
/**
 * 查询全部用户
 * @return
 */
@Select("select * from users")
List<User> getUsers ( );
2.需要再核心配置文件中绑定接口(coonfig.xml)
<!--    绑定接口-->
    <mappers>
        <mapper class="com.xinzhi.dao.UserMapper"/>
    </mappers>
3.测试
@Test
public void test () {
    SqlSession sqlSession = MybatisUtil05.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = userMapper.getUsers();
    for (User uuu: userList
         ) {
        System.out.println(uuu);
    }
    sqlSession.close();
}

本质:反射机制实现
底层:动态代理!

增删改查仿造上面来写就ok

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值