框架学习之Spring 第五节 SSH整合开发[Spring2.5+Hibernate3.3+Struts2]

1.首先整合Spring和Hibernate

①引入jar包:

hibernate核心安装包下的:

hibernate3.jar

lib\required\*.jar

lib\optional\ehcache-1.2.3.jar

hibernate 注解安装包下的

lib\test\slf4j-log4j12.jar

Spring安装包下的:

dist\spring.jar

dist\modules\spring-webmvc-struts.jar

lib\jakarta-commons\commons-logging.jar、commons-dbcp.jar、commons-pool.jar

lib\aspectj\aspectjweaver.jar、aspectjrt.jar

lib\cglib\cglib-nodep-2.1_3.jar

lib\j2ee\common-annotations.jar

lib\log4j\log4j-1.2.15.jar

 

数据库使用mysql:

添加 Mysql 驱动包  mysql-connector-java-5.1.16-bin.jar

 

后面还要进行单元测试,所以还有引入 junit4.jar,当然这个可以在后面由 Myeclipse 来进行,安装时已经有了这个jar包,Myeclipse会自动引入

 

②数据源配置的文件: jdbc.properties (放在src目录下)

driverClassName=org.gjt.mm.mysql.Driver
url=jdbc\:mysql\://localhost/hibernate
username=root
password=yinger
initialSize=1
maxActive=500
maxIdle=2
minIdle=1

 

③Spring中的beans.xml (示例的最终版,放在src目录下)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <!-- 开启注解方式进行bean的管理和依赖注入 -->
    <context:annotation-config/>
    
    <!-- 数据源配置的文件的位置 -->
    <context:property-placeholder location="jdbc.properties" />
    
    <!-- 定义数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${driverClassName}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <!-- 连接池启动时的初始值 -->
        <property name="initialSize" value="${initialSize}" />
        <!-- 连接池的最大值 -->
        <property name="maxActive" value="${maxActive}" />
        <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
        <property name="maxIdle" value="${maxIdle}" />
        <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
        <property name="minIdle" value="${minIdle}" />
    </bean>

    <!-- 定义sessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mappingResources">
            <list>
                <value>com/yinger/domain/Person.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
                hibernate.hbm2ddl.auto=update
                hibernate.show_sql=true
                hibernate.format_sql=false
          </value>
        </property>
    </bean>
    
    <!-- 定义事务管理器:此处的含义就是下面的事务管理器管理由sessionFactory创建的session -->
    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
     <!--  开启注解方式使用事务  -->
    <tx:annotation-driven transaction-manager="txManager" />
    
    <!-- Spring管理的bean -->
    <bean id="personService" class="com.yinger.service.impl.PersonServiceBean"></bean>
    
</beans>

④测试 Spring 和 Hibernate  的整合是否成功

新建domain对象:Person

package com.yinger.domain;

public class Person {

    private int id;
    private String name;
    
    public Person(){}
    
    public Person(String name){
        this.name = name;
    }
    
    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;
    }
    
}

新建Person的映射文件:Person.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.yinger.domain">

    <class name="Person">
        <id name="id">
            <generator class="native" />
        </id>
        <property name="name" />
    </class>

</hibernate-mapping>

新建业务层接口:PersonService

package com.yinger.service;

import java.util.List;

import com.yinger.domain.Person;

public interface PersonService {

    public void save(Person p);
    
    public void update(Person p);
    
    public void delete(int personid);
    
    public Person getPerson(int personid);
    
    public List<Person> getPersons();
    
}

新建上面的接口的实现类:PersonServiceBean

package com.yinger.service.impl;

import java.util.List;

import javax.annotation.Resource;

import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.yinger.domain.Person;
import com.yinger.service.PersonService;

@Transactional
public class PersonServiceBean implements PersonService{
    
    private SessionFactory sessionFactory;
    
    @Resource //属性的setter方法注入对象
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    //保存
    public void save(Person p) {//建议使用persist方法,而不是save方法
        sessionFactory.getCurrentSession().persist(p);
    }
    //更新
    public void update(Person p) {//建议使用merge方法(把对游离态的对象的更新与数据库同步),而不是update方法
        sessionFactory.getCurrentSession().merge(p);
    }
    //删除
    public void delete(int personid) {//建议得到对象的方法使用load,免去数据封装的过程
        sessionFactory.getCurrentSession().delete(sessionFactory.getCurrentSession().load(Person.class, personid));
    }
    //得到指定id的person
    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public Person getPerson(int personid) {
        return (Person)sessionFactory.getCurrentSession().get(Person.class, personid);
    }
    //得到所有的person
    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    @SuppressWarnings("unchecked")
    public List<Person> getPersons() {
        return (List<Person>)sessionFactory.getCurrentSession().createQuery("from Person").list();
    }

}

 

基于PersonServiceBean 进行单元测试,编写测试类

package junit.test;

import java.util.List;

import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.yinger.domain.Person;
import com.yinger.service.PersonService;

public class SSHTest {

    public static PersonService personService;
    
    @BeforeClass  //这个方法是在单元测试实例生成之后就会执行的方法,可以在里面写上一些初始化操作
    public static void setUpBeforeClass() throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        personService = (PersonService)ctx.getBean("personService");
    }

    @Test
    public void testSave() {
        for(int i=0;i<5;i++){
            Person person = new Person("personname"+(i+1));
            personService.save(person);        
        }
    }

    @Test
    public void testUpdate() {
        Person person = personService.getPerson(2);
        person.setName("yinger");
        personService.update(person);
    }

    @Test
    public void testDelete() {
        personService.delete(3);
    }

    @Test
    public void testGetPerson() {
        personService.getPerson(4);
    }

    @Test
    public void testGetPersons() {
        List<Person> list = personService.getPersons();
        for(Person person:list){
            System.out.println(person.getName());
        }
    }
}

 

首先测试第一个方法,输出结果:

log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person (name) values (?)

数据库中的数据

18

 

测试delete方法:

Hibernate: select person0_.id as id0_0_, person0_.name as name0_0_ from Person person0_ where person0_.id=?
Hibernate: delete from Person where id=?

19

测试update方法: 【我不清楚为什么这里会有两条select语句,应该是一条就可以了的啊,是不是merge方法的原因呢?望高手指点!】

Hibernate: select person0_.id as id0_0_, person0_.name as name0_0_ from Person person0_ where person0_.id=?
Hibernate: select person0_.id as id0_0_, person0_.name as name0_0_ from Person person0_ where person0_.id=?
Hibernate: update Person set name=? where id=?
20

 

测试getPersons方法(getPerson就不用了)

Hibernate: select person0_.id as id0_, person0_.name as name0_ from Person person0_
personname1
yinger
personname4
personname5

数据库数据保持不变

从上面的测试中,我们可以看出,Spring 和 Hibernate 整合成功了!

接下来做 Hibernate中二级缓存的配置

在beans.xml中关于sessionFactory的配置中添加以下设置,最后如下所示:(假设二级缓存使用的是EhCacheProvider )

        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
                hibernate.hbm2ddl.auto=update
                hibernate.show_sql=true
                hibernate.format_sql=false
                hibernate.cache.use_second_level_cache=true
                hibernate.cache.use_query_cache=false
                hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
            </value>
        </property>

 

然后,自然要在Hibernate下载包中找到与EhCache缓存相关的jar包, 添加进去,然后找到EnCache的配置文件:encache.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
    defaultCache节点为缺省的缓存策略
     maxElementsInMemory 内存中最大允许存在的对象数量
     eternal 设置缓存中的对象是否永远不过期
     overflowToDisk 把溢出的对象存放到硬盘上
     timeToIdleSeconds 指定缓存对象空闲多长时间就过期,过期的对象会被清除掉
     timeToLiveSeconds 指定缓存对象总的存活时间
     diskPersistent 当jvm结束是是否持久化对象
     diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
 -->
<ehcache>
    <diskStore path="D:\cache"/>
    <defaultCache  maxElementsInMemory="1000" eternal="false" overflowToDisk="true"
        timeToIdleSeconds="120"
        timeToLiveSeconds="180"
        diskPersistent="false"
        diskExpiryThreadIntervalSeconds="60"/>
    <cache name="com.yinger.domain.Person" maxElementsInMemory="100" eternal="false"
    overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="600" diskPersistent="false"/>
</ehcache>

 

如上所示,cache标签中就可以添加需要使用二级缓存的类了

当然,也可以在类的映射文件中添加,例如在 Person.hbm.xml 中添加:

    <class name="Person">
        <cache usage="read-write" region="com.yinger.domain.Person"/>
        <id name="id">
            <generator class="native" />
        </id>
        <property name="name" />
    </class>

region的解释:Hibernate为每个实体类创建一个Region作为缓存区, 默认情况下使用类的全路径名做为这个Region的名称

这样同样可以使得Person可以使用二级缓存

 

2.整合Spring 和 Struts2

①引入jar包:

使用到struts2的lib目录下所有不带-plugin结尾的jar文件,但除了struts2-spring-plugin-2.0.11.1.jar

 

②在web容器中实例化spring容器和配置struts2

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <!-- 指定spring的配置文件,默认从web根目录寻找配置文件,我们可以通过spring提供的classpath:前缀指定从类路径下寻找 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:beans.xml</param-value>
    </context-param>
    <!-- 对Spring容器进行实例化 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 配置struts2:注意struts1和struts2中的filter-class配置是不同的 -->
    <filter>
        <filter-name>struts2</filter-name>
        <!-- 
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
         -->
         <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

③配置struts2,编写struts.xml,我这里是包含了另外两个xml文件:

struts.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

    <include file="strutsconstant.xml"></include>
    <include file="strutspackage.xml"></include>
    
</struts>

strutsconstant.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

    <!-- 配置一些常量值 constant -->
    <!-- 通常,struts2按如下搜索顺序加载struts2常量:
        struts-default.xml
        struts-plugin.xml
        struts.xml
        struts.properties
        web.xml
        如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值.
     -->
    <!-- 指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出 -->
    <constant name="struts.i18n.encoding" value="UTF-8"/>
    <!-- 该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理
        如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开 -->
    <constant name="struts.action.extension" value="action,do"/>
    <!-- 设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 -->
    <constant name="struts.serve.static.browserCache" value="false"/>
    <!-- 当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 -->
    <constant name="struts.configuration.xml.reload" value="true"/>
    <!-- 开发模式下使用,这样可以打印出更详细的错误信息 -->
    <constant name="struts.devMode" value="true" />
    <!-- 默认的视图主题 -->
    <constant name="struts.ui.theme" value="simple" />
    <!-- 与spring集成时,指定由spring负责action对象的创建 -->
    <constant name="struts.objectFactory" value="spring" />
     <!-- 该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false --> 
    <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
    
</struts>

strutspackage.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

    <package name="person" namespace="/person" extends="struts-default">
        <global-results>
            <result name="message">/WEB-INF/page/message.jsp</result>
        </global-results>
<!-- 这里使用了通配符,调用的方法名正是通配符中的字符串,class是交给Spring管理的bean(class名是bean的name或者id) -->
        <action name="action_*" class="personAction" method="{1}">
            <result name="list">/WEB-INF/page/persons.jsp</result>
        </action>
    </package>

</struts>

④在Spring的bean管理中添加一个新的bean:

<bean id="personAction" class="com.yinger.web.action.PersonAction"></bean>

注意:这个bean的id 对应struts中定义的action中的class名称

 

以下是测试:

⑤新建action:PersonAction

package com.yinger.web.action;

import java.util.List;

import javax.annotation.Resource;

import com.yinger.domain.Person;
import com.yinger.service.PersonService;

public class PersonAction {
    
    //由于这个类已经交给了Spring管理,所以可以使用Spring的依赖注入功能注入personService
    //并且personService也已经交给了Spring管理
    @Resource private PersonService personService;
    private String message;
    private List<Person> persons;
    
    public String list(){
        this.persons = personService.getPersons();
        return "list";
    }
    
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public List<Person> getPersons() {
        return persons;
    }
    public void setPersons(List<Person> persons) {
        this.persons = persons;
    }    

}

 

⑥新建两个jsp,在WEB-INF目录下的page文件夹中

message.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'message.jsp' starting page</title>
  </head>
  
  <body>
    <s:property value="message"/>
  </body>
</html>

persons.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>人员列表</title>
  </head>
  
  <body>
    <s:iterator value="persons">
        id=<s:property value="id"/>---name=<s:property value="name"/><br/>
    </s:iterator>
  </body>
</html>

好了,一切OK!部署应用,然后启动Tomcat服务器(我的是Tomcat 6.0),最好要保证控制台没有报错,不然就要检查!

2011-8-26 21:59:00 org.apache.catalina.core.AprLifecycleListener init
信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: 
D:\Program Files\MyEclipse Blue Edition\Common\binary\com.sun.java.jdk.win32.x86_1.6.0.013\bin;D:\Program Files\Tomcat 6.0\bin
2011-8-26 21:59:00 org.apache.coyote.http11.Http11Protocol init
信息: Initializing Coyote HTTP/1.1 on http-8080
2011-8-26 21:59:00 org.apache.catalina.startup.Catalina load
信息: Initialization processed in 1408 ms
2011-8-26 21:59:00 org.apache.catalina.core.StandardService start
信息: Starting service Catalina
2011-8-26 21:59:00 org.apache.catalina.core.StandardEngine start
信息: Starting Servlet Engine: Apache Tomcat/6.0.16
2011-8-26 21:59:04 org.apache.catalina.core.StandardContext addApplicationListener
信息: The listener "org.springframework.web.context.ContextLoaderListener" is already configured for this context. The duplicate definition has been ignored.
log4j:WARN No appenders could be found for logger (org.springframework.core.CollectionFactory).
log4j:WARN Please initialize the log4j system properly.
2011-8-26 21:59:05 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring root WebApplicationContext
2011-8-26 21:59:19 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info
信息: Parsing configuration file [struts-default.xml]
2011-8-26 21:59:19 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info
信息: Unable to locate configuration files of the name struts-plugin.xml, skipping
2011-8-26 21:59:19 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info
信息: Parsing configuration file [struts-plugin.xml]
2011-8-26 21:59:19 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info
信息: Parsing configuration file [struts.xml]
2011-8-26 21:59:21 org.apache.catalina.core.StandardContext addApplicationListener
信息: The listener "wdlab.base.BSLoginListener" is already configured for this context. The duplicate definition has been ignored.
2011-8-26 21:59:22 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on http-8080
2011-8-26 21:59:22 org.apache.jk.common.ChannelSocket init
信息: JK: ajp13 listening on /0.0.0.0:8009
2011-8-26 21:59:22 org.apache.jk.server.JkMain start
信息: Jk running ID=0 time=0/84  config=null
2011-8-26 21:59:22 org.apache.catalina.startup.Catalina start
信息: Server startup in 21866 ms

 

打开IE,测试结果: 成功的输出了刚才的数据库中的数据!

21

 

SSH整合成功了!Congratulations!

最终的项目目录结构如下图所示:

22


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SSH2框架是结合了Spring3.2、Struts2.3.4和Hibernate4.2三个框架的一种开发模式。它的目标是将这三个框架的优势结合起来,实现更高效、更方便的开发方式。 首先,Spring框架作为SSH2的核心框架,提供了Spring IOC容器和AOP特性。Spring能够管理对象的生命周期,简化了大量的配置和代码。它还提供了声明式事务管理和面向切面编程能力,极大地简化了事务处理和异常处理。 其次,Struts2作为SSH2的MVC框架,提供了强大的请求处理和表单验证功能。它基于标签和表达式语言,可以直接将页面元素和后台数据进行绑定,大大减少了前端和后台开发的工作量。Struts2还支持拦截器,能够方便地进行权限控制和请求的预处理。 最后,Hibernate作为SSH2的ORM框架,提供了面向对象的数据库访问方式。Hibernate能够将Java对象和数据库表进行映射,并提供了强大的查询和缓存功能。使用Hibernate,开发者可以将精力集中在业务逻辑上,而不需要过多关注数据库操作的细节。 通过整合SpringStruts2和Hibernate,SSH2框架能够极大地简化企业级应用的开发过程。开发者可以更专注于业务逻辑的设计和实现,而不需要关心框架的集成和配置。同时,SSH2框架还提供了良好的扩展性和灵活性,可以根据项目需求进行定制和拓展。 总而言之,SSH2框架是一种结合了SpringStruts2和Hibernate的开发模式,通过整合三个框架的优势,提供了更高效、更方便的开发方式。它简化了项目的配置和集成,提高了开发效率,是开发企业级应用的优秀选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值