Hibernate

Hibernate
Hibernate简介:官网:http://hibernate.org/
Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate 可以应用在任何使用 JDBC 的场合, 既可以在 Java 的客户端程序使用,也可以在 Servlet/JSP 的 Web 应用中使用,最具革命意义的是,Hibernate 可以在应用 EJB 的 J2EE 架构中取代 CMP,完成数据持久化的重任。 ORM 框架,对象关系映射(Object/Relation Mapping)。

配置文件:
附件:
<hibernate-configuration>
<session-factory>
<!--数据库连接设置 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/test</property>
<property name="connection.username">root</property>
<property name="connection.password">1996</property>

<!-- 方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 控制台显示SQL -->
<property name="show_sql">true</property>
<!-- 自动更新表结构 -->
<property name="hbm2ddl.auto">update</property>
<mapping resource="com/java1234/model/Student.hbm.xml"/>
</session-factory>
</hibernate-configuration>

在对应model包中对应的类配置对应的xml
xml配置版:
附件:
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="student">
<id name="id" column="id">//与数据库中的列名对应
<generator class="native"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>

注解版:(直接在model对应的类中注解)
@Entity
@Table(name="t_teacher") //表名
public class Teacher {
private long id;
private String name;

@Id
@GeneratedValue(generator="_native") //主键
@GenericGenerator(name="_native",strategy="native")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

JUNIT 引入
新建一个JUnit test case 勾选方法生成类,然后将测试代码粘贴过去。运行时run as junit test就行,执行成功显示绿色进度条。失败显示红色进度条。

映射对象标识符(OID) Hibernate 用对象标识符(OID)来区分对象
Hibernate 对象标识符生成策略主键的分类 业务主键 VS 代理主键 代理主键是不具有业务性的;
1,increment 由 Hibernate 自动以递增的方式生成标识符,适用代理主键;
2,identity 由底层数据库生成标识符;适用代理主键;
3,sequcence 由 Hibernate 根据底层数据库的序列来生成标识符;适用代理主键;
4,hilo Hibernate 根据 high/low 算法来生成标识符。适用代理主键
5,native 根据底层数据库对自动生成标识符的支持能力, 来选择 identity,sequence 或 hilo;

JUnit4方法详解
setUpBeforeClass() 类初始化前调用;
tearDownAfterClass() 类初始化后调用;
setUp() 在测试方法前调用;
tearDown() 在测试方法后调用;

级联保存更新

在<many-to-one>这端,cascade 默认是”none”,假如我们希望在持久化多的一端的时候,自动级 联保存和更新一的一端,我们可以把 cascade 设置成”save-update”;(Student.hbm.xml)
实例:


班级学生一对多映射实现(双向)

原理:一个班级多个学生,在班级类建立学生集合。通过班级添加多个学生来实现
Hibernate: insert into t_class (className) values (?)
Hibernate: insert into student (name, classId) values (?, ?)
Hibernate: insert into student (name, classId) values (?, ?)
Hibernate: update student set classId=? where id=?
Hibernate: update student set classId=? where id=?
class.hbm.xml配置:实现绑定
<set name="students" cascade="save-update">
<key column="classId"></key>
<one-to-many class="com.java1234.model.Student"></one-to-many>
</set>

inverse 属性 优点:优化性能(少执行一条SQL语句)。一般是在一对多中一的类中设置,也就是实例中的class.hbm.xml

级联删除:(不推荐使用,容易造成企业业务数据丢失)
ERROR: Cannot delete or update a parent row: a foreign key constraint fails (`test/student`, CONSTRAINT `FK_bpo4fedx1yvfay69587qo0jpd` FOREIGN KEY (`classId`) REFERENCES `t_class` (`classId`)) 报这个错意思是存在关联主键的表,要先删除关联表才能删除需要删除的表

使用方法:将cascade属性设置为delete即可

语句执行过程:
Hibernate : select class0_.classId as classId1_1_0_, class0_.className as classNam2_1_0_ from t_class class0_ where class0_.classId=?
Hibernate : select students0_.classId as classId3_1_0_, students0_.id as id1_0_0_, students0_.id as id1_0_1_, students0_.name as name2_0_1_, students0_.classId as classId3_0_1_ from student students0_ where students0_.classId=?
Hibernate : delete from student where id=?
Hibernate : delete from t_class where classId=?

Hibernate 中四种对象状态

临时状态(transient):刚用 new 语句创建,还没有被持久化,并且不处于 Sesssion 的缓存中。处于临时状态的 Java 对象被称为临时对象。
持久化状态(persistent):已经被持久化,并且加入到 Session 的缓存中。处于持久化状态的 Java 对象被称为持久化对象。
删除状态(removed):不再处于 Session 的缓存中,并且 Session 已经计划将其从数据库中删除。处于删除状态的 Java 对象被称为删除对象。
游离状态(detached):已经被持久化,但不再处于 Session 的缓存中。处于游离状态的 Java 对象被称为游离对象。



Session 常用方法讲解
1,save()方法 将一个临时对象转变成持久化对象;
2,load()方法 VS get()方法 都是根据 OID 从数据库中加载一个持久化对象。 区别 1:假如数据 库中不存在与 OID 对应的记录,Load()方法会抛出异常,而 get()方法返回 null; 区别 2:load 方法默认采用延迟加载策略,get 方法采用立即检索策略;
3,update()方法 将一个游离对象转变为持久化对象;
4,saveOrUpdate()方法 包含了 save()和 update()方法;
5,merge()方法,合并对象;
6,delete()方法,删除对象;

Hibernate 基本映射类型


集合类型映射

1, Set 无序 元素不可重复
<set name="images" table="t_image">
<key column="studentId"></key>
<element column="imageName" type="string"></element>
</set>
2, List 有序 元素可重复
<list name="images" table="t_image2">
<key column="studentId"></key>
<list-index column="imageIndex"></list-index>
<element column="imageName" type="string"></element>
</list>
3, Bag 无序 元素可重复
<idbag name="images" table="t_image3">
<collection-id type="long" column="imageId">
<generator class="increment"></generator>
</collection-id>
<key column="studentId"></key>
<element column="imageName" type="string"></element>
</idbag>
4, Map 键值对
<map name="images" table="t_image4">
<key column="studentId"></key>
<map-key column="imageKey" type="string"></map-key>
<element column="imageName" type="string"></element>
</map>

Hibernate 映射继承

第一节:每个具体类对应一个表
定义一个抽象类,两个子类,每个子类对应一个表
第二节:根类对应一个表
定义三个实体类,两个子类一个父类,父类对应一个表,表里有字段区分子类的类型
<hibernate-mapping package="com.java1234.model">
<class name="Image2" table="image2">
<id name="imageId" column="imageId">
<generator class="native"></generator>
</id>
<discriminator column="imageType" type="string"/> //子类类型区分列
<property name="imageName" column="imageName"></property>
<many-to-one name="s" column="stuId" class="com.java1234.model.Student2" cascade="save-update"/>
<subclass name="com.java1234.model.LifeImage2" discriminator-value="li"></subclass> //设置子类与其在数据库中类型属性值
<subclass name="com.java1234.model.WorkImage2" discriminator-value="wi"></subclass>
</class>
</hibernate-mapping>
第三节:每个类对应一个表
定义三个实体类,两个子类一个父类,在父类的配置文件里面配置与子类对应的主外键关系。数据库中生成三张表,子类表添加了记录,父类表才能添加。
<hibernate-mapping package="com.java1234.model">
<class name="Image3" table="image3">
<id name="imageId" column="imageId">
<generator class="native"></generator>
</id>
<property name="imageName" column="imageName"></property>
<many-to-one name="s" column="stuId" class="com.java1234.model.Student3" cascade="save-update"/>
<joined-subclass table="t_LifeImage3" name="com.java1234.model.LifeImage3">
<key column="lifeimageId"></key>
</joined-subclass> //设置子类表名称,与父子表间的主外键关系
<joined-subclass table="t_WorkImage3" name="com.java1234.model.WorkImage3">
<key column="workimageId"></key>
</joined-subclass>
</class>
</hibernate-mapping>
实例:

Hibernate 一对一映射关系实现

1. 按照主键映射
Address.hbm.xml
<hibernate-mapping package="com.java1234.model">
<class name="Address" table="t_address">
<id name="id" column="addressId">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="address" column="address"></property>
<property name="zipcode" column="zipcode"></property>
<one-to-one name="user" class="com.java1234.model.User" constrained="true"></one-to-one>
</class>
</hibernate-mapping>

User.hbm.xml
<hibernate-mapping package="com.java1234.model">
<class name="User" table="t_user">
<id name="id" column="userId">
<generator class="native"></generator>
</id>
<property name="name" column="userName"></property>
<one-to-one name="address" class="com.java1234.model.Address" cascade="all"></one-to-one>
</class>
</hibernate-mapping>
2. 按照外键映射
Address2.hbm.xml
<hibernate-mapping package="com.java1234.model">
<class name="Address2" table="t_address2">
<id name="id" column="addressId">
<generator class="native">
</generator>
</id>
<property name="address" column="address"></property>
<property name="zipcode" column="zipcode"></property>
<one-to-one name="user" class="com.java1234.model.User2" property-ref="address"></one-to-one>
</class>
</hibernate-mapping>

User.hbm.xml
<hibernate-mapping package="com.java1234.model">
<class name="User2" table="t_user2">
<id name="id" column="userId">
<generator class="native"></generator>
</id>
<property name="name" column="userName"></property>
<many-to-one name="address" class="com.java1234.model.Address2" column="addressId" cascade="all" unique="true"></many-to-one>
</class>
</hibernate-mapping>

Hibernate 多对多映射关系实现

1,多对多单向实现;
Course.hbm.xml
<hibernate-mapping package="com.java1234.model">
<class name="Course" table="t_course">
<id name="id" column="courseId">
<generator class="native"></generator>
</id>
<property name="name" column="courseName"></property>
</class>
</hibernate-mapping>

Student.hbm.xml
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="studentId">
<generator class="native"></generator>
</id>
<property name="name" column="studentName"></property>
<set name="courses" table="student_course" cascade="save-update">
<key column="student_id"></key>
<many-to-many class="com.java1234.model.Course" column="course_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
2,多对多双向实现;
Course2.hbm.xml
<hibernate-mapping package="com.java1234.model">
<class name="Course2" table="t_course2">
<id name="id" column="courseId">
<generator class="native"></generator>
</id>
<property name="name" column="courseName"></property>
<set name="students" table="student_course2" inverse="true" >
<key column="course_id"></key>
<many-to-many class="com.java1234.model.Student2" column="student_id"></many-to-many>
</set>
</class>
</hibernate-mapping>

Student2.hbm.xml
<hibernate-mapping package="com.java1234.model">
<class name="Student2" table="t_student2">
<id name="id" column="studentId">
<generator class="native"></generator>
</id>
<property name="name" column="studentName"></property>
<set name="courses" table="student_course2" cascade="save-update">
<key column="student_id"></key>
<many-to-many class="com.java1234.model.Course2" column="course_id"></many-to-many>
</set>
</class>
</hibernate-mapping>


Hibernate 检索策略

检索策略属性 Lazy
Lazy:true (默认) 延迟检索 ;set 端 一对多
比如查询一个班级的信息,只有当需要用到的时候才会查询学生信息详情
Lazy:false 立即检索;set 端 一对多
比如查询一个班级的信息,会直接查询学生信息详情
Lazy:extra 增强延迟检索; set 端 一对多
当lazy为true时,比如查询一个班级学生的数量,会把所有学生的相关属性字段都一个个查出来。改用为extra时,会直接优化语句为 select count(*)...........
Lazy:proxy(默认) 延迟检索;many-to-one 多对一
会生成一个类似代理类的东西,无实际数据。仅当接下来会用到的时候才会继续查询用到的东西
Lazy:no-proxy 无代理延迟检索;many-to-one 多对一 (需要编译时字节码增强)

检索策略属性 batch-size
1,批量延迟检索;
比如要查询每个班级对应有多少个学生,不使用batch-size是一条条地语句查。使用后一条语句即可解决,优化性能
2,批量立即检索;
比如要查询每个班级对应有多少个学生,Lazy:false batch-size="X"直接可以查询学生信息详情

检索策略属性 Fetch
1,Fetch:select(默认) 查询方式;
2,Fetch:subselect 子查询方式;
数据量大的时候有的时候可以提高性能
3,Fetch:join 迫切左外连接查询方式;


Hibernate 查询方式简介
1,导航对象图查询方式;
2,OID 查询方式;
3,本地 SQL 查询方式;
@Test //不带具体参数查询
public void testSQLQuery() {
String sql="select * from t_student";
Query query=session.createSQLQuery(sql).addEntity(Student.class);
List studentList=query.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
@Test //带参查询
public void testSQLQuery2() {
String sql="select * from t_student where stuName like :stuName and stuAge=:stuAge";
Query query=session.createSQLQuery(sql).addEntity(Student.class);
query.setString("stuName", "张%");
query.setInteger("stuAge", 10);
List studentList=query.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
4,HQL 查询方式;
HQL(Hibernate Query Language)是面向对象的查询语言;是使用最广的一种查询方式;
1,普通查询
@Test
public void testHQLQuery() {
String hql="from Student";
Query query=session.createQuery(hql);
List<Student> studentList=(List<Student>)query.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
2,带条件查询;
@Test
public void testHQLQuery2() {
String hql="from Student where name like :stuName and age=:stuAge"; //name和age都是Student类中的属性
Query query=session.createQuery(hql);
query.setString("stuName", "张%");
query.setInteger("stuAge", 10);
List<Student> studentList=(List<Student>)query.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
3,使用别名
@Test
public void testHQLQuery3() {
String hql="from Student as s where s.name like :stuName and s.age=:stuAge";
Query query=session.createQuery(hql);
query.setString("stuName", "张%");
query.setInteger("stuAge", 10);
List<Student> studentList=(List<Student>)query.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
4,对结果排序;
@Test
public void testHQLQuery4() {
String hql="from Student order by age desc";
Query query=session.createQuery(hql);
List<Student> studentList=(List<Student>)query.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
5,分页查询;
@Test
public void testHQLQuery5() {
String hql="from Student";
Query query=session.createQuery(hql);
query.setFirstResult(1);
query.setMaxResults(2);
List<Student> studentList=(List<Student>)query.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
6,查询单个对象;
@Test
public void testHQLQuery6() {
String hql="from Student";
Query query=session.createQuery(hql);
query.setFirstResult(1);
query.setMaxResults(1);
Student student=(Student)query.uniqueResult();
System.out.println(student);
}
7,链式写法;
@Test
public void testHQLQuery7() {
String hql="from Student as s where s.name like :stuName and s.age=:stuAge";
Query query=session.createQuery(hql);
List<Student> studentList=(List<Student>)query
.setString("stuName", "张%")
.setInteger("stuAge", 10)
.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
5,QBC 查询方式;(Query By Criteria)
QBC 查询方式(Query By Criteria)是用一套接口来实现的查询方式;
1,普通查询;
@Test //Criteria 接口查询
public void testQBCQuery1(){
Criteria criteria=session.createCriteria(Student.class);
List<Student> studentList=criteria.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
2,带条件查询;
@Test //Restrictions设置条件 返回一个Criterion 对象
public void testQBCQuery2(){
Criteria criteria=session.createCriteria(Student.class);
Criterion c1=Restrictions.like("name", "张%");
Criterion c2=Restrictions.eq("age", 10);
criteria.add(c1);
criteria.add(c2);
List<Student> studentList=criteria.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
3,对结果排序
@Test
public void testQBCQuery3(){
Criteria criteria=session.createCriteria(Student.class);
criteria.addOrder(Order.desc("age")); //通过设置排序方式和排序字段来进行排序
List<Student> studentList=criteria.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
4,分页查询
@Test //和HQL查询方式差不多
public void testQBCQuery4(){
Criteria criteria=session.createCriteria(Student.class);
criteria.setFirstResult(2);
criteria.setMaxResults(2);
List<Student> studentList=criteria.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
5,查询单个对象;
@Test
public void testQBCQuery5(){
Criteria criteria=session.createCriteria(Student.class);
criteria.setFirstResult(2);
criteria.setMaxResults(1);
Student student=(Student)criteria.uniqueResult();
System.out.println(student);
}
6,链式写法;
@Test
public void testQBCQuery6(){
Criteria criteria=session.createCriteria(Student.class);
List<Student> studentList=criteria
.setFirstResult(0)
.setMaxResults(2)
.list();
Iterator it=studentList.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}

Hibernate高级配置
配置数据库连接池
访问数据库,需要不断的创建和释放连接,假如访问量大的话,效率比较低级,服务器消耗大; 使用数据库连接池,我们可以根据实际项目的情况,定义连接池的连接个数,从而可以实现从连接池获取连 接,用户放回到连接池。从而有效的提高的系统的执行效率; Hibernate 自带的连接池不是很好,有 bug; 推荐使用 C3P0,proxool 等;

<!-- 最小连接数 -->
<property name="c3p0.min_size">7</property>
<!-- 最大连接数 -->
<property name="c3p0.max_size">42</property>
<!-- 获得连接的超时时间,如果超过这个时间,会抛出异常,单位毫秒 -->
<property name="c3p0.timeout">1800</property>
<!-- 最大的PreparedStatement的数量 -->
<property name="c3p0.max_statements">50</property>

配置日志框架 Log4J
Log4J 作为一个开源的优秀日志框架,被广泛使用,Hibernate4 必须包中直接支持 Log4J 日志框架;我们只需 要引入 Log4j jar 包,即可使用;
log4j.rootLogger=debug,appender1,appender2 //rootLogger设置级别 //appender1,appender2 为输出目标的地址,地址可以为多个

log4j.appender.appender1=org.apache.log4j.ConsoleAppender //设置输出方式为控制台输出

log4j.appender.appender2=org.apache.log4j.FileAppender //设置输出方式为文件输出 log4j.appender.appender2.File=C:/logFile.txt //设置输出文件保存路径
log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout //设置日志输出的样式
log4j.appender.appender2.layout=org.apache.log4j.TTCCLayout
核心jar包:

配置 Hibernate 二级缓存
1,缓存的概念: 缓存是介于物理数据源与应用程序之间,是对数据库中的数据复制一份临时放在内存或者硬盘中的容 器,其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用程序的运行性能。Hibernate 在进 行读取数据的时候,根据缓存机制在相应的缓存中查询,如果在缓存中找到了需要的数据(我们把这称做“缓 存命 中"),则就直接把命中的数据作为结果加以利用,避免了大量发送 SQL 语句到数据库查询的性能损耗。

2,Hibernate 缓存的分类: 一、Session 缓存(又称作事务缓存):Hibernate 内置的,不能卸除。 缓存范围:缓存只能被当前 Session 对象访问。缓存的生命周期依赖于 Session 的生命周期,当 Session 被关闭 后,缓存也就结束生命周期。 二、SessionFactory 缓存(又称作应用缓存):使用第三方插件,可插拔。 缓存范围:缓存被应用范围内的所有 session 共享,不同的 Session 可以共享。这些 session 有可能是并发访问缓 存,因此必须对缓存进行更新。缓存的生命周期依赖于应用的生命周期,应用结束时,缓存也就结束了生命 周期,二级缓存存在于应用程序范围。

3,二级缓存策略提供商: 提供了 HashTable 缓存,EHCache,OSCache,SwarmCache,jBoss Cathe2,这些缓存机制,其中 EHCache, OSCache 是不能用于集群环境(Cluster Safe)的,而 SwarmCache,jBoss Cathe2 是可以的。HashTable 缓存主 要是用来测试的,只能把对象放在内存中,EHCache,OSCache 可以把对象放在内存(memory)中,也可以 把对象放在硬盘(disk)上(为什么放到硬盘上?上面解释了)。

4,什么数据适合放二级缓存中: (1)经常被访问 (2)改动不大 (3)数量有限 (4)不是很重要的数据,允许出现偶尔并发的数据。 比如组织机构代码,列表信息等;

5,配置 EHCache 二级缓存;
hibernate.cfg.xml添加配置
<!-- 启用二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 配置使用的二级缓存的产品 -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<!-- 配置启用查询缓存 -->
<property name="cache.use_query_cache">true</property>

<ehcache>
<!-- 指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个目录下 -->
<diskStore path="c:\\ehcache"/>
ehcache.xml配置
<!--
设置缓存的默认数据过期策略
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>

<!--
name 设置缓存的名字,他的取值为类的完整名字或者类的集合的名字;
maxElementsInMemory 设置基于内存的缓存可存放的对象的最大数目
eternal 如果为true,表示对象永远不会过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds,默认为false;
timeToIdleSeconds 设定允许对象处于空闲状态的最长时间,以秒为单位;
timeToLiveSeconds 设定对象允许存在于缓存中的最长时间,以秒为单位;
overflowToDisk 如果为true,表示当基于内存的缓存中的对象数目达到maxElementsInMemory界限,会把溢出的对象写到基于硬盘的缓存中;
-->
<!-- 设定具体的第二级缓存的数据过期策略 -->
<cache name="com.java1234.model.Class"
maxElementsInMemory="1"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
</ehcache>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值