HibernateDay02(持久化类 悲乐观锁 事务 session一级缓存 Criteria接口查询)

持久化类
    就是一个Java类(编写的JavaBean),这个Java类与表建立了关系就可以成为持久化类
        持久化类 = JavaBean + xxx.hbm.xml

持久化类的编写规则
1、提供一个无参数 public访问控制符的构造器       底层需要进行反射
2、提供一个标识属性,映射数据表主键字段            唯一标识OID,数据库中通过主键。Java对象通过地址确定对象,持久化类通过唯一标识OID确定记录
3、所有属性提供public访问控制符的 set或者get方法
4、标识属性应尽量使用基本数据类型的包装类型

区分自然主键和代理主键
1、创建表的时候
    自然主键:对象本身的一个属性,创建一个人员表,每个人都有一个身份证号(唯一的)使用身份证号作为表的主键,自然主键(开发不会用 因为可能重复)

    代理主键:不是对象本身的一个属性,创建一个人员表,为每个人员单独创建一个字段,用这个字段作为主键,代理主键(推荐使用)

2、创建表的时候尽量使用代理主键创建表

主键的生成策略
1、increment:适用于short,int,long作为主键,不是使用的数据库自动增长机制
    Hibernate中提供的一种增长机制
        先进行查询 select max(id) from user
        再进行插入 获得最大值+1作为新的记录的主键
    问题:不能在集群环境下或者有并发访问的情况下使用

2、identity:适用于short,int,long作为主键,但是这个必须使用在有自动增长数据库中,才有的是数据库底层的自动增长机制
    底层使用的是数据库的自动增长(auto_increment)像Oracle数据库没有自动增长

3、sequence:适用于short,int,long作为主键,底层使用的是序列的增长方式
    Oracle数据库底层没有自动增长,想自动增长需要使用序列

4、uuid:适用于char,varchar类型的作为主键
    使用随机的字符串作为主键

5、native:本地策略,根据底层的数据库不同,自动选择适用于该种数据库的生成策略(short,int,long)
    如果底层使用的是MySQL的数据库,相当于identity
    如果底层使用的是Oracle数据库,相当于sequence

6、assigned:主键的生成不用Hibernate管理了,必须手动设置主键

持久化对象的状态
1、Hibernate的持久化类
    持久化类:Java类与数据库的某个表建立了映射关系,这个表就称之为持久化类
        持久化类=Java类+hbm的配置文件

2、Hibernate的持久化类的状态
    Hibernate为了管理持久化类:将持久化类分成了三个状态
        瞬时态:Transient Object
            没有持久化标识OID,没有被纳入到Session对象的管理

        持久态:Persistent Object
            有持久化标识OID,已经被纳入到Session对象的管理
            注意:持久化持久态的对象有自动更新数据库的能力

        脱管态:Detached Object
            有持久化标识OID,没有被纳入到Session对象的管理

持久化对象的状态的转换
    1、瞬时态Transient      没有oid没有被session管理
        获得瞬时态的对象
            User user = new User();
        瞬时态对象转换成持久态
            save()/saveOrUpdate()
        瞬时态对象转换成脱管态
            user.setId(1)
    2、持久态Persistent         有持久化标识的OID,已经被纳入到Session对象的管理
        获得持久态的对象
            get()/load()
        持久态转换成瞬时态对象
            delete()    比较有争议的,进入特殊的状态(删除态:Hibernate中不建议使用的)
        持久态对象转换成脱管对象
            session的close()/evict()/clear()
    3、脱管态Detached           有持久化标识OID,没有被纳入到Session对象的管理
        获得脱管态对象,不建议直接获得脱管态的对象
            User user = new User();
            user.setId(1);
        脱管态对象转换成持久态对象
            update()/saveOrUpdate()/lock()
        脱管态对象转换成瞬时态对象
            user.setId(null)
    4、持久态对象有自动更新数据库的能力

Session对象的一级缓存
1、什么是缓存
    其实就是一块内存空间,将数据源(数据库或文件)中的数据放到缓存中,再次获取的时候,直接从缓存中获取,可以提升程序的性能

2、Hibernate框架提供了两种缓存
    一级缓存        自带的不可卸载的,一级缓存的生命周期与session一致,一级缓存称为session级别的缓存
    二级缓存        默认没有开启,需要手动配置才可以使用的,二级缓存可以在多个session中共享数据,二级缓存称为是sessionFactory级别的缓存

3、Session对象的缓存概述
    Session接口中,有一系列的java的集合,这些java集合构成了Session级别的缓存(以及缓存),将对象存入到一级缓存中,
    session没有结束生命周期,那么对象在session中存放着

    内存中包含session实例->Session的缓存(一些集合)->集合中包含的是缓存对象

4、证明一级缓存的存在,编写查询的代码即可证明
    在同一个session对象中查询两次,可以证明用了缓存

5、Hibernate框架是如何做到数据库发生包含时进行同步操作
    使用get方法查询User对象
    然后设置User对象的一个属性,注意:没有做update的操作 却发现数据库中的记录也改变了
    利用快照机制来完成的(SnapShot)

Session接口中与以及缓存相关的方法
    Session.clear()                清空缓存
    Session.evict(Object entiyty)  从一级缓存中清除指定的实体对象
    Session.flush()                刷出缓存

事务的概念
1、什么是事务
    事务就是逻辑上的一组操作,组成事务的各个执行单元,操作要么全部成功,要么全部失败
    转账的 扣钱和加钱 两个操作组成了一个事情

2、事务的特性
    原子性     事务不可分隔
    一致性     事务执行的前后数据的完整性保持一致
    隔离性     一个事务执行的过程中,不应该收到其他的事务的干扰
    持久性     事务一旦提交,数据就永远保持在数据库中

3、如果不考虑隔离性,引发一些读的问题
    脏读          一个事务读到另一个事务未提交的数据
    不可重复读       一个事务读到了另一个事务已经提交的update数据,导致多次查询结果不一致
    虚读          一个事务读到了另一个事务已经提交的insert数据,导致多次查询结果不一致

4、通过设置数据库的隔离级别来解决读的问题
    未提交读:以上的读的问题都有可能发生
    已提交读:避免脏读,但是不可重复读,虚读都有可能发生
    可重复读:避免脏读、不可重复读,但是虚读可能发生
    串行化:以上读的情况都可以避免

5、如果要在Hibernate的框架中设置隔离级别,需要在hibernate.cfg.xml的配置文件中通过标签来配置
    通过hibernate.connection.isolation = 4来配置
    取值
        1-Read uncommitted isolation
        2-Read committed isolation oracle默认
        4-Repeatable read isolation mysql默认
        8-Serializable isolation

丢失更新的问题
1、如果不考虑隔离性,也会产生写入数据的问题,这一类类的问题叫丢失更新的问题
2、例如:两个事务同时对某一条记录做修改,就会引发丢失更新的问题
    A事务和B事务同时获取到一条数据,同时再做修改
    如果A事务修改完成后,提交了事务
    B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对数据产生影响

3、解决方案
    悲观锁
        才有的是数据库提供的一种锁机制,如果采用了这种机制,在SQL语句后面添加for update子句
            当A事务在操作该条记录时,会把该条记录锁起来,其他事务是不能操作这条记录的
            只有当A事务提交后,锁释放了,其他事务才能操作该条记录
    乐观锁
        采用版本号的机制来解决的,会给表结构添加一个字段version=0,默认值是0
            当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发现版本号的值相同时,才可以提交事务。同时会更新版本号version=1
            当B事务操作完该条记录,提交事务时,会先检查版本号,如果发现版本不同时,程序会出现错误

4、使用Hibernate框架来解决丢失更新的问题
    悲观锁
        使用session.get(Customer.class,1.LockMode.UPGRADE)方法
    乐观锁
        1、在对应的JavaBean中添加一个属性,名称可以是任意的。例如:private Integer version ;提供get和set方法
        2、在映射的配置文件中,提供<version name="version"/>标签即可

绑定本地的Session
1、JavaWeb的事务,需要在业务层使用Connection来开启事务
    一种是通过参数的方式传递下去
    另一种是把Connection绑定到ThreadLocal对象中
2、现在的Hibernate框架中使用session对象来开启事务,所以需要传递session对象,框架提供了ThreadLocal的方式
    需要在hibernate.cfg.xml的配置文件中提供配置
        <property name="hibernate.current_session_context_class">thread</property>

    重新HibernateUtil工具类,使用SessionFactory的getCurrentSession()方法,获取当前Session对象,并且该session对象不用手动关闭。
    线程结束了,会自动关闭
        public static Session getCurrentSession(){
            return factory.getCurrentSession();
        }

        想使用getCurrentSession()方法,必须先配置才能使用

Query查询接口
1、具体的查询代码:
    //1、查询所有记录
    Query query = session.createQuery("from Customer");
    List<Customer> list = query.list();
    System.out.println(list);
    //2、条件查询
    Query query = session.createQuery("from Customer where name=?");
    query.setString(0,"l");
    List<Customer> list = query.list();
    System.out.println(list);
    //3、条件查询
    Query query = session.createQuery("from Customer where name = :aaa and age =:bbb");
    query.setString("aaa","run");
    query.setInteger("bbb",12);
    List<Customer> list = query.list();
    System.out.println(list);

Criteria(做条件查询非常合适)查询接口
1、具体的查询代码
    //1、查询所有记录
    Criteria criteria = session.createCriteria(Customer.class)
    List<Customer> list = criteria.list();
    syso(list)

    //2、条件查询
    Criteria criteria = session.crateCriteria(Customer.class)
    criteria.add(Restrictions.eq("name","abc"));
    List<Customer> list = criteria.list();
    syso(list);

    //3、条件查询
    Criteria criteria = session.createCriteria(Customer.class);
    criteria.add(Restrictions.eq("name","ll"));
    criteria.add(Restrictions.eq("age",11));
    List<Customer> list = criteria.list();
    syso(list); 

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!-- 先配置SessionFactory标签:一个数据库对应一个SessionFactory -->
    <session-factory>
        <!-- 必须要配置的参数有5个,4大参数,数据库的方言 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_day01</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">123</property>
        <!-- 数据库的方言(使用不同的数据库) -->
        <property name="hibernate.dialet">org.hibernate.dialect.MySQLDialect</property>
        <!-- 可选配置 -->
        <!-- 显示SQL语句,在控制台显示 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 格式化SQL语句 -->
        <property name="hibernate.format_sql">true</property>
        <!-- 生成数据库的表结构 
            create 删掉之前的重新创建一般用来测试 create-drop删了之前了创建完再删
            update:如果没有表结构,创建表结构。如果存在,不会创建,添加数据
            validate:进行校验 如果字段和数据库中的不同会报错
        -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- 设置数据库的隔离级别,用默认值就可以 -->
        <property name="hibernate.connection.isolation">4</property>
        <!-- 开启绑定本地的session -->
        <property name="hibernate.current_session_context_class">thread</property>

        <!-- 映射配置文件,需要引入映射的配置文件 -->
        <mapping resource="my/domain/User.hbm.xml"/>
        <mapping resource="my/domain/Person.hbm.xml"/>
    </session-factory>
</hibernate-configuration>  

Person.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="my.domain.Person" table="t_person">
        <id name="pid" column="pid">
            <!-- increment 获取主键的最大值,+1后作为主键-->
            <!-- <generator class="uuid"/> -->
            <!-- <generator class="assigned"/>自己维护 也就是自己设置 -->
            <generator class="uuid"/>
        </id>
        <property name="pname" column="pname"   length="30"></property>
    </class>
</hibernate-mapping>

User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="my.domain.User" table="t_user">
        <id name="id" column="id">
        <!-- 
        increment 获取主键的最大值,+1后作为主键
        -->
            <generator class="native"/>
        </id>
        <!-- 乐观锁,就使用version标签 -->
        <version name="version"/>
        <property name="name" column="name" length="30"></property>
        <property name="age" column="age"></property>
    </class>
</hibernate-mapping>

Domain User

    private Integer id;
    private String name;
    private Integer age;

    //在JavaBean中添加属性
    private Integer version;

    public Integer getVersion() {
        return version;
    }

Dao

public class UserDao {

    public void save1(User u1) {
        Session session = HibernateUtils.getCurrentSession();
        session.save(u1);
    }

    public void save2(User u2) {
        Session session = HibernateUtils.getCurrentSession();
        session.save(u2);
    }
}

Service

public class UserService {

    public void save(User u1,User u2) {
        UserDao dao = new UserDao();
        //获取SESSION
        Session session = HibernateUtils.getCurrentSession();
        //开启事务
        Transaction tr = session.beginTransaction();
        try {
            dao.save1(u1);
//          int a = 10/0;
            dao.save2(u2);
            //提交事务
            tr.commit();
            //结束事务
        } catch (Exception e) {
            e.printStackTrace();
            //出现问题,回滚事务
            tr.rollback();
        } finally {
            //原先自己释放资源,现在,session不用关闭,线程结束了,自动关闭的
        }

    }
}

Servlet doGet

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        User u1 = new User();
        u1.setName("u1");
        User u2 = new User();
        u2.setName("u2");
        new UserService().save(u1, u2);
    }
public class Demo1 {
    @Test
    public void fun1() {
        //获取session
        Session session = HibernateUtils.getSession();
        Transaction tr = session.getTransaction();
        tr.begin();
        //保存用户
        User user = new User();
        user.setName("abc");

        session.save(user);
        tr.commit();

        session.close();
    }

    /**
     * uuid主键的生成策略
     */
    @Test
    public void fun2() {
        //获取session
        Session session = HibernateUtils.getSession();
        Transaction tr = session.getTransaction();
        tr.begin();
        //保存用户
        Person user = new Person();
        user.setPname("abc");

        session.save(user);
        tr.commit();

        session.close();
    } 

    /**
     * 持久类的对象的三个状态
     */
    @Test
    public void run3() {
        //获取session
        Session session = HibernateUtils.getSession();
        Transaction tr = session.getTransaction();

        //持久User的对象
        //瞬时态:没有OID的值,没有被session管理,此时User对象是瞬时态对象
        User user = new User();
        user.setName("abc");
        user.setAge(11);

        //使用session保存用户,默认的情况下,把user对象也保存到session的缓存中
//      session.save(user);
        Serializable id = session.save(user);
        System.out.println("主键值"+id);
        //user是持久态对象 

        tr.commit();
        //session销毁,缓存没有了
        session.close();
        //打印
        //user对象存在id值,session销毁,缓存不存在,session不管理user对象了
        //user是脱管态的对象
        System.out.println(user.getId());
        System.out.println(user.getName());
    }
}
public class Demo2 {
    /**
     * 持久态的对象有自动更新数据库的能力
     * Session的一级缓存
     */
    @Test
    public void run1() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        //获取持久态的对象
        User user = session.get(User.class,1);
        //user是持久态,有自动更新数据库的能力
        System.out.println(user.getName());

        //重新设置新的名称
        user.setName("run1");

        //正常编写代码
//      session.update(user);
        tr.commit();
        session.close();

    }
    /**
     * 证明一级缓存的存在
     */
    @Test
    public void run2() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        //创建对象
        User user = new User();
        user.setName("run2");

        //保存用户 user已经被存入到session的缓存当中
        Serializable id  = session.save(user);
        System.out.println(id);

        //获取对象 不会看到SQL语句
        User user2 = session.get(User.class, id);
        System.out.println(user2);
        tr.commit();
        session.close();

    }
    @Test
    public void run3() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        //最简单的证明 查询两次
        User user1 =session.get(User.class, 1);
        System.out.println(user1);
        User user2 =session.get(User.class, 1);
        System.out.println(user1);

        tr.commit();
        session.close();

    }

    /**
     * 快照机制
     * 
     */
    @Test
    public void run4() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        //获得持久态对象
        User user = session.get(User.class, 1);
        //重新设置名称
        user.setName("我的天");

        tr.commit();
        session.close();

    }
    /**
     * Session.clear()                 清空缓存
     */
    @Test
    public void run5() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        User user1 =session.get(User.class, 1);
        System.out.println(user1);
        //清空缓存
        session.clear();
        User user2 =session.get(User.class, 1);
        System.out.println(user2);

        tr.commit();
        session.close();

    }
    /**
     * Session.evict()清除指定的对象
     */
    @Test
    public void run6() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        User user1 =session.get(User.class, 1);
        System.out.println(user1);
        //清空缓存
        session.evict(user1);
        User user2 =session.get(User.class, 1);
        System.out.println(user2);

        tr.commit();
        session.close();

    }

    @Test
    public void run7() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        //获得持久态对象
        User user = session.get(User.class, 1);
        //重新设置名称
        user.setName("11");
        //自动刷新缓存
        session.flush();
        tr.commit();
        session.close();

    }
}
public class Demo3 {
    @Test
    public void run1() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        //获得持久态对象
        User user = session.get(User.class, 1);
        //重新设置名称
        user.setName("run1");
        tr.commit();
        session.close();
    }

    @Test
    public void run2() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        //获得持久态对象
        User user = session.get(User.class, 1);
        //重新设置名称
        user.setAge(2);
        tr.commit();
        session.close();
    }


}
/**
 * 测试查询
 * @author Administrator
 *
 */
public class Demo4 {
    /**
     * 测试Query的查询接口
     */
    @Test
    public void run1() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();
        //查询的方式 HQL from User 
        Query query = session.createQuery("from User ");
        //查询
        List<User> list = query.list();
        for(User user:list) {
            System.out.println(user);
        }
        tr.commit();
        session.close();
    }
    /**
     * 添加查询条件
     */
    @Test
    public void run2() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();
        //查询的方式 HQL from User where 属性条件
        //QL:select * from t_user where 字段 条件
        Query query = session.createQuery("from User where age>?");
        //设置值
        query.setInteger(0, 12);
        //查询
        List<User> list = query.list();
        for(User user:list) {
            System.out.println(user);
        }
        tr.commit();
        session.close();
    }
    @Test
    public void run3() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();
        //查询的方式 HQL from User where 属性条件
        //QL:select * from t_user where 字段 条件
        Query query = session.createQuery("from User where name like ?");
        //设置值
        query.setString(0, "%r%");
        //查询
        List<User> list = query.list();
        for(User user:list) {
            System.out.println(user);
        }
        tr.commit();
        session.close();
    }
    @Test
    public void run4() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();
        //查询的方式 HQL from User where 属性条件
        //QL:select * from t_user where 字段 条件
        // aaa是自己随便定的
        Query query = session.createQuery("from User where age>:aaa");
        query.setInteger("aaa", 10);
        //查询
        List<User> list = query.list();
        for(User user:list) {
            System.out.println(user);
        }
        tr.commit();
        session.close();
    }
    /**
     * Criteria接口,非常适合条件查询
     */
    @Test
    public void run5() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();
        //先获取到Criteria接口
        Criteria criteria = session.createCriteria(User.class);
        //没有添加条件,查询所有的数据
        List<User> list = criteria.list();
        System.out.println(list);


        tr.commit();
        session.close();
    }
    /**
     * 按条件的查询
     */
    @Test
    public void run6() {
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();
        //先获取到Criteria接口
        Criteria criteria = session.createCriteria(User.class);
        //添加查询的条件 select * from t_user where age>11
        //Criterion 是Hibernate提供的条件查询的对象,想传入条件的 使用工具类Restrictions
        //Restrictions提供的静态的方法,拼接查询的条件
        criteria.add(Restrictions.gt("age", 11));
        criteria.add(Restrictions.like("name","%run%"));

        List<User> list = criteria.list();
        System.out.println(list);
        tr.commit();
        session.close();
    }
}

Utils

/**
 * Hibernate框架的工具类
 * @author Administrator
 *
 */
public class HibernateUtils {
    //ctrl + shift + x 大写
    private static final Configuration CONFIG;
    private static final SessionFactory FACTORY;

    //编写静态代码块
    static {
        //加载XML的配置文件
        CONFIG = new Configuration().configure();
        //构建工厂
        FACTORY = CONFIG.buildSessionFactory();
    }

    /**
     * 从工厂中获取Session对象
     * @return
     */
    public static Session getSession() {
        return FACTORY.openSession();
    }

    /**
     * 从ThreadLocal类中获取到session的对象
     * @return
     */
    public static Session getCurrentSession() {
        return FACTORY.getCurrentSession();
    }

    public static void main(String[] args) {
        //调用获取session的方法
        getSession();
    }

}

CriteriaDao

    /**
     * 带查询条件的查询所有的客户
     */
    public List<Customer> findAll(String custName){
        //QBC查询
        Session session = HibernateUtils.getSession();
        Transaction tr = session.beginTransaction();

        //查询
        Criteria criteria = session.createCriteria(Customer.class);

        //添加查询的条件
        if(custName != null && !custName.trim().isEmpty()) {
            //添加查询条件
            criteria.add(Restrictions.like("cust_name", "%"+custName+"%"));
        }

        //查询
        List<Customer> list = criteria.list();
        tr.commit();
        session.close();
        return list;
    }

CriteriaServlet

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //先获取请求的参数
        request.setCharacterEncoding("utf-8");
        //获取到客户的名称
        String custName= request.getParameter("custName");
        //查询所有的方法时候,传入进去
        List<Customer> list = new CustomerService().findAll(custName);
        //存入request
        request.setAttribute("list", list);
        //转发
        request.getRequestDispatcher("/jsp/customer/list.jsp").forward(request, response);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值