MyBatis基础-ORM、全局配置、mapper配置、使用

本文介绍了MyBatis的基础概念,包括ORM、MyBatis框架架构和与JDBC、Hibernate的区别。详细阐述了MyBatis的安装、核心配置文件和Mapper配置的步骤,以及SQL执行的过程。通过理解MyBatis的功能和使用,可以更好地掌握数据库操作的灵活性和便捷性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 

基础概念

ORM

MyBatis

MyBatis功能架构

MyBatis框架架构

MyBatis 与 JDBC、Hibernate 的区别

基本术语

基本使用

安装

基础核心配置文件

核心映射文件Mapper

过程概述

1.在pom.xml中导入依赖并设置

2.配置主文件,并在开发过程中不断添加typeAlias、mapper等

 

3.写映射,即接口与xml映射文件

4.使用映射完成sql语句的执行


基础概念

ORM

ORM(Object Relational Mapping),即对象关系映射,它完成面向对象的编程语言到关系数据库的映射。ORM 工具的唯一作用是:把持久化对象的保存、修改、删除等操作,转换成对数据库的操作。

ORM 基本映射关系:

  • 数据表映射类
  • 数据表的行映射对象(实例)
  • 数据表的列(字段)映射对象的属性

MyBatis

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及对结果集的检索封装。MyBatis 可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。

MyBatis 的主要思想是将程序中的大量 SQL 语句抽取出来,配置在配置文件中,以实现 SQL 的灵活配置。

MyBatis 并不完全是一种 ORM 框架,它的设计思想和 ORM 相似,只是它允许直接编写 SQL 语句,使得数据库访问更加灵活。因此,准确地说,MyBatis 提供了一种“半自动化”的 ORM 实现,是一种 "SQL Mapping" 框架。

MyBatis功能架构

分为三层

API接口层:提供给外部使用的接口 API,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

数据处理层:负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。

基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件,为上层的数据处理层提供最基础的支撑。

MyBatis框架架构

加载配置:MyBatis 应用程序根据XML配置文件加载运行环境,创建 SqlSessionFactory,SqlSessionFactory可来源于xml配置文件或者注解,将 SQL 的配置信息加载成为一个个 MappedStatement 对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。

SQL 解析:当 API 接口层接收到调用请求时,会接收到传入 SQL 的 ID 和传入对象(可以是 Map、JavaBean 或者基本数据类型),Mybatis 会根据 SQL 的 ID 找到对应的 MappedStatement,然后根据传入参数对象对 MappedStatement 进行解析,解析后可以得到最终要执行的 SQL 语句和参数。

SQL 执行:SqlSession 将最终得到的 SQL 和参数拿到数据库进行执行,得到操作数据库的结果。

结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成 HashMap、JavaBean 或者基本数据类型,并将最终结果返回,用完之后关闭 SqlSession。

更具体的:

1.SqlSessionFactory

sql会话工厂,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 是单个数据库映射关系经过编译后的内存映像(类似于JDBC中加载完驱动,创建连接)。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。SqlSessionFactory 是创建 SqlSession 的工厂。

2.SqlSession

SqlSession 是执行持久化操作的对象,它完全包含了面向数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例来直接执行已映射的 SQL 语句(类似于JDBC中通过连接获取Statement/PreparedStatement,然后执行SQL)。在使用完 SqlSession 后我们应该使用 finally 块来确保关闭它。

MyBatis 与 JDBC、Hibernate 的区别

        Mybatis也是基于JDBC的。Java与数据库操作仅能通过JDBC完成。 Mybatis也要通过JDBC完成数据查询、更新这些动作。Mybatis仅仅是在JDBC基础上做了,OO化、封装事务管理接口这些东西。 Mybatis 和 Hibernate 都屏蔽JDBC API 的底层方问细节,使我们不用于JDBC API打交道就可以访问数据库。但是,Hibernate是全自动的ORM映射工具,可以自动生成SQL语句,Mybatis需要在xml配置文件中写SQL语句;因为Hibernate是自动生成SQL语句的,在写复杂查询时,Hibernate实现比Mybatis复杂的多。

基本术语

表示层(视图层)、业务层(业务逻辑层)、持久层(数据访问层),注:严格来说持久层包括数据访问层

表示层负责接收用户请求、转发请求、显示数据等;业务层负责组织业务逻辑;持久层负责持久化业务对象。

这三个分层,每一层都有不同的模式,就是架构模式。表示层最常用的架构模式就是MVC

DAL:data access layout数据访问层

DAO:data access object 数据访问对象

基本使用

安装

要使用 MyBatis, 只需在项目中将 mybatis-x.x.x.jar 文件导入即可。

如果使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

(框架如何使用,可直接查看中文官方文档:|------传送门------|)

基础核心配置文件

XML 配置文件中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。一般放在src/main/resources下。

先看一个实例:

<?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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <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>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

MyBatis 配置文件的 configuration 标签主要包括:

  • configuration 配置
    • properties 属性
    • settings 设置
    • typeAliases 类型命名
    • typeHandlers 类型处理器
    • objectFactory 对象工厂
    • plugins 插件
    • environments 环境
      • environment 环境变量
      • transactionManager 事务管理器
    • databaseIdProvider 数据库厂商标识
    • mappers 映射器

 properties 属性

这个标签就是用来进行声明name/value的,相当于头部设置变量,下面的配置可直接拿来用,避免冗余。

使用下级标签property声明key/value,如:

<properties>
      <property name="username" value="root"/>
      <property name="password" value="123"/>
</properties>

也可以使用resource或url进行外部配置且动态替换,例如建立 Java 属性文件 config.properties ,用于配置一些数据库的信息,内容如下:

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

在配置文件中配置 <properties.../> 属性中就可直接引入

<properties resource="config.properties"/>

然后在其他标签里就可以通过 properties 元素的子元素来传递值,如在这里设置 username 和 password 的值:

<dataSource type="POOLED">
     <!--直接拿properties中的值过来用即可-->
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
</dataSource>

如果重名,则优先级是这样的:通过方法参数传递的属性具有最高优先级(即其他标签内部自定义的property优先级最高),resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。

 settings 设置

该标签内部设置MyBatis运行时的一些操作设置(如自动生成主键,设置超时时间等),如果不指定设置就使用默认值。

详情参看官方文档:http://www.mybatis.org/mybatis-3/zh/configuration.html#settings

typeAliases 类型别名 

类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:

@Alias("author")
public class Author {
    ...
}

这是一些为常见的 Java 类型内建的相应的类型别名。它们都是不区分大小写的,注意对基本类型名称重复采取的特殊命名风格。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

environments 环境

MyBatis 的环境配置实际是数据源的配置。MyBatis 可以配置多个环境,帮助你将 SQL 映射对应到多种数据库。environments下的environment环境就相当于一个数据库连接配置,所以每个 SqlSessionFactory 实例只能选择一种环境。

environments下有多个environment,每个environment有多个子标签

环境元素定义了如何配置环境。

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </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>

注意这里的关键点:

  • 默认使用的环境 ID(比如:default="development")。
  • 每个 environment 元素定义的环境 ID(比如:id="development")。
  • 事务管理器的配置(比如:type="JDBC")。
  • 数据源的配置(比如:type="POOLED")。

默认的环境和环境 ID 是自解释的,因此一目了然。

关于environment的子标签:

    transactionManager  事务管理器

在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):

  • JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。例如:
    <transactionManager type="MANAGED">
      <property name="closeConnection" value="false"/>
    </transactionManager>

如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名。

    dataSource 数据源

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。MyBatis 三种內建的数据源类型,即 type="[UNPOOLED|POOLED|JNDI]",数据源规定了可以配置哪些属性。

(1)UNPOOLED

UNPOOLED 不支持 JDBC 数据源连接池,实现的只是每次被请求时打开和关闭连接。其包含的属性:

  • driver:JDBC 驱动的 Java 类的完全限定名,如 MySQL 的 com.mysql.jdbc.Driver
  • url:数据库的 JDBC URL 地址
  • username:数据库的用户名
  • password:数据库的密码
  • defaultTransactionIsolationLevel:默认的连接事务隔离级别。

(2)POOLED

POOLED 支持 JDBC 数据源连接池,利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。除了有 UNPOOLED 的属性外还有包括 poolMaximumActiveConnections 、 poolMaximumIdleConnections 等属性。

(3)JNDI

JNDI 支持外部数据源连接池,它的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。其包含的属性:

  • initial_context:用来在 InitialContext 中寻找上下文
  • data_source:引用数据源实例位置的上下文的路径

mappers 映射器

        数据访问对象DAO定义好了,数据表设定好了,如何进行数据的curd操作,即sql操作呢?对于DAO,要定义相应的接口,该接口提供操控DAO的一系列方法(增删查改啥的),同时在设置一个xml配置文件与该接口对应,该xml与DAO得对应在主配置文件的mappers标签下进行配置。

        说白了就是要定义 SQL 映射语句。 但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

这些配置会告诉了 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件

核心映射文件Mapper

使用JDBC操控数据库写sql时,每次都要手动去执行sql获取数据,映射文件是专门用来放置sql语句的地方,执行的sql如何对应到DAO上?答案是使用接口,接口提供操控DAO的方法,配置文件使用sql实现与这些方法对应的映射,然后在主配置文件进行配置(mappers标签下配置mapper),完成了解耦。

即写接口,然后写xml配置文件。

映射文件(mapper)包含的顶级元素:

  • cache:给定命名空间的缓存配置。
  • cache-ref:其他命名空间缓存配置的引用。
  • resultMap:描述如何从数据库结果集中来加载对象。
  • sql:可被其他语句引用的可重用语句块。
  • insert:映射插入语句
  • update:映射更新语句
  • delete:映射删除语句
  • select:映射查询语句

属性namespace="xxx" 其中xxx必须是某个接口名,这样才能完成绑定

select

select映射查询语句,简单查询的 select 元素是非常简单的。比如:

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>

select查询到的结果会自动映射到resultType或者resultMap指定的类型上。

如:

<select id="selectUsers" resultType="com.someapp.model.User">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>

select内置了多个属性,常用的就是id,parameterType,resultType,resultMap。如果resultType的类型是某个自己写的Java Bean,那么MyBatis会自动创建对应的resultMap,当然手动配置resultMap也可以。

resultType:

1、基本类型  :resultType=基本类型

2、List类型:   resultType=List中元素的类型

3、Map类型     resultType =map

即select *自动返回list,xml的  resultType=List中元素的类型即可。

resultMap

resultMap相当于结果集,可在外部设置,如果使用resultType,则会进行自动配置,如果进行的是比较复杂的配置,那么可以在外部设置resultMap。主要配置的就是属性与列的对应。

<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id" />
  <result property="username" column="user_name"/>
  <result property="password" column="hashed_password"/>
</resultMap>

而在引用它的语句中使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:

<select id="selectUsers" resultMap="userResultMap">
  select user_id, user_name, hashed_password
  from some_table
  where id = #{id}
</select>

如果是复杂的sql语句,那么映射也会变得复杂,resultMap提供多种子标签解决这些事情。

id:主键映射,即DAO字段与数据库表的主键映射

result:非主键映射

id与result常见的属性是property(指定DAO字段),column(指定映射的列),javaType(指定映射字段的类型)

constructor:构造器映射,如果在DAO中定义了构造器,那么resultMap中进行相应配置即可

            例:

public User(Integer id,String username,String password,String sex,String address){
    this.id = id;
    this.username = username;
    this.password = password;
    this.sex = sex;
    this.address = address;
}

配置为:

<resultMap id="userMap" type="User">
    <constructor>  
        <idArg column="id" javaType="int"/>
        <arg column="username" javaType="String"/>
        <arg column="password" javaType="String"/>
        <arg column="sex" javaType="String"/>
        <arg column="address" javaType="String"/>
    </constructor>  
</resultMap>

指定参数与类型即可。

discriminator:鉴别器,用于动态鉴别列的值并映射到对应的结果集,即通过列的值分配不同的字段给DAO,很像 Java 语言中的 switch 语句。

update/insert/delete

与select类似,想要什么功能直接查看官方文档

过程概述

1.在pom.xml中导入依赖并设置<build>

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>lsl</groupId>
    <artifactId>lsl.desigin.db</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>springJdbc</name>
    <url>http://maven.apache.org</url>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
    </dependencies>


    <!--指定执行时的扫描选项-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

 

2.配置主文件,并在开发过程中不断添加typeAlias、mapper等

<?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 resource="myBatis.configure.properties" />

    <typeAliases>
        <typeAlias alias="Student" type="dao.Student"  />
        <typeAlias alias="Elder" type="dao.Elder"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <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>

    <mappers>
        <package name="mapper"/>
    </mappers>

</configuration>

 

3.写映射,即接口与xml映射文件

package mapper;

import dao.Student;

public interface StudentMapper {
    Student selectStudentById(Integer id) throws Exception;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.StudentMapper">

    <select id="selectStudentById" parameterType="int" resultType="Student">
        select * from students where id=#{id}
    </select>

</mapper>

4.使用映射完成sql语句的执行

使用过程为:

Resources.getResourceAsStream(configResource)获取总配置文件流

new SqlSessionFactoryBuilder().build(resourceInputStream);通过总配置文件流获取会话工厂

sqlSessionFactory.openSession();开启会话

sqlSession.getMapper(StudentMapper.class);通过反射、动态代理等获取自动生成的实现对应接口的对象

Student student=studentMapper.selectStudentById(1); sqlSession.commit();执行sql,会话进行提交执行

package study;

import dao.Student;
import mapper.StudentMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class MyBatisStudy {
    private static String configResource="MyBatis.config.xml";
    private static InputStream resourceInputStream=null;

    public static void main(String[] args){
        try {
            resourceInputStream= Resources.getResourceAsStream(configResource);//获取文件配置流
        }catch (Exception e){
            e.printStackTrace();
        }

        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(resourceInputStream);//创建工厂,传入信息
        SqlSession sqlSession=sqlSessionFactory.openSession();//开启会话
        StudentMapper studentMapper=sqlSession.getMapper(StudentMapper.class);//获取映射1,返回相关实例

        //可以执行sql语句了,对应的映射由MyBatis帮助完成
        try{
            Student student=studentMapper.selectStudentById(1);
            sqlSession.commit();//会话进行提交执行
            System.out.println(student.toString());
        }catch (Exception e){
            e.printStackTrace();
        }

        sqlSession.close();//关闭会话即可
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值