oracle数据库中is的使用说明,分享数据库工具类DatabaseUtils实现数据库登录验证isUrlValid、数据类型判断isOracle及连接关闭closeDbObject等常见操作...

一、前言

基于dbcon-5.0.1.jar.zip开源包的数据库工具类uk.ac.sanger.cgp.dbcon.util.DatabaseUtils对数据连接有效关闭closeDbObject、数据库类型判断isOracle、数据库账号登录验证isUrlValid及获取验证后有效连接配置getValidConnectionURL等,具体见如下源码说明。

二、源码说明

1.DatabaseUtils工具类package uk.ac.sanger.cgp.dbcon.util;@b@@b@import java.sql.Connection;@b@import java.sql.DatabaseMetaData;@b@import java.sql.DriverManager;@b@import java.sql.ResultSet;@b@import java.sql.SQLException;@b@import java.sql.Statement;@b@import org.apache.commons.logging.Log;@b@import org.apache.commons.logging.LogFactory;@b@import uk.ac.sanger.cgp.dbcon.config.Config;@b@import uk.ac.sanger.cgp.dbcon.exceptions.DbConException;@b@@b@public class DatabaseUtils@b@{@b@  private static final Log LOG = LogFactory.getLog(DatabaseUtils.class);@b@@b@  public static void connectionSettings(Connection conn)@b@  {@b@    try@b@    {@b@      conn.setAutoCommit(false);@b@      conn.rollback();@b@    }@b@    catch (SQLException e) {@b@      throw new DbConException("Error whilst setting autoCommit off and rollbacking the connection", e);@b@    }@b@  }@b@@b@  public static void closeDbObject(Connection conn)@b@  {@b@    if (conn != null)@b@      try {@b@        conn.rollback();@b@      }@b@      catch (SQLException e) {@b@        LOG.warn("Could not rollback java.sql.Connection", e);@b@      }@b@      finally {@b@        try {@b@          conn.close();@b@        }@b@        catch (SQLException e) {@b@          LOG.warn("Could not close java.sql.Connection", e);@b@        }@b@      }@b@  }@b@@b@  public static void closeDbObject(Statement st)@b@  {@b@    if (st != null)@b@      try {@b@        st.close();@b@      }@b@      catch (SQLException e) {@b@        LOG.warn("Could not close java.sql.Statement", e);@b@      }@b@  }@b@@b@  public static void closeDbObject(ResultSet rs)@b@  {@b@    if (rs != null)@b@      try {@b@        rs.close();@b@      }@b@      catch (SQLException e) {@b@        LOG.warn("Could not close java.sql.ResultSet", e);@b@      }@b@  }@b@@b@  public static boolean isOracle(Connection conn)@b@    throws SQLException@b@  {@b@    return conn.getMetaData().getDriverName().matches("^Oracle.+");@b@  }@b@@b@  public static boolean isFixRequiredForOracleVersion(Connection conn)@b@    throws SQLException@b@  {@b@    int majorVersion = conn.getMetaData().getDriverMajorVersion();@b@    if (majorVersion > 10) throw new DbConException("Unknown Oracle driver major version: cannot determine whether to apply V8 compatibility");@b@    return (majorVersion == 10);@b@  }@b@@b@  public static boolean setOraclePropertyForBugFix(Connection conn)@b@    throws SQLException@b@  {@b@    if ((!(Boolean.getBoolean("oracle.jdbc.V8Compatible"))) && (isOracle(conn)) && (isFixRequiredForOracleVersion(conn)))@b@    {@b@      System.setProperty("oracle.jdbc.V8Compatible", "true");@b@      return true;@b@    }@b@@b@    return false;@b@  }@b@@b@  public static String getValidConnectionURL(Config config)@b@    throws DbConException@b@  {@b@    String validUrl = "";@b@@b@    if (isUrlValid(config.getUrl(), config.getUsername(), config.getPassword(), config.getDriver())) {@b@      validUrl = config.getUrl();@b@    }@b@    else if ((config.getBackupUrl() != null) && (isUrlValid(config.getBackupUrl(), config.getUsername(), config.getPassword(), config.getDriver()))) {@b@      validUrl = config.getBackupUrl();@b@    }@b@    else {@b@      throw new DbConException("No valid URL could be found for DB " + config.getName());@b@    }@b@@b@    config.setWorkingUrl(validUrl);@b@@b@    return validUrl;@b@  }@b@@b@  public static boolean isUrlValid(String url, String username, String password, String driver)@b@  {@b@    boolean okay = false;@b@    Connection conn = null;@b@    try {@b@      Class.forName(driver);@b@      conn = DriverManager.getConnection(url, username, password);@b@      okay = true;@b@    }@b@    catch (ClassNotFoundException e) {@b@      if (LOG.isDebugEnabled()) LOG.debug("Cannot find class", e);@b@      okay = false;@b@    }@b@    catch (SQLException e) {@b@      if (LOG.isDebugEnabled()) LOG.debug("SQLException issue", e);@b@      okay = false;@b@    }@b@    finally {@b@      closeDbObject(conn);@b@    }@b@@b@    return okay;@b@  }@b@}

2.Config数据连接配置类package uk.ac.sanger.cgp.dbcon.config;@b@@b@import java.io.Serializable;@b@@b@public class Config@b@  implements Serializable, Cloneable@b@{@b@  private static final long serialVersionUID = 8255104234840048031L;@b@  public static final byte FAIL = 0;@b@  public static final byte BLOCK = 1;@b@  public static final byte GROW = 2;@b@  private int maxActive = 0;@b@  private int maxIdle = 0;@b@  private int numTestsPerEvictionRun = 0;@b@  private byte exhausted = 0;@b@  private long maxWait = 0L;@b@  private long timeBetweenEvictRun = 0L;@b@  private long minEvictTime = 0L;@b@  private int cachedPreparedStatements = 0;@b@  private boolean testOnBorrow = false;@b@  private boolean testOnReturn = false;@b@  private boolean testWhileIdle = false;@b@  private String url = null;@b@  private String backupUrl = null;@b@  private String username = null;@b@  private String password = null;@b@  private String name = null;@b@  private String validationQuery = null;@b@  private String driver = null;@b@  private String workingUrl = null;@b@@b@  private Config()@b@  {@b@  }@b@@b@  public static Config createEmptyConfig()@b@  {@b@    return new Config();@b@  }@b@@b@  public static Config createBasicPoolConfig(int maxActive, byte exhausted, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictRun, int numTestsPerEvictionRun, long minEvictTime, boolean testWhileIdle, String url, String username, String password, String name, String validationQuery, String driver, int cachedPreparedStatements)@b@  {@b@    Config config = new Config(maxActive, exhausted, maxWait, maxIdle, testOnBorrow, testOnReturn, timeBetweenEvictRun, numTestsPerEvictionRun, minEvictTime, testWhileIdle, url, username, password, name, validationQuery, driver, null, cachedPreparedStatements);@b@@b@    return config;@b@  }@b@@b@  public static Config createBasicPoolConfigWithBackup(int maxActive, byte exhausted, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictRun, int numTestsPerEvictionRun, long minEvictTime, boolean testWhileIdle, String url, String username, String password, String name, String validationQuery, String driver, String backupUrl, int cachedPreparedStatements)@b@  {@b@    Config config = new Config(maxActive, exhausted, maxWait, maxIdle, testOnBorrow, testOnReturn, timeBetweenEvictRun, numTestsPerEvictionRun, minEvictTime, testWhileIdle, url, username, password, name, validationQuery, driver, backupUrl, cachedPreparedStatements);@b@@b@    return config;@b@  }@b@@b@  private Config(int maxActive, byte exhausted, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictRun, int numTestsPerEvictionRun, long minEvictTime, boolean testWhileIdle, String url, String username, String password, String name, String validationQuery, String driver, String backupUrl, int cachedPreparedStatements)@b@  {@b@    this.maxActive = maxActive;@b@    this.exhausted = exhausted;@b@    this.maxWait = maxWait;@b@    this.maxIdle = maxIdle;@b@    this.testOnBorrow = testOnBorrow;@b@    this.testOnReturn = testOnReturn;@b@    this.timeBetweenEvictRun = timeBetweenEvictRun;@b@    this.numTestsPerEvictionRun = numTestsPerEvictionRun;@b@    this.minEvictTime = minEvictTime;@b@    this.testWhileIdle = testWhileIdle;@b@    this.url = url;@b@    this.username = username;@b@    this.password = password;@b@    this.name = name;@b@    this.validationQuery = validationQuery;@b@    this.driver = driver;@b@    this.backupUrl = backupUrl;@b@    this.cachedPreparedStatements = cachedPreparedStatements;@b@  }@b@@b@  public Object clone()@b@  {@b@    Config clone = new Config(this.maxActive, this.exhausted, this.maxWait, this.maxIdle, this.testOnBorrow, this.testOnReturn, this.timeBetweenEvictRun, this.numTestsPerEvictionRun, this.minEvictTime, this.testWhileIdle, this.url, this.username, this.password, this.name, this.validationQuery, this.driver, this.backupUrl, this.cachedPreparedStatements);@b@@b@    return clone;@b@  }@b@@b@  public int getMaxActive() {@b@    return this.maxActive;@b@  }@b@@b@  public void setMaxActive(int maxActive) {@b@    this.maxActive = maxActive;@b@  }@b@@b@  public byte getExhausted() {@b@    return this.exhausted;@b@  }@b@@b@  public void setExhausted(byte exhausted) {@b@    this.exhausted = exhausted;@b@  }@b@@b@  public long getMaxWait() {@b@    return this.maxWait;@b@  }@b@@b@  public void setMaxWait(long maxWait) {@b@    this.maxWait = maxWait;@b@  }@b@@b@  public int getMaxIdle() {@b@    return this.maxIdle;@b@  }@b@@b@  public void setMaxIdle(int maxIdle) {@b@    this.maxIdle = maxIdle;@b@  }@b@@b@  public boolean isTestOnBorrow() {@b@    return this.testOnBorrow;@b@  }@b@@b@  public void setTestOnBorrow(boolean testOnBorrow) {@b@    this.testOnBorrow = testOnBorrow;@b@  }@b@@b@  public boolean isTestOnReturn() {@b@    return this.testOnReturn;@b@  }@b@@b@  public void setTestOnReturn(boolean testOnReturn) {@b@    this.testOnReturn = testOnReturn;@b@  }@b@@b@  public long getTimeBetweenEvictRun() {@b@    return this.timeBetweenEvictRun;@b@  }@b@@b@  public void setTimeBetweenEvictRun(long timeBetweenEvictRun) {@b@    this.timeBetweenEvictRun = timeBetweenEvictRun;@b@  }@b@@b@  public int getNumTestsPerEvictionRun() {@b@    return this.numTestsPerEvictionRun;@b@  }@b@@b@  public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {@b@    this.numTestsPerEvictionRun = numTestsPerEvictionRun;@b@  }@b@@b@  public void setMinEvictTime(long minEvictTime) {@b@    this.minEvictTime = minEvictTime;@b@  }@b@@b@  public long getMinEvictTime() {@b@    return this.minEvictTime;@b@  }@b@@b@  public boolean isTestWhileIdle() {@b@    return this.testWhileIdle;@b@  }@b@@b@  public void setTestWhileIdle(boolean testWhileIdle) {@b@    this.testWhileIdle = testWhileIdle;@b@  }@b@@b@  public String getUrl() {@b@    return this.url;@b@  }@b@@b@  public void setUrl(String url) {@b@    this.url = url;@b@  }@b@@b@  public String getName() {@b@    return this.name;@b@  }@b@@b@  public void setName(String name) {@b@    this.name = name;@b@  }@b@@b@  public String getValidationQuery() {@b@    return this.validationQuery;@b@  }@b@@b@  public void setValidationQuery(String validationQuery) {@b@    this.validationQuery = validationQuery;@b@  }@b@@b@  public String getDriver() {@b@    return this.driver;@b@  }@b@@b@  public void setDriver(String driver) {@b@    this.driver = driver;@b@  }@b@@b@  public String getUsername() {@b@    return this.username;@b@  }@b@@b@  public void setUsername(String username) {@b@    this.username = username;@b@  }@b@@b@  public String getPassword() {@b@    return this.password;@b@  }@b@@b@  public void setPassword(String password) {@b@    this.password = password;@b@  }@b@@b@  public String getBackupUrl() {@b@    return this.backupUrl;@b@  }@b@@b@  public void setBackupUrl(String backupUrl) {@b@    this.backupUrl = backupUrl;@b@  }@b@@b@  public String getWorkingUrl() {@b@    return this.workingUrl;@b@  }@b@@b@  public void setWorkingUrl(String workingUrl) {@b@    this.workingUrl = workingUrl;@b@  }@b@@b@  public int getCachedPreparedStatements() {@b@    return this.cachedPreparedStatements;@b@  }@b@@b@  public void setCachedPreparedStatements(int cachedPreparedStatements) {@b@    this.cachedPreparedStatements = cachedPreparedStatements;@b@  }@b@@b@  public String toString()@b@  {@b@    StringBuffer sb = new StringBuffer();@b@@b@    sb.append("name: " + this.name + "\n");@b@    sb.append("driver: " + this.driver + "\n");@b@    sb.append("url: " + this.url + "\n");@b@    sb.append("backupUrl: " + this.backupUrl + "\n");@b@    sb.append("workingUrl: " + this.workingUrl + "\n");@b@    sb.append("username: " + this.username + "\n");@b@    sb.append("password: " + this.password + "\n");@b@    sb.append("validationQuery: " + this.validationQuery + "\n");@b@    sb.append("maxActive: " + this.maxActive + "\n");@b@    sb.append("exhausted: " + this.exhausted + "\n");@b@    sb.append("maxWait: " + this.maxWait + "\n");@b@    sb.append("maxIdle: " + this.maxIdle + "\n");@b@    sb.append("testOnBorrow: " + this.testOnBorrow + "\n");@b@    sb.append("testOnReturn: " + this.testOnReturn + "\n");@b@    sb.append("timeBetweenEvictRun: " + this.timeBetweenEvictRun + "\n");@b@    sb.append("numTestsPerEvictionRun: " + this.numTestsPerEvictionRun + "\n");@b@    sb.append("minEvictTime: " + this.minEvictTime + "\n");@b@    sb.append("testWhileIdle: " + this.testWhileIdle + "\n");@b@    sb.append("cachedPreparedStatements: " + this.cachedPreparedStatements + "\n");@b@@b@    return sb.toString();@b@  }@b@}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.hexiang.utils; import java.sql.*; import java.util.*; /** * * Title: 数据库工具类 * * * Description: 将大部分的数据库操作放入这个类, 包括数据库连接的建立, 自动释放等. * * * @author beansoft 日期: 2004年04月 * @version 2.0 */ public class DatabaseUtil { /** 数据库连接 */ private java.sql.Connection connection; /** * All database resources created by this class, should be free after all * operations, holds: ResultSet, Statement, PreparedStatement, etc. */ private ArrayList resourcesList = new ArrayList(5); public DatabaseUtil() { } /** 关闭数据库连接并释放所有数据库资源 */ public void close() { closeAllResources(); close(getConnection()); } /** * Close given connection. * * @param connection * Connection */ public static void close(Connection connection) { try { connection.close(); } catch (Exception ex) { System.err.println("Exception when close a connection: " + ex.getMessage()); } } /** * Close all resources created by this class. */ public void closeAllResources() { for (int i = 0; i < this.getResourcesList().size(); i++) { closeJDBCResource(getResourcesList().get(i)); } } /** * Close a jdbc resource, such as ResultSet, Statement, Connection.... All * these objects must have a method signature is void close(). * * @param resource - * jdbc resouce to close */ public void closeJDBCResource(Object resource) { try { Class clazz = resource.getClass(); java.lang.reflect.Method method = clazz.getMethod("close", null); method.invoke(resource, null); } catch (Exception e) { // e.printStackTrace(); } } /** * 执行 SELECT 等 SQL 语句并返回结果集. * * @param sql * 需要发送到数据库 SQL 语句 * @return a ResultSet object that contains the data produced * by the given query; never null */ public ResultSet executeQuery(String sql) { try { Statement statement = getStatement(); ResultSet rs = statement.executeQuery(sql); this.getResourcesList().add(rs); this.getResourcesList().add(statement);// BUG fix at 2006-04-29 by BeanSoft, added this to res list // MySql 数据库要求必需关闭 statement 对象, 否则释放不掉资源 // - 此观点错误, 因为关闭此对象后有时数据无法读出 //statement.close(); return rs; } catch (Exception ex) { System.out.println("Error in executeQuery(\"" + sql + "\"):" + ex); // ex.printStackTrace(); return null; } } /** * Executes the given SQL statement, which may be an INSERT, * UPDATE, or DELETE statement or an SQL * statement that returns nothing, such as an SQL DDL statement. 执行给定的 SQL * 语句, 这些语句可能是 INSERT, UPDATE 或者 DELETE 语句, 或者是一个不返回任何东西的 SQL 语句, 例如一个 SQL * DDL 语句. * * @param sql * an SQL INSERT,UPDATE or * DELETE statement or an SQL statement that * returns nothing * @return either the row count for INSERT, * UPDATE or DELETE statements, or * 0 for SQL statements that return nothing */ public int executeUpdate(String sql) { try { Statement statement = getStatement(); return statement.executeUpdate(sql); // MySql 数据库要求必需关闭 statement 对象, 否则释放不掉资源 // - 此观点错误, 因为关闭此对象后有时数据无法读出 //statement.close(); } catch (Exception ex) { System.out.println("Error in executeUpdate(): " + sql + " " + ex); //System.out.println("executeUpdate:" + sql); ex.printStackTrace(); } return -1; } /** * 返回记录总数, 使用方法: getAllCount("SELECT count(ID) from tableName") 2004-06-09 * 可滚动的 Statement 不能执行 SELECT MAX(ID) 之类的查询语句(SQLServer 2000) * * @param sql * 需要执行的 SQL * @return 记录总数 */ public int getAllCount(String sql) { try { Statement statement = getConnection().createStatement(); this.getResourcesList().add(statement); ResultSet rs = statement.executeQuery(sql); rs.next(); int cnt = rs.getInt(1); rs.close(); try { statement.close(); this.getResourcesList().remove(statement); } catch (Exception ex) { ex.printStackTrace(); } return cnt; } catch (Exception ex) { System.out.println("Exception in DatabaseUtil.getAllCount(" + sql + "):" + ex); ex.printStackTrace(); return 0; } } /** * 返回当前数据库连接. */ public java.sql.Connection getConnection() { return connection; } /** * 连接新的数据库对象到这个工具类, 首先尝试关闭连接. */ public void setConnection(java.sql.Connection connection) { if (this.connection != null) { try { getConnection().close(); } catch (Exception ex) { } } this.connection = connection; } /** * Create a common statement from the database connection and return it. * * @return Statement */ public Statement getStatement() { // 首先尝试获取可滚动的 Statement, 然后才是普通 Statement Statement updatableStmt = getUpdatableStatement(); if (updatableStmt != null) return updatableStmt; try { Statement statement = getConnection().createStatement(); this.getResourcesList().add(statement); return statement; } catch (Exception ex) { System.out.println("Error in getStatement(): " + ex); } return null; } /** * Create a updatable and scrollable statement from the database connection * and return it. * * @return Statement */ public Statement getUpdatableStatement() { try { Statement statement = getConnection() .createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); this.getResourcesList().add(statement); return statement; } catch (Exception ex) { System.out.println("Error in getUpdatableStatement(): " + ex); } return null; } /** * Create a prepared statement and return it. * * @param sql * String SQL to prepare * @throws SQLException * any database exception * @return PreparedStatement the prepared statement */ public PreparedStatement getPreparedStatement(String sql) throws SQLException { try { PreparedStatement preparedStatement = getConnection() .prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); this.getResourcesList().add(preparedStatement); return preparedStatement; } catch (Exception ex) { ex.printStackTrace(); } return null; } /** * Return the resources list of this class. * * @return ArrayList the resources list */ public ArrayList getResourcesList() { return resourcesList; } /** * Fetch a string from the result set, and avoid return a null string. * * @param rs * the ResultSet * @param columnName * the column name * @return the fetched string */ public static String getString(ResultSet rs, String columnName) { try { String result = rs.getString(columnName); if (result == null) { result = ""; } return result; } catch (Exception ex) { } return ""; } /** * Get all the column labels * * @param resultSet * ResultSet * @return String[] */ public static String[] getColumns(ResultSet resultSet) { if (resultSet == null) { return null; } try { ResultSetMetaData metaData = resultSet.getMetaData(); int numberOfColumns = metaData.getColumnCount(); if (numberOfColumns <= 0) { return null; } String[] columns = new String[numberOfColumns]; //System.err.println("numberOfColumns=" + numberOfColumns); // Get the column names for (int column = 0; column < numberOfColumns; column++) { // System.out.print(metaData.getColumnLabel(column + 1) + "\t"); columns[column] = metaData.getColumnName(column + 1); } return columns; } catch (Exception ex) { ex.printStackTrace(); } return null; } /** * Get the row count of the result set. * * @param resultset * ResultSet * @throws SQLException * if a database access error occurs or the result set type is * TYPE_FORWARD_ONLY * @return int the row count * @since 1.2 */ public static int getRowCount(ResultSet resultset) throws SQLException { int row = 0; try { int currentRow = resultset.getRow(); // Remember old row position resultset.last(); row = resultset.getRow(); if (currentRow > 0) { resultset.absolute(row); } } catch (Exception ex) { ex.printStackTrace(); } return row; } /** * Get the column count of the result set. * * @param resultSet * ResultSet * @return int the column count */ public static int getColumnCount(ResultSet resultSet) { if (resultSet == null) { return 0; } try { ResultSetMetaData metaData = resultSet.getMetaData(); int numberOfColumns = metaData.getColumnCount(); return numberOfColumns; } catch (Exception ex) { ex.printStackTrace(); } return 0; } /** * Read one row's data from result set automatically and put the result it a * hashtable. Stored as "columnName" = "value", where value is converted to * String. * * @param resultSet * ResultSet * @return Hashtable */ public static final Hashtable readResultToHashtable(ResultSet resultSet) { if (resultSet == null) { return null; } Hashtable resultHash = new Hashtable(); try { String[] columns = getColumns(resultSet); if (columns != null) { // Read data column by column for (int i = 0; i < columns.length; i++) { resultHash.put(columns[i], getString(resultSet, columns[i])); } } } catch (Exception ex) { ex.printStackTrace(); } return resultHash; } /** * Read data from result set automatically and put the result it a * hashtable. Stored as "columnName" = "value", where value is converted to * String. * * Note: assume the default database string encoding is ISO8859-1. * * @param resultSet * ResultSet * @return Hashtable */ @SuppressWarnings("unchecked") public static final Hashtable readResultToHashtableISO(ResultSet resultSet) { if (resultSet == null) { return null; } Hashtable resultHash = new Hashtable(); try { String[] columns = getColumns(resultSet); if (columns != null) { // Read data column by column for (int i = 0; i < columns.length; i++) { String isoString = getString(resultSet, columns[i]); try { resultHash.put(columns[i], new String(isoString .getBytes("ISO8859-1"), "GBK")); } catch (Exception ex) { resultHash.put(columns[i], isoString); } } } } catch (Exception ex) { ex.printStackTrace(); } return resultHash; } /** Test this class. */ public static void main(String[] args) throws Exception { DatabaseUtil util = new DatabaseUtil(); // TODO: 从连接池工厂获取连接 // util.setConnection(ConnectionFactory.getConnection()); ResultSet rs = util.executeQuery("SELECT * FROM e_hyx_trans_info"); while (rs.next()) { Hashtable hash = readResultToHashtableISO(rs); Enumeration keys = hash.keys(); while (keys.hasMoreElements()) { Object key = keys.nextElement(); System.out.println(key + "=" + hash.get(key)); } } rs.close(); util.close(); } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值