Insert和Rs.Addnew的比较

本文探讨了使用ADO RecordSet对象的Addnew方法与直接使用SQL INSERT语句在添加数据库记录时的区别。直接使用SQL语句可以提高数据存取速度,但可能在处理长字段和复杂情况时受限;而ADO方法虽然资源消耗较大,但更易于管理和调试。

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

   添加一条新记录是通过RecordSet对象的Addnew方法实现的,还是通过执行SQL语句中的insert语句也可以添加新记录,为何不选用后者呢?二者有何区别呢?

从本质上说,ADO的Addnew方法只是将“Insert into”语句封装了起来,所以,直接使用SQL语句将会大大加快存取数据的速度,因为他减少了ADO的“翻译”时间,尤其当对大量数据进行操作的时候表现得更为明显。但是直接使用SQL语句的缺点:不能传递太长的字段内容,在向数据库中IMAGE字段加入数据时,长文本数据时,就需要使用Addnew了。此外,当字段比较多时SQL语句将会很长而导致不易排错,推荐有经验的编程者优先考虑。

相比较而言,RecordSet对象的Addnew方法首先需要建立一个可更新的RecordSet对象,并且一直保持到Update结束,所以比较消耗系统资源。不过它的优点是灵活性比较好,代码容易阅读,除错方便,推荐新手使用。

package com.api.hw.hssl; import bsh.Console; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.api.hw.utils.Result; import com.api.hw.utils.WebJsonUtils; import com.api.integration.util.DbUtil; import com.api.integration.web.OAuth2ServerAction; import com.caucho.quercus.env.Var; import com.engine.cpt.cmd.alert.GetBaseInfoCmd; import dm.jdbc.stat.support.WebServer; import org.apache.axis.session.Session; import org.mozilla.javascript.Function; import vba.word.Index; import weaver.conn.DBUtil; import weaver.conn.RecordSet; import weaver.general.BaseBean; import weaver.integration.logging.Logger; import weaver.integration.logging.LoggerFactory; import weaver.mobile.plugin.ecology.RequestOperation; import weaver.soa.workflow.request.RequestInfo; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.swing.*; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @Path("/hw/hssl") public class SyncOrderAPI { private Logger logger = LoggerFactory.getLogger(OAuth2ServerAction.class); public static final String TABLE_NAME = "formtable_main_333_dt2"; @POST @Path("/SynOrders") @Produces("application/json") public Object SyncOrderinfo(@Context HttpServletRequest request, @Context HttpServletResponse response) { this.logger.info("================SyncOrderInfo start...==============================="); JSONObject requestJSON = WebJsonUtils.getRequestJSON(request); JSONArray items = requestJSON.getJSONArray("data"); String requestId = requestJSON.getString("requestId"); List<List> batchParams = new ArrayList<>(); RecordSet rs = new RecordSet(); Result result = null; HashMap retMap = new HashMap(); String sql = "INSERT INTO " + TABLE_NAME + " (" + "orderType, originalOrderNo, bookingName, travelerName, orderDate, orderStatus," + "start_datetime, end_datetime, orderAmount, personPay, businessPay, discount," + "actualPlaceOfDeparture,actualDestination, universalType,roomType, trainSeat,cabinType, " + "vehicleType, isExcess, excessReason, flightNumber, trainNumber, hotelAddress," + "roomQuantity, liveDay, roomDays, mainid" + ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; String sql_1 = "SELECT id FROM formtable_main_333 where requestid = ?"; try { for (int i=0;i<items.size();i++) { JSONObject item = items.getJSONObject(i); List<Object> rowParams = new ArrayList<>(); rowParams.add(item.getInteger("orderType"));//订单类型 rowParams.add(item.getString("originalOrderNo"));//订单明细编号 rowParams.add(item.getString("bookingName"));//订票人姓名 rowParams.add(item.getString("travelerName"));//出行人姓名 rowParams.add(item.getString("orderDate"));//订单创建日期 rowParams.add(item.getString("orderStatus"));//订单状态 rowParams.add(item.getString("start_datetime"));//订单开始时间 rowParams.add(item.getString("end_datetime"));//订单结束时间 rowParams.add(item.getString("orderAmount"));//订单总金额 rowParams.add(item.getString("personPay"));//个人支付金额 rowParams.add(item.getString("businessPay"));//企业支付金额 rowParams.add(item.getString("discount"));//折扣 rowParams.add(item.getString("actualPlaceOfDeparture"));//实际出发地 rowParams.add(item.getString("actualDestination"));//实际到达地 rowParams.add(item.getString("universalType"));//通用类型 rowParams.add(item.getInteger("roomType"));//房型 rowParams.add(item.getInteger("trainSeat"));//火车席位 rowParams.add(item.getInteger("cabinType"));//舱位类型 rowParams.add(item.getInteger("vehicleType"));//车型 rowParams.add(item.getInteger("isExcess"));//是否超标 rowParams.add(item.getString("excessReason"));//超标原因 rowParams.add(item.getString("flightNumber"));//航班号 rowParams.add(item.getString("trainNumber"));//火车车次 rowParams.add(item.getString("hotelAddress"));//酒店地址 rowParams.add(item.getString("roomQuantity"));//房间数量 rowParams.add(item.getString("liveDay"));//入住天数 rowParams.add(item.getString("roomDays"));//间夜数 rowParams.add(item.getInteger("mainid")); batchParams.add(rowParams); } boolean success = rs.executeBatchSql(sql,batchParams); if (success) { result = Result.success(); result.setMessage(""); } else { return Result.build(500, "数据库操作失败",null); } } catch (Exception e) { logger.error(e.getMessage()); return Result.build(500, "系统异常: "+e.getMessage(),retMap); } finally { logger.info("=============== SyncOrderInfo 结束 ==============="); } return result; } }根据requestid查询id 然后将其传给mainid
07-11
package game.game2048; /** * @BelongsProject: TimeGame * @BelongsPackage: game * @Author: 土里埋埋埋埋埋 * @CreateTime: 2025-07-31 11:04 * @Description: 数据库操作 * @Version: 1.0 */ import java.sql.*; import java.util.ArrayList; import java.util.List; public class GameDAO { private Connection connection; //登录数据库 public GameDAO() throws SQLException { connection = DriverManager.getConnection( "jdbc:mysql://117.72.189.79:3306/2048?useUnicode=true&characterEncoding=utf8", "root", "Fbb608425@" ); } // 用户注册 public boolean registerUser(String username, String password) throws SQLException { String sql = "INSERT INTO users (username, password) VALUES (?, ?)"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setString(1, username); stmt.setString(2, password); return stmt.executeUpdate() > 0; } } //用户登录 public int authenticateUser(String username, String password) throws SQLException { String sql = "SELECT id FROM users WHERE username = ? AND password = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setString(1, username); stmt.setString(2, password); ResultSet rs = stmt.executeQuery(); if (rs.next()) { return rs.getInt("id"); } } return -1; } // 分数操作 public void saveScore(int userId, int score) throws SQLException { String sql = "INSERT INTO scores (user_id, score) VALUES (?, ?)"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, userId); stmt.setInt(2, score); stmt.executeUpdate(); } } //个人最高分 public List<String> maxscore(int user_id) throws SQLException { List<String> ranking = new ArrayList<>(); String sql = "SELECT MAX(score) AS max_score " + "FROM scores WHERE user_id = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, user_id); ResultSet rs = stmt.executeQuery(); if (rs.next()) { int maxScore = rs.getInt("max_score"); ranking.add("最高分数: " + maxScore); } } return ranking; } //总排行 public List<String> getGlobalRanking() throws SQLException { List<String> ranking = new ArrayList<>(); String sql = "SELECT u.username, s.score " + "FROM scores s " + "JOIN users u ON s.user_id = u.id " + "ORDER BY score DESC LIMIT 10"; try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { int rank = 1; while (rs.next()) { ranking.add(rank++ + ". " + rs.getString("username") + " - " + "得分:" + rs.getInt("score")); } } return ranking; } //个人历史 public List<String> getUserHistory(int userId) throws SQLException { List<String> history = new ArrayList<>(); String sql = "SELECT score, game_time FROM scores " + "WHERE user_id = ? ORDER BY game_time DESC LIMIT 10"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, userId); ResultSet rs = stmt.executeQuery(); int rank = 1; while (rs.next()) { history.add(rank++ + ". " + "得分:" + rs.getInt("score") + " - " + rs.getTimestamp("game_time")); } } return history; } // 存档操作 public void saveGame(int userId, String saveName, String boardState, int score) throws SQLException { String sql = "INSERT INTO saves (user_id, save_name, board_state, score) " + "VALUES (?, ?, ?, ?)"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, userId); stmt.setString(2, saveName); stmt.setString(3, boardState); stmt.setInt(4, score); stmt.executeUpdate(); } } public List<String> getUserSaves(int userId) throws SQLException { List<String> saves = new ArrayList<>(); String sql = "SELECT id, save_name, score, save_time FROM saves " + "WHERE user_id = ? ORDER BY save_time DESC"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, userId); ResultSet rs = stmt.executeQuery(); while (rs.next()) { saves.add(rs.getInt("id") + ". " + rs.getString("save_name") + " - " + rs.getInt("score") + " - " + rs.getTimestamp("save_time")); } } return saves; } //读档操作 public GameSave loadGame(int saveId) throws SQLException { String sql = "SELECT board_state, score FROM saves WHERE id = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, saveId); ResultSet rs = stmt.executeQuery(); if (rs.next()) { return new GameSave(rs.getString("board_state"), rs.getInt("score")); } } return null; } static class GameSave { String boardState; int score; public GameSave(String boardState, int score) { this.boardState = boardState; this.score = score; } } }上述条件中修改此代码
最新发布
08-07
package com.cissst.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import com.cissst.dao.IAdminDao; import com.cissst.entity.Admin; import com.cissst.util.DBUtil; public class AdminDaoImpl implements IAdminDao { @Override public List<Admin> getAllAdmin() throws SQLException { String sql = "select * from admin order by name"; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery()) { List<Admin> list = new ArrayList<>(); while (rs.next()) { Admin a = new Admin(); a.setId(rs.getInt("id")); a.setName(rs.getString("name")); a.setPassword(rs.getString("password")); a.setAge(rs.getInt("age")); a.setSex(rs.getString("sex")); a.setTel(rs.getString("tel")); a.setPhone(rs.getString("phone")); a.setAddr(rs.getString("addr")); a.setMemo(rs.getString("memo")); list.add(a); } return list; } } @Override public void save(Admin a) throws SQLException { String sql = "insert into admin(NAME,PASSWORD,SEX,AGE,TEL,PHONE,ADDR,MEMO) values(?,?,?,?,?,?,?,?) "; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, a.getName()); stmt.setString(2, a.getPassword()); stmt.setString(3, a.getSex()); stmt.setInt(4, a.getAge()); stmt.setString(5, a.getTel()); stmt.setString(6, a.getPhone()); stmt.setString(7, a.getAddr()); stmt.setString(8, a.getMemo()); stmt.executeUpdate(); } } @Override public Admin getAdminById(String id) throws SQLException { String sql = "select * from admin where id = ?"; Admin a = null; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, id); try (ResultSet rs = stmt.executeQuery()) { while (rs.next()) { a = new Admin(); a.setId(rs.getInt("id")); a.setName(rs.getString("name")); a.setPassword(rs.getString("password")); a.setAge(rs.getInt("age")); a.setSex(rs.getString("sex")); a.setTel(rs.getString("tel")); a.setPhone(rs.getString("phone")); a.setAddr(rs.getString("addr")); a.setMemo(rs.getString("memo")); } } } return a; } @Override public void update(Admin a) throws SQLException { String sql = " update admin set NAME = ?,PASSWORD = ?,SEX = ?,AGE = ?, TEL = ?,PHONE = ?,ADDR = ?,MEMO = ? where id = ? "; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, a.getName()); stmt.setString(2, a.getPassword()); stmt.setString(3, a.getSex()); stmt.setInt(4, a.getAge()); stmt.setString(5, a.getTel()); stmt.setString(6, a.getPhone()); stmt.setString(7, a.getAddr()); stmt.setString(8, a.getMemo()); stmt.setInt(9, a.getId()); stmt.executeUpdate(); } } @Override public void delete(String id) throws SQLException { String sql = "delete from admin where id= ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, id); stmt.executeUpdate(); } } @Override public Admin getAdminBynp(String name, String password) throws SQLException { String sql = "select * from admin where name = ? and password = ?"; Admin a = null; try (Connection conn = DBUtil.getConnection()) { assert conn != null; try (PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, name); stmt.setString(2, password); try (ResultSet rs = stmt.executeQuery()) { while (rs.next()) { a = new Admin(); a.setId(rs.getInt("id")); a.setName(rs.getString("name")); a.setPassword(rs.getString("password")); a.setAge(rs.getInt("age")); a.setSex(rs.getString("sex")); a.setTel(rs.getString("tel")); a.setPhone(rs.getString("phone")); a.setAddr(rs.getString("addr")); a.setMemo(rs.getString("memo")); } } } } return a; } }at com.cissst.dao.impl.AdminDaoImpl.getAdminBynp(AdminDaoImpl.java:116)
08-04
<think>我们正在分析`AdminDaoImpl.getAdminBynp`方法,根据用户提供的异常堆栈信息,我们需要关注该方法在执行数据库查询时可能出现的异常逻辑错误。结合引用内容,我们注意到: - 引用[1]提到:运行错误(如外部数据不正确)会导致程序异常终止。在数据库查询中,如果传入的参数不正确(如空值、格式错误等)或者数据库连接问题,都可能引发异常。 - 引用[2]提到:在需求频繁变动时,静态语言(如Java)可能需要频繁修改代码。这提示我们在设计数据库访问层时,应考虑可扩展性可维护性。 - 引用[3]提供了数据库表结构查询示例,我们可以参考其表结构SQL语句。 ### 1. 异常分析 在`getAdminBynp`方法中,可能发生的异常包括: - **`SQLException`**:数据库操作失败(如连接失败、SQL语法错误等)。 - **`NullPointerException`**:当`DBUtil.getConnection()`返回`null`时,后续操作会抛出空指针异常(如引用[1]所述,外部数据错误导致)。 - **参数错误**:如果传入的`name`或`password`为`null`,可能导致SQL语句执行错误(如引用[1]所述)。 ### 2. 逻辑错误分析 - **密码明文存储与比较**:在数据库中存储明文密码,存在安全风险(引用[3]中`Student`表的设计未涉及密码,但这里我们讨论的是`admin`表)。应使用哈希加盐存储。 - **结果集处理**:使用`while`循环但只取最后一个结果,当有多个同名用户时,逻辑错误。 - **连接管理**:虽然使用了try-with-resources,但`Connection`对象在方法内创建,应确保其正确关闭。 ### 3. 解决方案 #### 3.1 参数校验 在方法开始处增加参数校验: ```java if (name == null || password == null) { throw new IllegalArgumentException("用户名密码不能为空"); } ``` #### 3.2 数据库连接检查 替换`assert`为显式检查: ```java Connection conn = DBUtil.getConnection(); if (conn == null) { throw new SQLException("无法获取数据库连接"); } ``` #### 3.3 使用try-with-resources确保资源释放 由于`Connection`、`PreparedStatement``ResultSet`都实现了`AutoCloseable`,我们可以使用嵌套的try-with-resources: ```java try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { // 设置参数并执行查询 try (ResultSet rs = stmt.executeQuery()) { // 处理结果 } } ``` #### 3.4 结果集处理 修改为只取第一条记录(如果存在): ```java if (rs.next()) { Admin admin = new Admin(); admin.setId(rs.getInt("id")); admin.setName(rs.getString("name")); // 注意:这里不应该设置密码到Admin对象,或者至少不应是明文 // 应该将密码验证放在服务层,使用哈希比较 return admin; } return null; ``` #### 3.5 密码安全改进 在服务层(`AdminServiceImpl.findBynp`)中,不应直接使用明文密码查询数据库,而应: 1.数据库获取该用户名的记录(包括密码哈希盐值)。 2. 使用BCrypt等算法验证输入密码是否匹配哈希值。 因此,我们需要修改DAO方法,使其只按用户名查询: ```java public Admin getAdminByName(String name) throws SQLException { // 只根据用户名查询 String sql = "select * from admin where name = ?"; // ... 同上,省略 } ``` 然后在服务层验证密码: ```java public Admin findBynp(String name, String password) { try { Admin admin = adminDao.getAdminByName(name); if (admin != null && BCrypt.checkpw(password, admin.getPasswordHash())) { return admin; } return null; } catch (SQLException e) { // 处理异常 } } ``` ### 4. 完整代码示例(修改后) ```java // AdminDaoImpl.java @Override public Admin getAdminByName(String name) throws SQLException { if (name == null) { throw new IllegalArgumentException("用户名不能为空"); } String sql = "select id, name, password_hash as passwordHash from admin where name = ?"; try (Connection conn = DBUtil.getConnection()) { if (conn == null) { throw new SQLException("数据库连接失败"); } try (PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, name); try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { Admin admin = new Admin(); admin.setId(rs.getInt("id")); admin.setName(rs.getString("name")); admin.setPasswordHash(rs.getString("passwordHash")); // 存储的是哈希值 return admin; } } } } return null; } ``` ```java // AdminServiceImpl.java public Admin findBynp(String name, String password) { try { Admin admin = adminDao.getAdminByName(name); if (admin == null) { return null; } // 验证密码 if (BCrypt.checkpw(password, admin.getPasswordHash())) { return admin; } return null; // 密码不匹配 } catch (SQLException e) { // 记录日志并抛出适当的业务异常 throw new ServiceException("查询管理员失败", e); } } ``` ### 5. 总结 通过以上修改,我们解决了: 1. **异常处理**:参数校验连接检查避免了空指针非法参数。 2. **资源管理**:使用try-with-resources确保资源释放。 3. **安全**:采用哈希加盐存储密码,避免明文存储。 4. **逻辑**:正确返回第一个匹配的管理员(按用户名唯一),并在服务层验证密码。 §§相关问题§§ 1. Java 中如何正确处理 JDBC 连接的空指针异常? 2. 使用 try-with-resources 时有哪些常见陷阱? 3. 数据库密码存储的最佳实践是什么? 4. 如何设计安全的用户认证 DAO 层? 5. JDBC 查询结果集处理的最佳模式是什么?</think>### 分析 `getAdminBynp` 方法的异常逻辑错误问题 #### 一、潜在异常分析 1. **空指针异常(NPE)** - 当 `DBUtil.getConnection()` 返回 `null` 时(如数据库连接池耗尽或配置错误),`conn.prepareStatement()` 会抛出 NPE[^1] - **解决方案**:显式空值检查 ```java Connection conn = DBUtil.getConnection(); if (conn == null) { throw new SQLException("数据库连接不可用"); } ``` 2. **SQL 执行异常** - 参数未正确设置(如 `name` 或 `password` 为 `null`) - SQL 语法错误(如表名/列名错误)[^1] - **解决方案**:参数校验 + 异常处理 ```java if (name == null || password == null) { throw new IllegalArgumentException("用户名密码不能为空"); } ``` 3. **资源泄漏风险** - 未正确关闭 `ResultSet`(引用[3]的查询示例未处理资源释放) - **解决方案**:使用 try-with-resources ```java try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery()) { // 处理结果 } ``` #### 二、逻辑错误分析 1. **结果集处理缺陷** ```java while (rs.next()) { // 实际只需第一条记录 a = new Admin(); // 每次循环覆盖对象 } ``` - 错误:当存在同名用户时,只返回最后一个结果 - **修复**:使用 `if` 替代 `while` ```java if (rs.next()) { Admin a = new Admin(); a.setId(rs.getInt("id")); // ...其他属性 } ``` 2. **密码安全漏洞** - 明文存储密码(引用[3]的 `Student` 表示例未涉及密码字段) - **修复**:使用 BCrypt 哈希存储 ```sql ALTER TABLE admin ADD COLUMN password_hash VARCHAR(60); ``` ```java // 服务层验证 if (BCrypt.checkpw(inputPassword, storedHash)) { // 认证通过 } ``` 3. **身份验证逻辑缺陷** - 直接通过用户名+密码查询存在 SQL 注入风险 - **修复**:参数化查询 + 服务层验证 ```java stmt.setString(1, name); // 使用PreparedStatement防止注入 ``` #### 三、完整修复代码 ```java public Admin getAdminBynp(String name, String password) throws SQLException { // 参数校验 if (name == null || password == null) { throw new IllegalArgumentException("参数不能为空"); } String sql = "SELECT id, name, password_hash FROM admin WHERE name = ?"; try (Connection conn = DBUtil.getConnection()) { if (conn == null) throw new SQLException("数据库连接失败"); try (PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, name); try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { String storedHash = rs.getString("password_hash"); if (BCrypt.checkpw(password, storedHash)) { Admin admin = new Admin(); admin.setId(rs.getInt("id")); admin.setName(rs.getString("name")); return admin; } } return null; // 认证失败 } } } catch (SQLException e) { logger.error("数据库查询失败: {}", e.getMessage()); throw e; } } ``` #### 四、防御性编程实践 1. **输入验证层** ```java // 在Service层添加 public Admin login(String name, String password) { if (!isValidName(name)) { // 添加命名规则检查 throw new AuthException("无效用户名"); } return adminDao.getAdminBynp(name, password); } ``` 2. **连接池监控** ```java // 在DBUtil中添加连接状态检查 public static boolean isConnectionHealthy() { try (Connection conn = getConnection()) { return conn.isValid(2); // 2秒超时检查 } catch (SQLException e) { return false; } } ``` 3. **安全增强** - 添加登录失败次数限制 - 使用预编译语句防止 SQL 注入 - 敏感数据返回前脱敏处理 > 关键改进点:通过参数化查询解决 SQL 注入风险,BCrypt 哈希提升密码安全性,try-with-resources 确保资源释放,分层验证保障业务逻辑正确性[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值