2.MyBatis基础

本文详细介绍了MyBatis框架,包括其优点、缺点、主要组成、核心配置文件、映射文件、核心对象和简单框架搭建。MyBatis是一款优秀的持久层框架,支持定制化SQL、存储过程和高级映射。文章还深入讲解了SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession等核心对象以及映射文件中的各种元素,如resultMap、动态SQL等。
摘要由CSDN通过智能技术生成

目录

 

1. 初识MyBatis

1.1 优点和缺点

1.2 MyBatis主要组成

1.3 持久化与ORM

1.3.1 持久化

1.3.2 ORM(Object Relational Mapping)

2 核心配置文件

2.1 属性配置properties

2.2 全局配置settings

2.3 别名配置typeAliases

2.4 运行环境配置environment

3. 映射文件

3.1 mapper元素

3.2 sql元素

3.3 resultMap元素

3.4 Select

3.5 insert、update、delete

3.6 resultMap

3.7 一对一的配置

3.8 一对多的配置

3.9 缓存的配置

3.10 动态sql

3.10.1 If

3.10.2 choose元素(when、otherwise)

3.10.3 trim

3.10.4 where

3.10.5 set

3.10.6 foreach

4. 核心对象

4.1 SqlSessionFactoryBuilder

4.2 SqlSessionFactory

4.3 SqlSession

4.3.1 SqlSession的获取方式

4.3.2 SqlSession的两种使用方式

5. 简单框架搭建

5.1 核心文件配置实例

5.2 实体类和映射文件

5.3 MybatisUtils工具类与测试类

2.3.4 SqlSession的两种使用方式


1. 初识MyBatis

 

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。

官方网站:http://mybatis.org

ORM框架:实体类和SQL语句之间建立映射关系

MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案,适用于性能要求较高或者需求多变的互联网项目

1.1 优点和缺点

优点

  • 简单的持久化框架,小巧并简单易学
  • 灵活, 不会对应用程序或者数据库的现有设计强加任何影响
  • SQL语句封装在配置文件中,便于统一管理与维护,降低程序的耦合度
  • 提供XML标签,支持编写动态SQL
  • 提供映射标签,支持对象与数据库的ORM字段映射
  • 提供对象关系映射标签,支持对象关系组建维护

缺点

  • SQL语句编写工作量大,对开发人员有一定要求
  • 数据库移植性差

1.2 MyBatis主要组成

  • MyBatis的核心对象
    • SqlSessionFactoryBuilder(构造器)
      • SqlSessionFactory(工厂)
      • SqlSession(类似于JDBC的Connection)
      • SqlMapper(映射器)
  • 常用配置文件
    • mybatis-config.xml  系统核心配置文件
    • mapper.xml  SQL映射文件

1.3 持久化与ORM

1.3.1 持久化

持久化是程序数据在瞬时状态和持久状态间转换的过程

1.3.2 ORM(Object Relational Mapping)

编写程序的时候,以面向对象的方式处理数据

保存数据的时候,却以关系型数据库的方式存储

ORM解决方案包含下面四个部分

  • 在持久化对象上执行基本的增、删、改、查操作
  • 对持久化对象提供一种查询语言或者API
  • 对象关系映射工具
  • 提供与事务对象交互、执行检查、延迟加载以及其他优化功能

2 核心配置文件

 

 

2.1 属性配置properties

  • 使用resource属性加载外部properties文件
  • 使用property子元素配置
  • 在SqlSessionFactory执行build方法时增加Properties类型参数
  • 优先级:C(最后加载,覆盖之前加载的) > A > B(最先加载)

 

2.2 全局配置settings

  • 日志logImpl:配置日志类型,常用LOG4J、SLF4J
  • 允许使用别名useColumnLabel:默认true,改为false则无法使用列的别名进行自动映射,必须配置映射关系
  • 获取自增值useGeneratedKeys:默认false,全局配置,设为true后所有映射文件中需要获取自增值时就无需配置该属性了

2.3 别名配置typeAliases

  • 使用typeAlias子元素的type(类型全名)和alias(自定义别名)属性逐一配置别名
  • 使用package子元素的name属性给整个包中的类配置别名(类的简单名)

 

2.4 运行环境配置environment

事务管理器:目前使用jdbc方式

<transactionManager type="JDBC"/>

数据源:可以使用非连接池UNPOOLED、MyBatis的连接池POOLED以及JNDI方式

配置Tomcat的JNDI:

  • 在META-INF下创建context.xml文件,配置数据源连接池参数(注意&符号的写法)
<Context>
    <Resource 
        name="jdbc/mysql"   	
        auth="Container"    	
        type="javax.sql.DataSource"
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://127.0.0.1:3306/d183003?useUnicode=true&amp;characterEncoding=utf8"
        username="root"    	
        password="123456"
        maxIdle="2"    	
        maxWaitMillis="5000"    	
        maxTotal="10"/>
</Context>
  • 在MyBatis核心配置文件中加environment元素(其中initial_context是固定写法,data_source与上步的name对应)
<environment id="tomcat">
		<transactionManager type="JDBC"/>
		<dataSource type="JNDI">
			<property name="initial_context" value="java:comp/env"/>
			<property name="data_source" value="jdbc/mysql"/>
		</dataSource>
</environment>
  • 运行时指定所使用的environment的id(builder.build(is,”tomcat”);)

3. 映射文件

MyBatis 真正的强大在于映射语句,专注于SQL,功能强大,SQL映射的配置却是相当简单

SQL映射文件的几个顶级元素(按照定义的顺序)

mapper – namespace

  • cache – 配置给定命名空间的缓存
  • cache-ref – 从其他命名空间引用缓存配置
  • resultMap –用来描述数据库结果集和对象的对应关系
  • sql – 可以重用的SQL块,也可以被其他语句引用
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

 

 

3.1 mapper元素

namespace:命名空间

namespace和子元素的id联合保证唯一,区别不同的mapper

绑定DAO接口

  • namespace的命名必须跟某个接口同名
  • 接口中的方法与映射文件中SQL语句id一一对应

 

实例

<mapper namespace="cn.smbms.dao.user.UserMapper">

    <select id="getUserList" …>

       ……

    </select>

</mapper>

 

 

3.2 sql元素

简化sql语句中相同的部分

实例

<sql id="selM">select * from movie</sql>
<select id="getMovie" parameterType="int" resultType="Movie" >
      <include refid="selM"/> where mid=#{id}
</select>
<select id="getAll" resultType="Movie" >
      <include refid="selM"/>
</select>

 

 

3.3 resultMap元素

当表的列名与实体类的属性名不一致时,除了通过列的别名(前提是全局配置中允许使用别名useColumnLabel为true),还可以通过该元素进行映射配置

  • 配置resultMap:type属性是原始类型全名或简单名(如果配置过);id属性给select元素使用;id子元素用于映射主键列;result子元素用于映射其它列;column是表中的列名(不区分大小写),property是实体类的属性名(区分大小写)

      实例

<resultMap type="hall" id="hm">
    <id column="hid" property="id"/>
    <result column="cid" property="cinemaId" />
</resultMap>
  • 使用resultMap:在select元素中使用resultMap属性替代resultType属性,其值与上步中的id属性值对应

实例

<select id="getAll" resultMap="hm">
     <include refid="selH"/>
</select>

 

 

3.4 Select

属性

描述

id

在命名空间中唯一的标识符,可以被用来引用这条语句

parameterType

将会传入这条语句的参数类的完全限定名或别名

resultType

从这条语句中返回的期望类型的类的完全限定名或别名。注意集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用resultType或resultMap,但不能同时使用   

resultMap

命名引用外部的resultMap

flushCache

将其设置为true,不论语句什么时候被调用,都会导致缓存被清空。默认值:false

useCache

将其设置为true,将会导致本条语句的结果被缓存。默认值:true

timeout

这个设置驱动程序等待数据库返回请求结果,并抛出异常时间的最大等待值。默认不设置(驱动自行处理)   

fetchSize

这是暗示驱动程序每次批量返回的结果行数

statementType

STATEMENT,PREPARED或CALLABLE的一种。让MyBatis选择使用Statement,PreparedStatement或CallableStatement。默认值:PREPARED

resultSetType

FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE中的一种。默认是不设置(驱动自行处理)   

 

id

命名空间中唯一的标识符

接口中的方法与映射文件中的SQL语句id一一对应

parameterType

基础数据类型

  • int、String、Date等
  • 只能传入一个,通过#{参数名}即可获取传入的值

复杂数据类型

  • Java实体类、Map等
  • 通过#{属性名}或者#{map的keyName}即可获取传入值

resultType/resultMap

resultType :直接表示返回类型

  • 基本数据类型
  • 复杂数据类型

resultMap :对外部resultMap的引用

应用场景:

  • 数据库字段信息与对象属性不一致
  • 复杂的联合查询,自由控制映射结果

二者不能同时存在,本质上都是Map数据结构

 

 

3.5 insert、update、delete

  • id
  • parameterType

注意:insert、update、delete元素均没有resultType属性

 

 

3.6 resultMap

property:表示查询出来的属性对应的值赋给实体对象的哪个属性

column:从数据库中查询的列名或者别名

resultMap自动映射匹配

resultMap自动映射匹配前提:字段名与属性名一致

resultMap的自动映射级别-autoMappingBehavior

  • NONE:禁止自动匹配
  • PARTIAL(默认):自动匹配所有属性,内部嵌套除外
  • FULL:自动匹配所有

实例

<!-- 全局配置文件中配置 -->
<settings>
       <setting  name="autoMappingBehavior" value="NONE"/>
</settings>

resultMap实例

<resultMap type="User" id="userList">
    <result property="id" column="id"/>
    <result property="userCode" column="userCode"/>
    <result property="userName" column="userName"/>
    <result property="userRole" column=“userRole"/>
    <result property="userRoleName" column="roleName"/>
</resultMap>

 

 

3.7 一对一的配置

association

复杂的类型关联,一对一

内部嵌套

映射一个嵌套JavaBean属性

属性

property:映射数据库列的实体对象的属性

javaType:完整Java类名或者别名

resultMap:引用外部resultMap

子元素

id

result

  • property:映射数据库列的实体对象的属性
  • column:数据库列名或者别名

 

3.8 一对多的配置

collection

复杂类型集合,一对多

内部嵌套

映射一个嵌套结果集到一个列表

属性

property:映射数据库列的实体对象的属性

ofType:完整Java类名或者别名(集合所包括的类型)

resultMap:引用外部resultMap

子元素

id

result

  • property:映射数据库列的实体对象的属性
  • column:数据库列名或者别名

3.9 缓存的配置

MyBatis缓存

  • 一级缓存
  • 二级缓存

二级缓存的配置

  • MyBatis的全局cache配置
  • 在Mapper XML文件中设置缓存,默认情况下是没有开启缓存的
  • 在Mapper XML文件配置支持cache后,如果需要对个别查询进行调整,可以单独设置cache

实例

<!-- 全局配置配置 -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
<cache eviction="FIFO" flushInterval="60000"   size="512" readOnly="true"/>
<select id="selectAll" resultType="Emp" useCache="true">

 

3.10 动态sql

基于OGNL表达式

完成多条件查询等逻辑实现

 

 

3.10.1 If

用于条件判定,属性test,使用OGNL语法,无需添加#{...},示例如下(注意使用WHERE 1=1的目的)

实例:

<select id="selCinemaByNameAddress"  parameterType="cinema" resultType="cinemavo">
	select cid id ,name,address from cinema  where 1=1
	<if test="name!=null">
		and name like '%${name}%'
	</if>
	<if test="address!=null">
		and address like '%${address}%'
	</if>
</select>

 

 

3.10.2 choose元素(when、otherwise)

包含子元素when和otherwise

实例:

<select id="getMovieByPrice5" parameterType="map"
  		resultType="Movie" >
  	<include refid="selM"/>
  	<choose>
  	<when test="minPrice!=null and maxPrice==null">
  		where price>=#{minPrice}
  	</when>
  	<when test="maxPrice!=null and minPrice==null">
  		where price&lt;=#{maxPrice}
  	</when>
  	<when test="maxPrice!=null and minPrice!=null and minPrice lt maxPrice">
  		where price between #{minPrice} and #{maxPrice}
  	</when>
  	</choose>
</select>

 

 

3.10.3 trim

可代替where、set,也可以控制其它语句的结构,主要包含prefix(要增加的前缀)、prefixOverrides(要去除的前缀,多个使用|分隔)、suffix(要增加的后缀)、suffixOverrides(要去除的后缀)

 

 

3.10.4 where

自动添加where关键字,且返回语句以and或or开头的会被自动去除

实例

<select id="selCinemaByNameAddress2" 
	parameterType="cinema" resultType="cinemavo">
		select cid id ,name,address from cinema 
		<where>
		<if test="name!=null">
			name like '%${name}%'
		</if>
		<if test="address!=null">
			and address like '%${address}%'
		</if>
		</where>
</select>

3.10.5 set

用于update元素

实例

<update id="modify" parameterType="movie">
  	update movie
  	<set>
  		mid=#{mid},<!-- 增加该语句的目的是防止两个条件都不成立 -->
  		<if test="name!=null">name=#{name},</if>
  		<if test="price gt 0">price=#{price}</if>
  	</set>
  	where mid=#{mid}
</update>

 

3.10.6 foreach

主要包含collection(遍历的类型,list或array)、item(每次遍历到的数据)、open(增加的起始字符)、close(增加的结尾字符)、separator(每次遍历之间的分割字符)等属性

<update id="modifyPrice" parameterType="list">
  	update movie set price=price+1 where mid in
  	<foreach collection="list" item="id" 
  	separator="," open="(" close=")">
  		#{id}
  	</foreach>
</update>

4. 核心对象

4.1 SqlSessionFactoryBuilder

用过即丢,其生命周期只存在于方法体内

可重用其来创建多个 SqlSessionFactory 实例

负责构建SqlSessionFactory,并提供多个build方法的重载

常用构造

配置信息以三种形式提供给SqlSessionFactory的build方法:

InputStream (字节流)、Reader(字符流)、Configuration(类)

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties);
public SqlSessionFactory build(Reader reader, String environment, Properties properties);
public SqlSessionFactory build(Configuration config);

读取XML文件构造方式

String resource = "mybatis-config.xml";   
InputStream is = Resources.getResourceAsStream(resource);   
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

4.2 SqlSessionFactory

SqlSessionFactory是每个MyBatis应用的核心

作用:创建SqlSession实例

作用域:Application

生命周期与应用的生命周期相同

单例:存在于整个应用运行时,并且同时只存在一个对象实例

实例

SqlSession session = sqlSessionFactory.openSession();
SqlSession session = sqlSessionFactory.openSession(boolean autoCommit);

注意:openSession方法可传入参数,表示是否 自动提交事物

4.3 SqlSession

包含了执行SQL所需的所有方法

对应一次数据库会话,会话结束必须关闭

线程级别,不能共享

SqlSession session = sqlSessionFactory.openSession();
try {
      // do work
} finally {
     session.close();
}

注:SqlSession里可以执行多次SQL语句,但一旦关闭了SqlSession就需要重新创建

4.3.1 SqlSession的获取方式

String resource = "mybatis-config.xml";   
InputStream  is = Resources.getResourceAsStream(resource);   
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is) ;
SqlSession sqlSession = factory.openSession();

4.3.2 SqlSession的两种使用方式

  • 通过SqlSession实例直接运行映射的SQL语句
  • 基于Mapper接口方式操作数据

5. 简单框架搭建

注:不是最优化方案,只是一个方便学习的实例。

  1. 下载mybatis-3.2.x.jar包并导入工程
  2. 编写MyBatis核心配置文件
  3. 创建实体类-POJO
  4. DAO层-SQL映射文件(XxxMapper.xml)
  5. 创建测试类

5.1 核心文件配置实例

核心文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 加载外部配置文件(常用),也可以用properties元素的子元素进行配置(不常用) -->
	<properties resource="datasource.properties"></properties>
	<!-- 设置选项,用于设置和改变MyBatis 运行中的行为 -->
	<settings>
		<!-- 日志配置 -->
		<setting name="logImpl" value="LOG4J" />
		<!-- 设置查询数据库超时时间 (默认值:Not Set (null)) -->
		<setting name="defaultStatementTimeout" value="25000" />
		<!-- 指定 MyBatis 是否以及如何自动将列映 射到字段/属性。 PARTIAL(默认): 只是自动映射简单、非嵌套 的结果集。 FULL: 
			将会自动映射任何复杂的(嵌套 或非嵌套)的结果集 -->
		<setting name="autoMappingBehavior" value="FULL" />

		<!-- 除了需求外,以下属性一般无需特意配置 -->
		<!-- 全局性地启用或禁用所有在mapper配置文件中配置的缓存。(默认值:true) -->
		<setting name="cacheEnabled" value="true" />
		<!-- 全局性地启用或禁用延迟加载。当禁用时,所有关联的配置都会立即加载。(默认值:true) -->
		<setting name="lazyLoadingEnabled" value="true" />
		<!-- 允许或禁止从单一的语句返回多个结果集(默认值:true) -->
		<setting name="multipleResultSetsEnabled" value="true" />
		<!-- 使用列的标签而不是列的名称(默认值:true) -->
		<setting name="useColumnLabel" value="true" />
		<!-- 允许JDBC自动生成主键(默认值:false) -->
		<setting name="useGeneratedKeys" value="false" />
		<!-- 是否启用字节码增强机制 -->
		<setting name="enhancementEnabled" value="false" />
		<!-- 配置默认的执行器(executor)。 SIMPLE(默认):简单的执行器。 REUSE :重用prepared statements的 
			执行器。 BATCH:重用 statements并且进行批量 更新的执行器。 -->
		<setting name="defaultExecutorType" value="SIMPLE" />
	</settings>
	<!-- 实体类型别名配置,减少输入多余的完整类名 -->
	<typeAliases>
		<!-- <typeAlias alias="Student" type="cn.kgc.pojo.Student"/> 别名匹配,比较繁琐,不推荐使用 -->
		<!-- 包名匹配 -->
		<package name="cn.cs.pojo" />
	</typeAliases>

	<!-- 不常用配置,本实例没有配置 -->
	<!--typeHandlers:重写类型处理器(type handlers)后,在此配置映射 -->
	<!-- objectFactory:重写 ObjectFactory后,在此配置映射 -->

	<!-- 插件配置:需要时配置 -->
	<plugins>
		<!-- 分页插件配置 -->
		<plugin interceptor="com.github.pagehelper.PageInterceptor">
			<property name="helperDialect" value="mysql" />
		</plugin>
	</plugins>
	<!-- 运行环境配置。可以配置多个运行环境,但是每个SqlSessionFactory 实例只能选择一个运行环境。 -->
	<environments default="development">
		<environment id="development">
			<!-- 事物管理器 -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 数据源 -->
			<dataSource type="POOLED">
				<!-- 简单数据源配置,详细配置请自行百度 -->
				<property name="driver" value="${driver}" />
				<property name="url" value="${url}" />
				<property name="username" value="${username}" />
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
	</environments>
	<!-- SQL映射类配置,减少输入多余的完整类名 -->
	<mappers>
		<!-- <mapper resource="cn/kgc/dao/StudentMapper.xml"/> 全名称匹配,不推荐使用 -->
		<!-- 包名匹配 -->
		<package name="cn.cs.dao" />
	</mappers>
</configuration>

database.properties数据库配置

driverClassName=com.mysql.jdbc.Driver
#在和mysql传递数据的过程中,使用unicode编码格式,并且字符集设置为utf-8
url=jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=utf-8
username=root
password=root

补充:

属性能够被可动态替换(即使用占位符)的属性值引用 如:${driver},引用properties配置文件键后的值

5.2 实体类和映射文件

sql映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<mapper namespace="cn.kgc.dao.StudentMapper">
    <select id="getStudents" resultType="Student">
        select * from student
    </select>
    <update id="updateStu" parameterType="Student">
        update student set sName=#{sName} where id = #{id}
    </update>
    <delete id="addStu" parameterType="Student">
        insert into student(sName,age,addres) values(#{sName},#{age},#{addres})
    </delete>
    <insert id="delstu">
        delete from student where id = #{id}
    </insert>
</mapper>

实体类

public class Student {
	private int id;
	private String sName;
	private int age;
	private String addres;
	public Student() {
		super();
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getsName() {
		return sName;
	}
	public void setsName(String sName) {
		this.sName = sName;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getAddres() {
		return addres;
	}
	public void setAddres(String addres) {
		this.addres = addres;
	}
	@Override
	public String toString() {
		return "Strudent [id=" + id + ", sName=" + sName + ", age=" + age + ", addres=" + addres + "]";
	}
	
}

  1. 实体类POJO,按照数据库中对应表的结构编写
  2. 映射文件按照给定模板编写
  3. 根元素mapper,指定namespace
  4. 当属性名与列名不一致时,定义resultMap进行映射
  5. 定义CRUD标签(select、insert、delete、update),编写SQL语句
  6. 每个标签指定id用于查找;parameterType属性指定参数类型;resultMap指定返回值类型

5.3 MybatisUtils工具类与测试类

MybatisUtils工具类

public class MybatisUtils {
	private static SqlSessionFactory factory;
	// 在static里,factory只会被建立一次
	static {
		try {
			// 获取config.xml的输入流
			InputStream is = Resources.getResourceAsStream("config.xml");
			//工厂建造器build()方法创建sqlSession 工厂
			factory = new SqlSessionFactoryBuilder().build(is);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	// 建立sqlsession
	public static SqlSession createSqlSession() {
		return factory.openSession(true); // true: 自动提交事物
	}

	// 释放资源
	public static void close(SqlSession sqlSession) {
		if (null != sqlSession) {
			sqlSession.close();
		}
	}
}

2.3.4 SqlSession的两种使用方式

StudentMapper接口

public interface StudentMapper {
	  //1.查询学生表的记录数
	  public int count();
}

 

通过SqlSession实例直接运行映射的SQL语句

@Test
public void test1() throws IOException {
    SqlSession se=null;
    se=MybatisUtils.createSqlSession();
    //通过SqlSession实例直接运行映射的SQL语句
    int count=se.selectOne("cn.kgc.dao.StudentMapper.count");
    System.out.println(count);
    MybatisUtils.close(se);
}

基于Mapper接口方式操作数据

@Test
public void test2() throws IOException {
    SqlSession se=MybatisUtils.createSqlSession();
    //基于Mapper接口方式操作数据
    int count= se.getMapper(StudentMapper.class).count();
    System.out.println(count);
    MybatisUtils.close(se);
}

注意:方式2要求Mapper接口的名称必须和映射文件中mapper元素的namespace属性完全一致,方式1则无此要求;另外Mapper接口中的方法名需和映射文件中相应元素的id属性对应

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值