MyBatis Demo 编写(2)结果映射转换处理

【Mybatis】XML映射文件 xml和dtd 部分是必须填写且不需要配置的部分每次使用时,只需要将这部分复制到文件顶部即可。mapper 元素是整个映射文件的容器,所有的SQL映射都包含在这个元素中,mapper本身有一个参数namespace,即命名空间这个命名空间就是文件所对应的接口文件的Java类只有当这个命名空间被配置时,才可以直接通过访问Java接口的方法实现SOL调用。映射器是MyBatis最复杂且最重要的组件它由一个接口加上XML文件(或者注解)组成。 阅读详情

简介

在上篇中,我们完成了MyBatis一部分功能的搭建,已经能通过Mapper接口类的编写,自动执行相关的语句了,接下来完善结果处理部分

最终效果展示

修改下我们的Mapper

public interface PersonMapper {

    @Select("select * from person")
    List<Person> list();

    @Insert("insert into person (id, name) values ('1', '1')")
    void save();
}

测试代码如下:

public class SelfMybatisTest {

    @Test
    public void test() {
        try(SelfSqlSession session = buildSqlSessionFactory()) {
            PersonMapper personMapper = session.getMapper(PersonMapper.class);
            personMapper.save();
            List<Person> personList = personMapper.list();
            for (Object person: personList) {
                System.out.println(person.toString());
            }
        }
    }

    public static SelfSqlSession buildSqlSessionFactory() {
        String JDBC_DRIVER = "org.h2.Driver";
        String DB_URL = "jdbc:h2:file:./testDb";
        String USER = "sa";
        String PASS = "";

        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(DB_URL);
        config.setUsername(USER);
        config.setPassword(PASS);
        config.setDriverClassName(JDBC_DRIVER);
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        DataSource dataSource = new HikariDataSource(config);

        SelfConfiguration configuration = new SelfConfiguration(dataSource);
        configuration.addMapper(PersonMapper.class);
        return new SelfSqlSession(configuration);
    }
}

输出如下:

add sql source: mapper.mapper.PersonMapper.list
add sql source: mapper.mapper.PersonMapper.save
executor
executor
Person(id=1, name=1)

成功的返回我们自定义的Person对象,这个Demo已经有一点样子,算是达成了目标

下面是实现的相关细节

Demo编写

完整的工程已放到GitHub上:https://github.com/lw1243925457/MybatisDemo/tree/master/

本篇文章的代码对应的Tag是: V2

思路梳理

要实现SQL查询结果到Java对象的转换,我们需要下面的东西:

  • 1.返回的Java对象信息
  • 2.对应的SQL表字段信息
  • 3.SQL字段值到Java对象字段的转换处理
  • 4.读取SQL结果,转换成Java对象

1.返回的Java对象信息

我们需要知道当前接口方法返回的Java对象信息,方便后面的读取SQL查询结果,转换成Java对象

借鉴MyBatis,我们定义下面一个类,来保存接口方法返回的对象和SQL查询结果字段与Java对象字段的TypeHandler处理器

@Builder
@Data
public class ResultMap {

    private Object returnType;
    private Map<String,TypeHandler> typeHandlerMaps;
}

2.对应的SQL表字段信息

在以前的MyBatis源码解析中,我们大致知道获取TypeHandler是根据JavaType和JdbcType,我们就需要知道数据库表中各个字段的类型,方便后面去匹配对应的TypeHandler

我们在程序初始化的时候,读取数据库中所有的表,保存下其各个字段对应的jdbcType

可能不同表中有相关的字段,但是不同的类型,所以第一层是表名,第二层是字段名称,最后对应其jdbcType

代码如下:

public class SelfConfiguration {

    /**
     * 读取数据库中的所有表
     * 获取其字段对应的类型
     * @throws SQLException e
     */
    private void initJdbcTypeCache() throws SQLException {
        try (Connection conn = dataSource.getConnection()){
            final DatabaseMetaData dbMetaData = conn.getMetaData();
            ResultSet tableNameRes = dbMetaData.getTables(conn.getCatalog(),null, null,new String[] { "TABLE" });
            final List<String> tableNames = new ArrayList<>(tableNameRes.getFetchSize());
            while (tableNameRes.next()) {
                tableNames.add(tableNameRes.getString("TABLE_NAME"));
            }

            for (String tableName : tableNames) {
                try {
                    String sql = "select * from " + tableName;
                    PreparedStatement ps = conn.prepareStatement(sql);
                    ResultSet rs = ps.executeQuery();
                    ResultSetMetaData meta = rs.getMetaData();
                    int columnCount = meta.getColumnCount();
                    Map<String, Integer> jdbcTypeMap = new HashMap<>(columnCount);
                    for (int i = 1; i < columnCount + 1; i++) {
                        jdbcTypeMap.put(meta.getColumnName(i).toLowerCase(), meta.getColumnType(i));
                    }
                    jdbcTypeCache.put(tableName.toLowerCase(), jdbcTypeMap);
                } catch (Exception ignored) {
                }
            }
        }
    }
}

3.SQL字段值到Java对象字段的转换处理

接下来我们要定义JavaType与jdbcType相互转换的TypeHandler

简化点,我们内置定义String和Long类型的处理,并在初始化的时候进行注册(还没涉及到参数转换处理,所以暂时定义jdbcType到JavaType的处理)

TypeHandler代码如下:

public interface TypeHandler {

    Object getResult(ResultSet res, String cluName) throws SQLException;
}

public class StringTypeHandler implements TypeHandler {

    private static final StringTypeHandler instance = new StringTypeHandler();

    public static StringTypeHandler getInstance() {
        return instance;
    }

    @Override
    public Object getResult(ResultSet res, String cluName) throws SQLException {
        return res.getString(cluName);
    }
}

public class LongTypeHandler implements TypeHandler {

    private static LongTypeHandler instance = new LongTypeHandler();

    public static LongTypeHandler getInstance() {
        return instance;
    }

    @Override
    public Object getResult(ResultSet res, String cluName) throws SQLException {
        return res.getLong(cluName);
    }
}

默认初始化注册:

public class SelfConfiguration {

    private void initTypeHandlers() {
        final Map<Integer, TypeHandler> varchar = new HashMap<>();
        varchar.put(JDBCType.VARCHAR.getVendorTypeNumber(), StringTypeHandler.getInstance());
        typeHandlerMap.put(String.class, varchar);

        final Map<Integer, TypeHandler> intType = new HashMap<>();
        intType.put(JDBCType.INTEGER.getVendorTypeNumber(), LongTypeHandler.getInstance());
        typeHandlerMap.put(Long.class, intType);
    }
}

接着重要的一步是构建接口函数方法返回的结果处理了,具体的细节如下,关键的都进行了相关的注释:

public class SelfConfiguration {

    private final DataSource dataSource;
    private final Map<String, SqlSource> sqlCache = new HashMap<>();
    private final Map<String, ResultMap> resultMapCache = new HashMap<>();
    private final Map<String, Map<String, Integer>> jdbcTypeCache = new HashMap<>();
    private final Map<Class<?>, Map<Integer, TypeHandler>> typeHandlerMap = new HashMap<>();

        /**
     * Mapper添加
     * 方法路径作为唯一的id
     * 保存接口方法的SQL类型和方法
     * 保存接口方法返回类型
     * @param mapperClass mapper
     */
    public void addMapper(Class<?> mapperClass) {
        final String classPath = mapperClass.getPackageName();
        final String className = mapperClass.getName();
        for (Method method: mapperClass.getMethods()) {
            final String id = StringUtils.joinWith("." ,classPath, className, method.getName());
            for (Annotation annotation: method.getAnnotations()) {
                if (annotation instanceof Select) {
                    addSqlSource(id, ((Select) annotation).value(), SqlType.SELECT);
                    continue;
                }
                if (annotation instanceof Insert) {
                    addSqlSource(id, ((Insert) annotation).value(), SqlType.INSERT);
                }
            }

            // 构建接口函数方法返回值处理
            addResultMap(id, method);
        }

    }

    /**
     * 构建接口函数方法返回值处理
     * @param id 接口函数 id
     * @param method 接口函数方法
     */
    private void addResultMap(String id, Method method) {
        // 空直接发返回
        if (method.getReturnType().getName().equals("void")) {
            return;
        }

        // 获取返回对象类型
        // 这里需要特殊处理下,如果是List的话,需要特殊处理得到List里面的对象
        Type type = method.getGenericReturnType();
        Type returnType;
        if (type instanceof ParameterizedType) {
            returnType = ((ParameterizedType) type).getActualTypeArguments()[0];
        } else {
            returnType = method.getReturnType();
        }
        // 接口方法id作为key,值为 接口方法返回对象类型和其中每个字段对应处理的TypeHandler映射
        resultMapCache.put(id, ResultMap.builder()
                .returnType(returnType)
                .typeHandlerMaps(buildTypeHandlerMaps((Class<?>) returnType))
                .build());
    }

    /**
     * 构建实体类的每个字段对应处理的TypeHandler映射
     * @param returnType 接口函数返回对象类型
     * @return TypeHandler映射
     */
    private Map<String, TypeHandler> buildTypeHandlerMaps(Class<?> returnType) {
        // 这里默认取类名的小写为对应的数据库表名,当然也可以使用@TableName之类的注解
        final String tableName = StringUtils.substringAfterLast(returnType.getTypeName(), ".").toLowerCase();
        final Map<String, TypeHandler> typeHandler = new HashMap<>(returnType.getDeclaredFields().length);
        for (Field field : returnType.getDeclaredFields()) {
            final String javaType = field.getType().getName();
            final String name = field.getName();
            final Integer jdbcType = jdbcTypeCache.get(tableName).get(name);
            // 根据JavaType和jdbcType得到对应的TypeHandler
            typeHandler.put(javaType, typeHandlerMap.get(field.getType()).get(jdbcType));
        }
        return typeHandler;
    }
}

4.读取SQL结果,转换成Java对象

接下来就是SQL查询结果的处理了,主要是根据在初始化阶段构建好的针对每个返回类型ResultMap

  • 根据ResultMap中的返回对象类型,生成对象实例
  • 根据ResultMap中的TypeHandler映射,得到各个字段对应的TypeHandler,得到处理结果
  • 反射调用对象的Set方法

代码如下:

public class ResultHandler {

    public List<Object> parse(String id, ResultSet res, SelfConfiguration config) throws SQLException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        if (res == null) {
            return null;
        }

        // 根据接口函数Id得到初始化时国家队ResultMap
        ResultMap resultMap = config.getResultType(id);

        final List<Object> list = new ArrayList<>(res.getFetchSize());
        while (res.next()) {
            // 根据接口函数返回类型,生成一个实例
            Class<?> returnType = (Class<?>) resultMap.getReturnType();
            Object val = returnType.getDeclaredConstructor().newInstance();
            
            for (Field field: returnType.getDeclaredFields()) {
                final String name = field.getName();
                // 根据返回对象的字段类型,得到对应的TypeHandler,调用TypeHandler处理得到结果
                TypeHandler typeHandler = resultMap.getTypeHandlerMaps().get(field.getType().getName());
                Object value = typeHandler.getResult(res, name);
                // 调用对象的Set方法
                String methodEnd = name.substring(0, 1).toUpperCase() + name.substring(1);
                Method setMethod = val.getClass().getDeclaredMethod("set" + methodEnd, field.getType());
                setMethod.invoke(val, value);
            }
            list.add(val);
        }
        return list;
    }
}

总结

本篇中完善了Demo中对查询结果集的自动处理转换部分,完成后,核心的功能就算已经完成了,基本达到了目标

MyBatis反射和类型转换 反射工具 阅读详情

相关推荐

Python大数据舆情分析系统开发实战

舆情监测系统是大数据分析的重要应用场景,通过自然语言处理(NLP)技术解析社交媒体文本情感倾向。基于Python技术栈的解决方案整合了Scrapy爬虫框架进行数据采集,PySpark处理海量文本数据,结合SnowNLP和Jieba实现中文语义分析。系统采用微服务架构设计,包含数据采集层、处理层、存储层和应用层,通过情感分析优化和实时热点检测算法提升监测准确率。典型应用包括企业品牌监测、公共事件预警等场景,开发过程中需特别注意数据合规性和分布式集群的性能调优。

weixin_34408624的博客 386

mybatis 指定类型映射处理,实现针对数组集合,枚举,JSON对应类型映射

主要说明对于mapper映射查询和新增时候进行的一些特殊类型处理。 首先讲解一下映射数组 CREATE TABLE `admin_role_ids` ( `id` bigint(20) NOT NULL primary key AUTO_INCREMENT COMMENT 'id', `type` varchar(16) DEFAULT NULL COMMENT '分别 admin , tourist 枚举', `role_ids` varchar(256) DEFAULT NULL COMME

Dizzilys的博客 4983

Layout常见错误汇总-不定时更

1、在做LVS时,出现中断错误 解决方法: 重新建立新的schematic和layout文件,对应好其中的元器件,同时两个文件的名字也要对应好“schematic”和“layout”,其他名字的话软件找不到如何对应。

weixin_38753095的博客 4万+

一文帮你搞定MyBatis的类型转换模块,深度好文,欢迎一键三连!!!

  本文来给大家详细的分析下MyBatis的基础支持层中的类型转换模块。 类型转换模块   MyBatis是一个持久层框架ORM框架,实现数据库中数据和Java对象中的属性的双向映射,那么不可避免的就会碰到类型转换的问题,在PreparedStatement为SQL语句绑定参数时,需要从Java类型转换为JDBC类型,而从结果集中获取数据时,则需要从JDBC类型转换为Java类型,所以我们来看下在MyBatis中是如何实现类型的转换的。 String sql = "SELECT id,user.

波波烤鸭的博客 4726

无人机蜂群联合指挥作战技术详解

随着人工智能、通信技术、新材料等领域的不断进步,无人机蜂群联合指挥作战技术将迎来更加广阔的发展前景。未来,无人机蜂群将更加智能化、自主化,能够执行更加复杂、精细的作战任务。同时,加强国际合作,共同制定国际规则和标准,将是推动无人机蜂群技术健康发展的重要方向。无人机蜂群技术,是指通过先进的通信技术、自主导航与决策算法,将大量小型、低成本、功能各异的无人机组织成一个高度协同的群体,执行复杂多样的军事任务。2. 通信与数据链:采用高速、低延迟、抗干扰的通信技术,保障群体内无人机间的信息交换。

专注多旋翼、复合翼无人机技术研究,从事低空经济行业。 1542

【ubuntu】【VirtualBox】VirtualBox无法加载USB移动设备的解决方法(支持U盘启动盘)

提示:测试可用。

联系方式:927632640 2439

MyBatis 类型转换器

在数据库中采用varchar、text等字符串类型存储一个整形id集合,id间采用“,”分隔符分割,但在Java(MyBatis)实体中我们希望使用List ids来映射数据库中的字段。// 将List类型的属性转换成数据库中的字符串 StringBuilder sb = new StringBuilder();// 将数据库中的字符串转换成List类型的属性 String str = rs . getString(columnName);

做一名持续学习的GIS Coder 2856

基于STM32设计的环境监测系统(华为云IOT)_300

随着空气污染问题日益严峻,环境空气质量的监测变得愈加重要。为了解决这一问题,本项目设计并实现了一款基于STM32的空气质量监测设备,能够实时监测空气中的温湿度、二氧化碳浓度和甲醛浓度,并通过OLED显示屏展示数据。此外,设备支持通过WIFI模块将数据上传至云端,用户可以通过手机APP或Windows上位机查看实时数据。设备采用MQTT协议与华为云IOT物联网服务器进行数据通信,确保数据的实时性和可靠性。本项目还结合了低功耗设计和高灵敏度传感器,使得设备能够高效稳定地运行,并实现远程监控功能。

695

MyBatis-plus 转化处理 SQL 语句的源码分析

前言 在 MyBatis-plus 自定义通用方法及其实现原理 中笔者介绍了 MyBatis-plus 添加通用方法的实现方式,但是其中还有一些细节需要澄清,下文笔者将详细分析 MyBatis-plus 对 SQL 语句脚本的构建,以及将其嵌入MappedStatement 的过程 MyBatis 使用 SqlSource 构建可执行的 SQL 语句 1. MyBatis-plus 对 SQL 语句脚本的处理 我们都知道 MyBatis-plus 干掉了繁琐的 XML 文件,使 MyBatis 框架

谈谈1974 8184

YOLOv8改进 | 主干网络 | 将主干网络替换为轻量化的ShuffleNetv2【原理 + 完整代码】

yolov8,yolov8创新,yolov8改进

kay_545 2563

飞牛fnNAS同步微信微信聊天记录

微信聊天记录如何备份到NAS?本文介绍了利用NAS虚拟机和同步工具实现微信记录自动备份的方法。通过在NAS上搭建"云电脑"长期运行微信PC端,并配置微力同步将聊天记录文件夹自动同步到NAS存储,解决了传统电脑备份的安全性问题。操作步骤包括:1)在NAS上安装虚拟机系统;2)登录微信PC端并同步手机记录;3)配置同步工具将微信记录文件夹同步到NAS。该方法尤其适合不常使用PC微信的用户,可实现记录永久保存和远程查看,更换手机时也能快速恢复历史记录。

dgnankai的专栏 4297

MyBatis 映射文件之结果集映射总结

文章目录1. 简单结果映射1.1. 使用 Map1.2. 使用 JavaBean2. 高级结果映射2.1. id & result2.2. constructor2.3. association2.3.1. 嵌套 select 查询2.3.2. 嵌套结果映射2.3.3. 多结果集(存储过程)2.3.4. 简单映射(推荐使用)2.4. collection2.4.1. 嵌套 select 查询2.4.2. 嵌套结果映射(推荐使用)2.4.3. 多结果集(存储过程)2.4. discriminator3

一线大码 2613

17、自适应和学习索引:在线物理设计调整的新维度

本文探讨了现代数据库管理系统中自适应索引和数据库裂解的新方法,通过结合学习模型与多臂老虎机(MAB)框架,实现在线物理设计调整。文章详细介绍了自适应索引的工作原理、数据库裂解技术以及如何利用机器学习进行索引选择和优化,并讨论了MAB框架在动态调整索引结构中的应用及其实际案例。

go5gopher的博客 80

Mybatis结果映射详细讲解

resultMap是mybatis中最复杂的元素之一,它描述如何从结果集中加载对象,主要作用是定义映射规则、级联的更新、定制类型转化器。 resultMap参数讲解 constructor: 用于配置构造器方法 id: 将结果集标记为id,以方便全局调用 result: 配置POJO到数据库列名映射...

Yang_Hui_Liang的博客 2365

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Mybatis开启驼峰命名映射、Mybatis数据库与实体类自动映射、Mybatis底层映射原理、源码讲解结果集映射为实体类原理

Mybatis开启驼峰命名映射、Mybatis数据库与实体类自动映射、Mybatis底层映射原理、源码讲解结果集映射为实体类原理

weixin_49114503的博客 1万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Mybatis源码本地化构建Demo

目录绪论构建SQL配置文件映射文件Application总结 绪论 上一篇文章中专门整理了如何构建Mybatis源码,小伙伴们可以在本地写demo跟踪或者注释修改源码等等骚操作。。。本篇就是为小伙伴们介绍一下如何本地化构建demo。 这里分享一下mybatis的中文官方文档,小编构建本地化demo时参考了官方文档。 构建 首先,创建一个package,命名为demo。小编先贴出自己本地化目录。 SQL 数据库中创建blog表,并在model包下生成对应实体类Blog; DROP TABLE IF EXIS

增量星球 4014

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

MyBatis学习之映射器Mapper(接口映射器+xml映射文件)

Table of Contents 01 MyBatis映射器: 1.1 接口映射器+xml映射器 1.2 接口映射器+注解 02 接口映射器+xml映射器 方式 2.1 mybatis配置文件 引入映射器: 2.2 XML映射文件 的几个顶级元素: 2.2.1select元素 以及Select 元素的属性 2.2.2insert, update 和 delete元素 ...

LJW874362735的博客 1万+

Mybatis 映射器中使用普通结果映射(@Results)

本篇主要介绍如何在Mybatis映射器中使用@Results定义结果映射,并且使用无参数、带有参数的构造方法创建映射对象

Mointor 1046

Mybatis 复杂结果映射(ResultMap) - 一对多关系映射

使用Mybatis中resultMap标签的复杂结果映射解决一对多关系中复杂的数据映射问题。

Mointor 1261

【MyBatis】简单映射

今天开始复习总结MyBatis,正好换了新电脑(嘿嘿),重新熟悉一下如何从头开始创建一个MyBatis项目、导入依赖、基础配置、编写Mapper、结果映射、获取结果~

stillaliveQEJ的博客 727

【MyBatis】MyBatis XML 开发模式:配置、映射、CRUD 实战与结果映射全攻略

使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能.如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中.这篇文章主要来讲XML是如何使用的,本来也是先有的XML才有的后来的注解的方式,所以了解完注解的方式,再来看XML会特别简单,几乎没什么太大差别。关于开发中使用哪种模式这个问题,没有明确答案. 仁者见仁智者见智,并没有统一的标准,更多是取决于你的团队或者项目经理,项目负责人.同样的,使用XML 的方式进行查询,也存在数据封装的问题。

2503_90322569的博客 1055

MyBatis进阶:掌握MyBatis动态SQL与模糊查询、结果映射,让你在面试中脱颖而出!!

本文将从进阶的角度出发,详细介绍MyBatis的动态SQL和模糊查询,同时也会讲解结果映射的技巧。通过学习本文,你将能够更加熟练地使用MyBatis,提高自己的开发效率,为面试增添信心。快来学习吧!如果你正在寻找一份高薪稳定的工作,那么掌握MyBatis动态SQL与模糊查询、结果映射是必不可少的技能。这些技能可以让你在面试中展现出自己的实力,从而获得更好的职业发展机会。所以不要犹豫了,赶快来学习吧!

万维网连五洲,二进制写尽天下事,喜欢结识新伙伴寻找志同道合的朋友,欢迎一起探讨学习 1735

MyBatis结果映射机制详解:解决查询对象属性为null的实战方案

在Java持久层开发中,对象关系映射(ORM)框架负责将数据库查询结果转换为Java对象,这是数据访问层的核心机制。MyBatis通过ResultMap实现结果映射,其原理涉及构造方法调用、属性填充和类型处理等多个环节。理解这一机制的技术价值在于能够避免常见的NullPointerException问题,确保数据完整性,提升应用稳定性。在实际应用场景中,当实体类包含自定义构造方法时,MyBatis的实例化策略可能导致部分属性未被正确赋值,进而引发业务逻辑异常。本文针对这一典型问题,深入剖析MyBatis的映

weixin_30715523的博客 410

MyBatis进阶之结果集映射

MyBatis,resultMap 一对一,一对多,多对多映射

xiaotu的博客 225

Mybatis 复杂结果映射(ResultMap) - 一对一关系映射

使用Mybatis中复杂的结果映射(resultMap)解决一对一(association)关系中复杂的数据映射问题

Mointor 659

MyBatis使用Demo

MyBatis 基本用法的一个学习记录,方便后面查阅

蒋大钊的博客 1153
上一篇: MyBatis Demo 编写(1)基础功能搭建
下一篇: Mybatis3 源码解析系列
_萧_
博客等级 码龄10年 55粉丝 157原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值