mybatis的核心配置

MyBatis的核心对象

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder的多个重载build()方法 :

01

  • build()方法第一种形式中,参数inputStream是字节流,它封装了XML文件形式的配置信息;参数environment和参数properties为可选参数。其中,参数environment决定将要加载的环境,包括数据源和事务管理器;参数properties决定将要加载的properties文件。
  • build()方法可知,第二种形式的build()方法参数作用与第一种形式大体一致,唯一不同的是,第一种形式的build()方法使用InputStream字节流封装了XML文件形式的配置信息,而第二种形式的build()方法使用Reader字符流封装了xml文件形式的配置信息。
  • build()方法第三种形式中,配置信息可以通过InputStream(字节流)、Reader(字符流)、Configuration(类)三种形式提供给SqlSessionFactoryBuilder的build()方法。

通过过读取XML配置文件的方式构造SqlSessionFactory对象的关键代码如下所示。:

// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = 
new SqlSessionFactoryBuilder().build(inputStream);

SqlSessionFactory对象是线程安全的,它一旦被创建,在整个应用程序执行期间都会存在。如果我们多次创建同一个数据库的SqlSessionFactory对象,那么该数据库的资源将很容易被耗尽。通常每一个数据库都只创建一个SqlSessionFactory对象,所以在构建SqlSessionFactory对象时,建议使用单例模式。

SqlSessionFactory

SqlSessionFactory的openSession()方法

方法名称描述
SqlSession openSession()开启一个事务。
SqlSession openSession(Boolean autoCommit)参数autoCommit可设置是否开启事务。
SqlSession openSession(Connection connection)参数connection可提供自定义连接。
SqlSession openSession(TransactionIsolationLevel level)参数level可设置隔离级别。
SqlSession openSession(ExecutorType execType)参数execType有三个可选值。
SqlSession openSession(ExecutorType execType, Boolean autoCommit)参数execType有三个可选值。
SqlSession openSession(ExecutorType execType, Connection connection)参数ExecutorType有三个可选值。

参数execType有三个可选值:

l ExecutorType.SIMPLE:表示为每条语句创建一条新的预处理语句。

l ExecutorType.REUSE:表示会复用预处理语句。

l ExecutorType.BATCH:表示会批量执行所有更新语句。

l 参数autoCommit可设置是否开启事务。

l 参数autoCommit可设置是否开启事务。

SqlSession

SqlSession是MyBatis框架中另一个重要的对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,主要作用是执行持久化操作,类似于JDBC中的Connection。SqlSession对象包含了执行SQL操作的方法,由于其底层封装了JDBC连接,所以可以直接使用SqlSession对象来执行已映射的SQL语句。

方法名称描述
T selectOne(String statement)查询方法。参数statement是在配置文件中定义的元素的id。
T selectOne(String statement, Object parameter)查询方法。parameter是查询语句所需的参数。
List selectList(String statement)查询方法。参数statement是在配置文件中定义的元素的id。
List selectList(String statement, Object parameter)查询方法。parameter是查询语句所需的参数。
List selectList(String statement, Object parameter, RowBounds rowBounds)查询方法。rowBounds是用于分页的参数对象。
void select(String statement, Object parameter, ResultHandler handler)查询方法。,handler对象用于处理查询语句返回的复杂结果集。
方法名称描述
int insert(String statement)插入方法。参数statement是在配置文件中定义的元素的id。
int insert(String statement, Object parameter)插入方法。parameter是插入语句所需的参数。
int update(String statement)更新方法。参数statement是在配置文件中定义的元素的id。
int update(String statement, Object parameter)更新方法。parameter是更新语句所需的参数。
int delete(String statement)删除方法。参数statement是在配置文件中定义的元素的id。
int delete(String statement, Object parameter)删除方法。parameter是删除语句所需的参数。
方法名称描述
int insert(String statement)插入方法。参数statement是在配置文件中定义的元素的id。
int insert(String statement, Object parameter)插入方法。parameter是插入语句所需的参数。
int update(String statement)更新方法。参数statement是在配置文件中定义的元素的id。
int update(String statement, Object parameter)更新方法。parameter是更新语句所需的参数。
int delete(String statement)删除方法。参数statement是在配置文件中定义的元素的id。
int delete(String statement, Object parameter)删除方法。parameter是删除语句所需的参数。

SqlSession对象的使用范围

每一个线程都应该有一个自己的SqlSession对象,并且该对象不能共享。SqlSession对象是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在类的静态字段、对象字段或任何类型的管理范围(如Servlet的HttpSession)中使用。SqlSession对象使用完之后,要及时的关闭,SqlSession对象通常放在finally块中关闭,代码如下所示:

SqlSession sqlSession = sqlSessionFactory.openSession();
try {	
	// 此处执行持久化操作
} finally {		sqlSession.close();		}

MyBatis核心配置文件

MyBatis核心配置文件中的主要元素

02

的子元素的执行顺序

元素是整个XML配置文件的根元素,相当于MyBatis各元素的管理员。有很多子元素,MyBatis的核心配置就是通过这些子元素完成的。需要注意的是,在核心配置文件中,的子元素必须按照上图由上到下的顺序进行配置,否则MyBatis在解析XML配置文件的时候会报错。

properties元素

是一个配置属性的元素,该元素的作用是读取外部文件的配置信息。

假设现在有一个配置文件 db.properties,该文件配置了数据库的连接信息,具体如下:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

如果想获取数据库的连接信息,可以在 MyBatis 的核心配置文件 mybatis-config.xml 中使用元素先引入 db.properties 文件,具体代码如下:

<properties resource="db.properties" />

引入 db.properties 文件后,如果希望动态获取 db.properties 文件中的数据库连接信息,可以使用元素配置,示例代码如下:

<dataSource type="POOLED">
    <!-- 数据库驱动 -->
    <property name="driver" value="${jdbc.driver}" />
    <!-- 连接数据库的url -->
    <property name="url" value="${jdbc.url}" />
    <!-- 连接数据库的用户名 -->
    <property name="username" value="${jdbc.username}" />
    <!-- 连接数据库的密码 -->
    <property name="password" value="${jdbc.password}" />
</dataSource>

完成上述配置后,元素中连接数据库的 4 个属性(driver、url、username 和 password)值将会由db.properties 文件中对应的值来动态替换。这样一来,元素就可以通过 db.properties 文件实现动态参数配置。

settings元素

元素中的常见配置参数

配置参数描述
cacheEnabled用于配置是否开启缓存。
lazyLoadingEnabled延迟加载的全局开关。
aggressiveLazyLoading关联对象属性的延迟加载开关。
multipleResultSetsEnabled是否允许单一语句返回多结果集(需要兼容驱动)。
useColumnLabel使用列标签代替列名。
useGeneratedKeys允许JDBC支持自动生成主键,需要驱动兼容。
配置参数描述
autoMappingBehavior指定MyBatis应如何自动映射列到字段或属性。
defaultExecutorType配置默认的执行器。
defaultStatementTimeout配置超时时间,它决定驱动等待数据库响应的秒数。
mapUnderscoreToCamelCase是否开启自动驼峰命名规则(camel case)映射。
jdbcTypeForNull当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。

元素中常见配置参数的使用方式

<settings>
    <!-- 是否开启缓存 -->
    <setting name="cacheEnabled" value="true" />
    <!-- 是否开启延迟加载,如果开启,所有关联对象都会延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true" />
    <!-- 是否开启关联对象属性的延迟加载,如果开启,对任意延迟属性的调用都
    会使用带有延迟加载属性的对象向完整加载,否则每种属性都按需加载 -->
    <setting name="aggressiveLazyLoading" value="true" />
    ...
    <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

typeAliases元素

使用元素设置别名

核心配置文件若要引用一个POJO实体类,需要输入POJO实体类的全限定类名,而全限定类名比较冗长,如果直接输入,很容易拼写错误。例如,POJO实体类User的全限定类名是com.wei.pojo.User,未设置别名之前,映射文件的select语句块若要引用POJO类User,必须使用其全限定类名,引用代码如下。

<select id="findById" parameterType="int" resultType="com.wei.pojo.User">
       select * from users where uid = #{id}
</select>

多个全限定类设置别名的方式

方式一:在元素下,使用多个元素为每一个全限定类逐个配置别名。

<typeAliases>
    <typeAlias alias=“Usertype="com.wei.pojo.User"/>
    <typeAlias alias="Student" type="com.wei.pojo.Student"/>
    <typeAlias alias="Employee" 	type="com.wei.pojo.Employee"/>
    <typeAlias alias="Animal" type="com.wei.pojo.Animal"/>
</typeAliases>

方式二:通过自动扫描包的形式自定义别名。

<typeAliases>
    <package name="com.wei.pojo"/>
</typeAliases>

常见Java类型的默认别名问题

除了可以使用<typeAliases>元素为实体类自定义别名外,MyBatis框架还为许多常见的Java类型(如数值、字符串、日期和集合等)提供了相应的默认别名。例如别名_byte映射类型byte、_long映射类型long等,别名可以在MyBatis中直接使用,但由于别名不区分大小写,所以在使用时要注意重复定义的覆盖问题。

environments元素

使用元素定义运行环境

元素配置运行环境

​ MyBatis可以配置多套运行环境,如开发环境、测试环境、生产环境等,我们可以灵活选择不同的配置,从而将SQL映射到不同运行环境的数据库中。不同的运行环境可以通过元素来配置,但不管增加几套运行环境,都必须要明确选择出当前要用的唯一的一个运行环境。

各元素配置运行环境

​ MyBatis的运行环境信息包括事务管理器和数据源。在MyBatis的核心配置文件中,MyBatis通过元素定义一个运行环境。元素有两个子元素,元素和元素。元素用于配置运行环境的事务管理器;元素用于配置运行环境的数据源信息。

使用元素进行配置的示例代码

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC" /><!—设置使用JDBC事务管理 -->
        <dataSource type="POOLED"> <!-配置数据源 -->
            <property name="driver" value="${jdbc.driver}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </dataSource>
    </environment>	...
</environments>

元素配置事务管理器

​ 在MyBatis中,元素可以配置两种类型的事务管理器,分别是JDBC和MANAGED。

l JDBC:此配置直接使用JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。

l MANAGED:此配置不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下,它会关闭连接,但可以将元素的closeConnection属性设置为false来阻止它默认的关闭行为。

MyBatis数据源类型

​ 项目中使用Spring+MyBatis,则没必要在MyBatis中配置事务管理器,实际开发中,项目会使用Spring自带的管理器来实现事务管理。对于数据源的配置,MyBatis提供了UNPOOLED、POOLED和JNDI三种数据源类型。

UNPOOLED数据源

​ UNPOOLED表示数据源为无连接池类型。配置此数据源类型后,程序在每次被请求时会打开和关闭数据库连接。UNPOOLED适用于对性能要求不高的简单应用程。UNPOOLED类型的数据源需要配置5种属性。

UNPOOLED数据源需要配置的属性

属性说明
driverJDBC驱动的Java类的完全限定名
url数据库的URL地址
username登录数据库的用户名
password登录数据库的密码
defaultTransactionIsolationLevel默认的连接事务隔离级别

POOLED数据源

​ POOLED表示数据源为连接池类型。POOLED数据源利用“池”的概念将JDBC连接对象组织起来,节省了在创建新的连接对象时需要初始化和认证的时间。POOLED数据源使得并发Web应用可以快速的响应请求,是当前比较流行的数据源配置类型。

POOLED数据源可额外配置的属性

属性说明
poolMaximumActiveConnections在任意时间可以存在的活动连接数量,默认值:10。
poolMaximumIdleConnections任意时间可能存在的空闲连接数。
poolMaximumCheckoutTime在被强制返回之前,池中连接被检出时间,默认值:20000 毫秒。
poolTimeToWait如果获取连接花费的时间较长,它会给连接池打印状态日志并重新尝试获取一个连接,默认值:20000毫秒。
poolPingQuery发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中。默认是“NO PING QUERY SET”。
poolPingEnabled是否启用侦测查询,默认值:false。
poolPingConnectionsNotUsedFor配置poolPingQuery的使用频度。

JNDI数据源

JNDI 表示数据源可以在 EJB 或应用服务器等容器中使用。JNDI数据源需要配置的属性如下所示

属性说明
initial_context该属性主要用于在InitialContext中寻找上下文。该属性为可选属性,在忽略时,data_source属性会直接从InitialContext中寻找。
data_source该属性表示引用数据源对象位置的上下文路径。如果提供了initial_context配置,那么程序会在其返回的上下文中进行查找;如果没有提供,则直接在InitialContext中查找。

mappers元素

使用元素引入 MyBatis 映射文件

元素作用

在MyBatis的核心配置文件中,元素用于引入MyBatis映射文件。映射文件包含了POJO对象和数据表之间的映射信息,MyBatis通过核心配置文件中的元素找到映射文件并解析其中的映射信息。通过元素引入映射文件的方法有4种。

a.使用类路径引入

<mappers>
    <mapper resource="com/wei/mapper/UserMapper.xml"/>
</mappers>

b.使用本地文件路径引入

<mappers>
    <mapper url="file:///D:/com/wei/mapper/UserMapper.xml"/>
</mappers>

c.使用接口类引入

<mappers>
    <mapper class="com.wei.mapper.UserMapper"/>
</mappers>

d.使用包名引入

<mappers>
    <package name="com.wei.mapper"/>
</mappers>

MyBatis映射文件

MyBatis映射文件中的常用元素

属性说明
mapper映射文件的根元素,该元素只有一个namespace属性(命名空间)。
cache配置给定命名空间的缓存。
cache-ref从其他命名空间引用缓存配置。
resultMap描述数据库结果集和对象的对应关系。
sql可以重用的SQL块,也可以被其他语句使用。
insert用于映射插入语句。
delete用于映射删除语句。
update用于映射更新语句。
select用于映射查询语句。

元素中namespace属性作用

​ namespace属性有两个作用:

l 用于区分不同的mapper,全局唯一。

l 绑定DAO接口,即面向接口编程。当namespace绑定某一接口之后,可以不用写该接口的实现类,MyBatis会通过接口的全限定名查找到对应的mapper配置来执行SQL语句,因此namespace的命名必须跟接口同名。

元素如何区别不同的XML文件

​ 在不同的映射文件中,元素的子元素的id可以相同,MyBatis通过元素的namespace属性值和子元素的id联合区分不同的Mapper.xml文件。接口中的方法与映射文件中SQL语句id应一一对应。

select元素

#<select>元素用来映射查询语句,它可以从数据库中查询数据并返回。使用<select>元素执行查询操作非常简单,示例代码如下: 
<!—查询操作 -->
<select id="findUserById" parameterType="Integer"
    resultType="com.wei.pojo.User">
    select * from users where id = #{id}
</select>

常用属性:

属性说明
id表示命名空间中元素的唯一标识,通过该标识可以调用这条查询语句。
parameterType它是一个可选属性,用于指定SQL语句所需参数类的全限定名或者别名,其默认值是unset。
resultType用于指定执行这条SQL语句返回的全限定类名或别名。
resultMap表示外部resultMap的命名引用。resultMap和resultType不能同时使用。
flushCache用于指定是否需要MyBatis清空本地缓存和二级缓存。
属性说明
useCache用于控制二级缓存的开启和关闭。
timeout用于设置超时时间,单位为秒。
fetchSize获取记录的总条数设定,默认值是unset。
statementType用于设置MyBatis预处理类。
resultSetType表示结果集的类型,它的默认值是unset。

insert元素

#<insert>元素用于映射插入语句,在执行完<insert>元素中定义的SQL语句后,会返回插入记录的数量。使用< insert >元素执行插入操作非常简单,示例代码如下:
<!—插入操作 --><insert id="addUser" parameterType="com.wei.pojo.User">
    insert into users(uid,uname,uage)values(#{uid},#{uname},#{uage})
</insert>

数据库获取主键值的方式 :很多时候,执行插入操作后,需要获取插入成功的数据生成的主键值,不同类型数据库获取主键值的方式不同,下面分别对支持主键自动增长的数据库获取主键值和不支持主键自动增长的数据库获取主键值的方式进行介绍。

  • a.使用支持主键自动增长的数据库获取主键值

    #如果使用的数据库支持主键自动增长(如MySQL和SQL Server),那么可以通过keyProperty属性指定POJO类的某个属性接收主键返回值(通常会设置到id属性上),然后将useGeneratedKeys的属性值设置为true。
    <insert id="addUser" parameterType="com.wei.pojo.User"
    	keyProperty="uid" useGeneratedKeys="true" >
       	insert into 	users(uid,uname,uage)values(#{uid},#{uname},#{uage})
    </insert>
    
  • b.使用不支持主键自动增长的数据库获取主键值

    #使用MyBatis提供的<selectKey>元素来自定义主键。
    <selectKey
        keyProperty="id”	resultType="Integer"
        order="BEFORE”	statementType="PREPARED">
    #在上述<selectKey>元素的属性中,order属性可以被设置为BEFORE或AFTER。如果设置为BEFORE,那么它会首先执行<selectKey>元素中的配置来设置主键,然后执行插入语句;如果设置为AFTER,那么它先执行插入语句,然后执行<selectKey>元素中的配置内容。
    

update元素

#<update>元素用于映射更新语句,它可以更新数据库中的数据。在执行完元素中定义的SQL语句后,会返回更新的记录数量。使用<update>元素执行更新操作非常简单,示例代码如下:
<!—更新操作 -->
<update id="updateUser"    parameterType="com.wei.pojo.User">
    update users set uname= #{uname},uage = #{uage} where uid = #{uid}	
</update>

delete元素

元素用于映射删除语句,在执行完元素中的SQL语句之后,会返回删除的记录数量。使用元素执行删除操作非常简单,示例代码如下所示:

<!-- 删除操作 -->
<delete id="deleteUser" parameterType="Integer">
    delete from users where uid=#{uid}
</delete>

元素中,除了上述示例代码中的几个属性外,还有其他一些可以配置的属性,如flushCache、timeout等。

属性说明
id表示命名空间中元素的唯一标识,通过该标识可以调用这条语句。
parameterType它是一个可选属性,用于指定SQL语句所需参数类的全限定名或者别名,其默认值是unset。
flushCache用于指定是否需要MyBatis清空本地缓存和二级缓存。
timeout用于设置超时时间,单位为秒。
statementType用于设置MyBatis预处理类。

sql元素

作用:

在一个映射文件中,通常需要定义多条SQL语句,这些SQL语句的组成可能有一部分是相同的(如多条select语句中都查询相同的id、username字段),如果每一个SQL语句都重写一遍相同的部分,势必会增加代码量。针对此问题,可以在映射文件中使用MyBatis所提供的元素,将这些SQL语句中相同的组成部分抽取出来,然后在需要的地方引用。

元素的作用是定义可重用的SQL代码片段,它可以被包含在其他语句中。元素可以被静态地(在加载参数时)参数化,元素不同的属性值通过包含的对象发生变化。

#实现一个根据客户id查询客户信息的SQL片段 
<!--定义要查询的表 -->
<sql id=“someinclude">from <include refid="${include_target}" /></sql>
<!--定义查询列 --><sql id=“userColumns">	uid,uname,uage 	</sql>
<!--根据客户id查询客户信息 -->
<select id="findUserById" parameterType="Integer" 
         resultType="com.wei.pojo.User">	select 
    <include refid="userColumns"/>
    <include refid="someinclude">
        <property name="include_target" value="users" /></include>
    where uid = #{uid}
</select> 

resultMap元素

作用:

元素表示结果映射集,是MyBatis中最重要也是功能最强大的元素。元素主要作用是定义映射规则、更新级联以及定义类型转化器等。

数据表中的列和需要返回的对象的属性可能不会完全一致,这种情况下MyBatis不会自动赋值,这时就需要使用元素进行结果集映射。

示例:

  1. 在名称为mybatis的数据库中,创建一个t_student表,并插入几条测试数据。

    USE mybatis;

    CREATE TABLE t_student(

    sid INT PRIMARY KEY AUTO_INCREMENT,

    sname VARCHAR(50),

    sage INT

    );

    INSERT INTO t_student(sname,sage) VALUES('Lucy',25);

    INSERT INTO t_student(sname,sage) VALUES('Lili',20);

    INSERT INTO t_student(sname,sage) VALUES('Jim',20);

  2. 创建实体类Student,用于封装学生信息。在类中定义id、name和age属性,以及属性的getter/setter方法和toString()方法。

    package com.wei.pojo;

    public class Student {

    private Integer id; // 主键id

    private String name; // 学生姓名

    private Integer age; // 学生年龄

    // 省略getter/setter方法

    @Override

    public String toString() {

    return "User [id=" + id + ", name=" + name + ", age=" + age + "]";}

    }

  3. 创建映射文件StudentMapper.xml,在映射文件中编写映射查询语句。

    <!-- 只显示mapper元素的内容-->

    <mapper namespace="com.wei.mapper.StudentMapper">

    <resultMap type="com.wei.pojo.Student" id="studentMap">

    <id property="id" column="sid"/><result property="name" column="sname"/><result property="age" column="sage"/>

    </resultMap>

    <select id="findAllStudent" resultMap="studentMap">

    select * from t_student</select>

    </mapper>

  4. 在核心配置文件mybatis-config.xml中,引入StudentMapper.xml,将StudentMapper.xml映射文件加载到程序中。在mybatis-config.xml中的元素下添加如下代码。

    <mapper resource="com/wei/mapper/StudentMapper.xml">

    <mapper>

  5. 创建测试类MyBatisTest,在测试类中,编写测试方法findAllStudentTest(),用于测试元素实现查询结果的映射。

    public class MyBatisTest {

    private SqlSessionFactory sqlSessionFactory;

    private SqlSession sqlSession;

    // init()方法省略

    @Test

    public void findAllStudentTest() {

    List<Student> list = sqlSession.selectList("com.wei.mapper.StudentMapper.

    findAllStudent");

    for (Student student : list) { System.out.println(student);}}

    // destory()方法省略

    }

  6. 运行MyBatisTest测试类,控制台会输出结果。

    03

    需要注意的是,在测试类MyBatisTest中,每一个用@Test注解标注的方法称为测试方法,

    他们的调用顺序为@Before→@Test→@After。

多学一招:使用工具类创建SqlSession对象

在上述案例中,由于每个方法执行时都需要读取配置文件,并根据配置文件的信息构建SqlSessionFactory对象、创建SqlSession对象、释放资源,这导致了大量的重复代码。为了简化开发,我们可以将读取配置文件和释放资源的代码封装到一个工具类中,然后通过工具类创建SqlSession对象。
t list = sqlSession.selectList("com.wei.mapper.StudentMapper.`

`findAllStudent");`

for (Student student : list) { System.out.println(student);}}

 `// destory()方法省略`

}

  1. 运行MyBatisTest测试类,控制台会输出结果。

    [外链图片转存中…(img-u2yLCzTX-1676866693551)]

    需要注意的是,在测试类MyBatisTest中,每一个用@Test注解标注的方法称为测试方法,

    他们的调用顺序为@Before→@Test→@After。

多学一招:使用工具类创建SqlSession对象

在上述案例中,由于每个方法执行时都需要读取配置文件,并根据配置文件的信息构建SqlSessionFactory对象、创建SqlSession对象、释放资源,这导致了大量的重复代码。为了简化开发,我们可以将读取配置文件和释放资源的代码封装到一个工具类中,然后通过工具类创建SqlSession对象。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值