JDBC

本文详细介绍了JDBC的概念、本质及快速入门指南,涵盖DriverManager、Connection、Statement、ResultSet和PreparedStatement等核心类的使用方法,以及如何利用JDBC进行用户登录验证和事务管理。

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

1. JDBC 的概念

JDBC(Java DataBase Connectivity),Java 数据库连接。就是用 Java 语言来操作数据库。

2. JDBC 的本质

在这里插入图片描述
市面上有很多不同厂商的关系型数据库,对于程序员来说学习成本太高,大家期望使用统一的一套 Java 代码可以操作所有的关系型数据库,于是 sun 公司定义的一套操作所有关系型数据库的规则(接口),这就是 JDBC。各个数据库厂商去实现这套接口,提供数据库驱动 jar 包。我们可以使用这套接口来编程,真正执行的代码是驱动 jar 包中的实现类。

3. JDBC 快速入门

  1. JDBC 使用步骤

    1. 导入驱动 jar 包
      1. 复制 mysql-connector-java-5.1.37-bin.jar 到项目的 libs 目录下
      2. 右键 --> Add As Library
    2. 注册驱动
    3. 获取数据库连接对象 Connection
    4. 定义 sql
    5. 获取执行 sql 语句的对象 Statement
    6. 执行 sql,接受返回结果
    7. 处理结果
    8. 释放资源
  2. 编码实现

    package com.zt.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;
    
    public class JdbcDemo1 {
        public static final String URL = "jdbc:mysql://localhost:3306/test1";
        public static final String USER = "root";
        public static final String PASSWORD = "123456";
    
        public static void main(String[] args) throws Exception {
            // 1.导入驱动 jar 包
            // 2.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 3.获取数据库连接对象 Connection
            Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
            // 4.定义 sql 语句
            String sql = "select * from stu";
            // 5.获取执行 sql 的对象 Statement
            Statement statement = connection.createStatement();
            // 6.执行 sql,接受返回结果
            ResultSet resultSet = statement.executeQuery(sql);
            // 7.处理结果
            while (resultSet.next()){
                System.out.println(resultSet.getString(1)+" "+resultSet.getString(2));
            }
            // 8.释放资源
            statement.close();
            connection.close();
        }
    }
    
    

4. JDBC 各类详解

4.1 DriverManager:驱动管理对象

DriverManager 的功能:

  1. 注册驱动:告诉程序该使用哪一个数据库驱动 jar 包

    方法:

    // 注册与给定的驱动程序 DriverManager 
    static void registerDriver(Driver driver) 
    

    我们在上面注册驱动使用的代码为:

    Class.forName("com.mysql.jdbc.Driver");
    

    于是查看 com.mysql.jdbc.Driver 的源码发现存在静态代码块使用了 registerDriver(Driver driver) 方法

    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    

    注意:mysql5之后的驱动 jar 包可以省略注册驱动的步骤。

  2. 获取数据库连接

    方法:

    // 试图建立到给定数据库 URL 的连接
    static Connection getConnection(String url, String user, String password) 
    

    参数:

    • url:指定连接的路径

      语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
      例子:jdbc:mysql://localhost:3306/test1
      细节:如果连接的是本机 mysql 服务器,并且 mysql 服务默认端口是 3306,则 url 可以简写为:
      jdbc:mysql:///数据库名称
      
    • user:用户名

    • password:密码

4.2 Connection:数据库连接对象

Connection 的功能:

  1. 获取执行 sql 的对象

    方法:

    // 创建一个 Statement 对象来将 SQL 语句发送到数据库
    Statement createStatement()
    // 创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库
    PreparedStatement prepareStatement(String sql)
    
  2. 管理事务

    1. 开启事务

      方法:

      // 调用该方法设置参数为 false,即开启事务
      void setAutoCommit(boolean autoCommit) 
      
    2. 提交事务

      方法:

      void commit() 
      
    3. 回滚事务

      方法:

      void rollback() 
      

4.3 Statement:执行 sql 的对象

Statement 的功能:

  1. 执行 sql

    方法:

    // 可以执行任意的 sql(不常用) 
    boolean execute(String sql)
    // 执行 DML(insert、update、delete)语句、DDL(create,alter、drop)语句
    // 返回值 int:影响的行数,可以通过这个影响的行数判断 DML 语句是否执行成功 返回值 >0 的则执行成功,反之,则失败。
    int executeUpdate(String sql)
    // 执行 DQL(select)语句
    ResultSet executeQuery(String sql)
    

Statement 练习

  1. 需求

    stu 表增加一条记录

  2. 编码实现

    package com.zt.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    /**
     * stu 表增加一条记录
     */
    public class JDBCDemo2 {
        public static final String URL = "jdbc:mysql://localhost:3306/test1";
        public static final String USER = "root";
        public static final String PASSWORD = "123456";
    
        public static void main(String[] args) {
            Connection connection = null;
            Statement statement = null;
            try {
                // 1. 注册驱动
                Class.forName("com.mysql.jdbc.Driver");
                // 2.获取数据库连接对象
                connection = DriverManager.getConnection(URL,USER,PASSWORD);
                // 3.定义 sql 语句
                String sql = "insert into stu values(null,'王五',20,1)";
                // 4.获取执行 sql 的对象 Statement
                statement = connection.createStatement();
                // 5.执行sql
                int i = statement.executeUpdate(sql);
                // 6.处理结果
                if(i > 0){
                    System.out.println("添加成功");
                }else {
                    System.out.println("添加失败");
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // 释放资源
                if (connection != null){ // 避免空指针异常
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
    
                if (statement != null){
                    try {
                        statement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
    
            }
    
        }
    }
    
    

4.4 ResultSet:结果集对象,封装查询结果

ResultSet 的功能:

  1. 移动游标

    方法:

    // 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回 false,如果不是则返回 true
    boolean next()
    
  2. 获取数据

    方法:

    // Xxx:代表数据类型 如: int getInt() ,	String getString()
    Xxx getXxx(参数)
    

    参数:

    1. int:代表列的编号,从1开始 如:getString(1)
    2. String:代表列名称。 如:getInt(“id”)

ResultSet 的练习:

  1. 需求

    定义一个方法,查询 stu 表的数据将其封装为对象,然后装载集合,返回集合。

  2. 编码实现

    1. 定义一个 Student 类

      package com.zt.domain;
      
      /**
       * 封装 Stu 表数据的 JavaBean
       */
      public class Student {
          private int id;
          private String name;
          private int age;
          private int class_id;
      
          public Student(int id, String name, int age, int class_id) {
              this.id = id;
              this.name = name;
              this.age = age;
              this.class_id = class_id;
          }
      
          public int getId() {
              return id;
          }
      
          public void setId(int id) {
              this.id = id;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public int getClass_id() {
              return class_id;
          }
      
          public void setClass_id(int class_id) {
              this.class_id = class_id;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", age=" + age +
                      ", class_id=" + class_id +
                      '}';
          }
      }
      
      
    2. 定义一个方法,查询 stu 表的数据将其封装为对象,然后装载集合,返回集合。

      package com.zt.jdbc;
      
      import com.zt.domain.Student;
      
      import java.sql.*;
      import java.util.ArrayList;
      import java.util.List;
      
      public class JDBCDemo3 {
          /**
           * 定义一个方法,查询 stu 表的数据将其封装为对象,然后装载集合,返回集合。
           */
          public List<Student> getAllStudent(){
              List<Student> students = new ArrayList<Student>();
              Connection connection = null;
              Statement statement = null;
              ResultSet resultSet = null;
              try {
                  // 1.注册驱动
                  Class.forName("com.mysql.jdbc.Driver");
                  // 2.获取连接
                  connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1", "root", "123456");
                  // 3.定义 sql 语句
                  String sql = "select * from stu";
                  // 4.获取执行 sql 的对象
                  statement = connection.createStatement();
                  // 5.执行 sql
                  resultSet = statement.executeQuery(sql);
                  // 6.处理结果集
                  // 遍历结果集,封装对象,装载集合
                  Student student = null;
                  while (resultSet.next()){
                      int id = resultSet.getInt("id");
                      String name = resultSet.getString("name");
                      int age = resultSet.getInt("age");
                      int class_id = resultSet.getInt("class_id");
                      student = new Student(id,name,age,class_id);
                      students.add(student);
                  }
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  if(connection != null){
                      try {
                          connection.close();
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
      
                  if(statement != null){
                      try {
                          statement.close();
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
      
                  if(resultSet != null){
                      try {
                          resultSet.close();
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
              }
              return students;
          }
      }
      
      
    3. 单元测试

      package com.zt.test;
      
      import com.zt.domain.Student;
      import com.zt.jdbc.JDBCDemo3;
      import org.junit.Test;
      
      import java.util.List;
      
      public class JDBCDemo3Test {
          @Test
          public void testGetAllStudent(){
              JDBCDemo3 test = new JDBCDemo3();
              List<Student> allStudent = test.getAllStudent();
              for (Student student : allStudent) {
                  System.out.println(student);
              }
      
          }
      }
      
      
  3. 执行结果

    Student{id=1, name='张三', age=18, class_id=1}
    Student{id=2, name='李四', age=19, class_id=2}
    Student{id=3, name='王五', age=20, class_id=1}
    
    Process finished with exit code 0
    

4.5 PreparedStatement:执行 sql 的对象

  1. SQL 注入问题

    在用户登录方法中,如果采用 statement 来作为执行 sql 的对象,那么要定义的 sql 语句如下:

    String sql = "select * from user where username = '"+username+"'and password ='"+password+"'";
    

    此时用户输入:

    用户名:
    fhdsjkf
    密码:
    a' or 'a' = 'a
    

    结果却登录成功了!

    这是因为在拼接 sql 时,有一些 sql 的特殊关键字参与字符串的拼接,这就是SQL 注入

    我们分析一下传入参数后的 sql 语句:

    select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a' ;
    -- 等效于,相当于没过滤条件
    select * from user where 'a' = 'a' ;
    
  2. 解决 SQL 注入问题

    使用 PreparedStatement 对象可以解决这个问题。

    PreparedStatement 是怎样预防 SQL 注入的呢?

    数据库在处理 SQL 语句时都有一个预编译的过程,而 PreparedStatement 就是把一些格式固定的 SQL 编译后,存放在内存池中即数据库缓冲池,当我们再次执行相同的 SQL 语句时就不需要预编译的过程了,所以即使 SQL 注入特殊的语句,也会只当做参数传进去,不会当做指令执行。

  3. PreparedStatement 的使用步骤

    1. 导入驱动 jar 包

    2. 注册驱动

    3. 获取连接对象 Connection

    4. 定义 sql 语句(sql 的参数使用 ? 作为占位符。)如:

      String sql = "select * from user where username = ? and password =?";
      
    5. 获取执行 sql 的对象 PreparedStatement

      方法:

      PreparedStatement prepareStatement(String sql)
      
    6. 给占位符赋值

      方法:

      setXxx(参数1,参数2)
      

      参数:

      • 参数1:?的位置编号从 1 开始
      • 参数2:?的值
    7. 执行 sql

    8. 处理结果

    9. 释放资源

  4. PreparedStatement 的好处

    1. 可以防止 SQL 注入
    2. 效率更高

5. JDBC 工具类:JDBCUtils

  1. JDBCUtils 的作用

    提供连接对象,释放资源等方法,简化 JDBC 的使用

  2. JDBCUtils 的功能

    1. 获取连接对象方法 getConnection()

      需求:不想传递参数,还得保证工具类的通用性。

      解决方式:把参数写在配置文件中,再读取配置文件

    2. 释放资源方法

  3. 编码实现

    jdbc.properties:

    url=jdbc:mysql://localhost:3306/test1
    user=root
    password=123456
    driver=com.mysql.jdbc.Driver
    

    JDBCUtils:

    package com.zt.util;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.*;
    import java.util.Properties;
    
    public class JDBCUtils {
        private static String url;
        private static String user;
        private static String password;
        private static String driver;
    
        /**
         * 从 jdbc.properties 文件的读取这些值,只需要读取一次即可拿到,所以使用静态代码块。
         * 注意:静态代码块不能抛出异常,只能捕获
         */
        static {
            try {
                // 1.创建 Properties 集合类
                Properties properties = new Properties();
                // 2.通过类加载器获取 class 目录下 jdbc.properties 文件的路径
                ClassLoader classLoader = JDBCUtils.class.getClassLoader();
                InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc.properties");
                // 3.加载 jdbc.properties 文件到 properties 集合中
                properties.load(resourceAsStream);
                // 4.读取 jdbc.properties 文件内容,给变量赋值
                url = properties.getProperty("url");
                user = properties.getProperty("user");
                password = properties.getProperty("password");
                driver = properties.getProperty("driver");
                // 5.注册驱动
                Class.forName(driver);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    
        /**
         * 获取连接对象
         * @return 连接对象
         */
        public static Connection getConnection() throws SQLException {
            return DriverManager.getConnection(url,user,password);
        }
    
        /**
         * 释放资源(注意:释放资源是按从小到大的顺序)
         * @param statement
         * @param connection
         */
        public static void close(Statement statement,Connection connection){
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if(connection != null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 释放资源
         * @param resultSet
         * @param statement
         * @param connection
         */
        public static void close(ResultSet resultSet,Statement statement,Connection connection){
            if(resultSet != null){
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if(connection != null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
    }
    
    

6. 用户登录练习

  1. 需求分析

    1. 用户可以通过键盘输入用户名和密码来登陆
    2. 判断用户是否登录成功,并返回提示信息
  2. 编码实现

    1. 创建 user 表

      CREATE TABLE USER(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	username VARCHAR(32),
      	password VARCHAR(32)
      );
      
      INSERT INTO USER VALUES(NULL,'张三','123');
      INSERT INTO USER VALUES(NULL,'李四','321');
      
    2. 编写登陆类

      package com.zt.jdbc;
      
      import com.zt.util.JDBCUtils;
      
      import java.sql.*;
      import java.util.Scanner;
      
      public class JDBCDemo6 {
          public static void main(String[] args) {
              // 用户输入用户名和密码
              Scanner scanner = new Scanner(System.in);
              System.out.println("用户名:");
              String username = scanner.nextLine();
              System.out.println("密码:");
              String password = scanner.nextLine();
              // 调用登陆方法
              boolean login = new JDBCDemo6().login(username, password);
              // 判断登陆是否成功
              if(login){
                  System.out.println("登陆成功");
              }else {
                  System.out.println("登陆失败");
              }
      
          }
          /**
           * 登录方法
           * @param username
           * @param password
           * @return 成功返回 true,失败返回 false
           */
          public boolean login(String username,String password){
              // 如果用户名密码为空,返回 false
              if(username == null || password == null){
                  return false;
              }
              Connection connection = null;
              PreparedStatement preparedStatement = null;
              ResultSet resultSet = null;
              try {
                  // 1.获取连接对象
                  connection = JDBCUtils.getConnection();
                  // 2.定义 sql 语句
                  String sql = "select * from user where username = ? and password =?";
                  // 3.获取执行 sql 的对象
                  preparedStatement = connection.prepareStatement(sql);
                  // 4.给占位符赋值
                  preparedStatement.setString(1,username);
                  preparedStatement.setString(2,password);
                  // 5.执行 sql
                  resultSet = preparedStatement.executeQuery();
                  // 6.如果有下一行,则返回 true,没有返回 false
                  return resultSet.next();
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  JDBCUtils.close(resultSet,preparedStatement,connection);
              }
              return false;
          }
      }
      
      

7. JDBC 控制事务

  1. 事务的概念

    一个包含多个步骤的业务操作,如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

  2. 事务的操作(使用 Connection 对象来管理事务)

    1. 开启事务

      // 调用该方法设置参数为 false,即开启事务
      void setAutoCommit(boolean autoCommit) 
      
    2. 提交事务

      // 在执行sql之前开启事务
      void commit() 
      
    3. 回滚事务

      // 在 catch 中回滚事务
      void rollback() 
      
  3. 事务的练习

    1. 需求

      用事务管理实现张三向李四转账 500 元

    2. 编码实现

      package com.zt.jdbc;
      
      import com.zt.util.JDBCUtils;
      
      import java.sql.Connection;
      import java.sql.PreparedStatement;
      import java.sql.SQLException;
      
      public class JDBCDemo7 {
          public static void main(String[] args) {
              Connection connection = null;
              PreparedStatement preparedStatement1 = null;
              PreparedStatement preparedStatement2 = null;
              try {
                  // 1.获取连接
                  connection = JDBCUtils.getConnection();
                  // 2.开启事务
                  connection.setAutoCommit(false);
                  // 3.定义 sql
                  String sql1 = "update account set balance = balance - ? where id = ?";
                  String sql2 = "update account set balance = balance + ? where id = ?";
                  // 4.获取执行 sql 对象
                  preparedStatement1 = connection.prepareStatement(sql1);
                  preparedStatement2 = connection.prepareStatement(sql2);
                  // 5.给占位符赋值
                  preparedStatement1.setInt(1,500);
                  preparedStatement1.setInt(2,1);
                  preparedStatement2.setInt(1,500);
                  preparedStatement2.setInt(2,2);
                  // 6.执行 sql
                  preparedStatement1.executeUpdate();
                  preparedStatement2.executeUpdate();
                  // 7.提交事务
                  connection.commit();
              } catch (Exception e) { // 此处异常捕获一个大的
                  // 8.出现异常,事务回滚
                  try {
                      // 保证连接不为空才回滚
                      if(connection != null){
                          connection.rollback();
                      }
                  } catch (SQLException e1) {
                      e1.printStackTrace();
                  }
                  e.printStackTrace();
              } finally {
                  // 9.释放资源
                  JDBCUtils.close(preparedStatement1,connection);
                  JDBCUtils.close(preparedStatement2,null);
              }
          }
      }
      
      
基于STM32的循迹避障小车资料源码(高分项目),个人大四的毕业设计、经导师指导并认可通过的高分设计项目,评审分99分,代码完整确保可以运行,小白也可以亲自搞定,主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(高分项目)基于STM32的循迹避障小车资料源码(
内容概要:本文详细介绍了SSM框架(Spring、SpringMVC、MyBatis)的相关知识,涵盖Maven项目管理工具、前端开发技术、HTTP协议及Tomcat服务器等内容。文章首先讲解了SSM框架的组成,包括Spring的IOC、DI、AOP等功能,SpringMVC的请求处理流程以及MyBatis的数据操作。接着介绍了Maven作为项目管理工具的作用,如依赖管理和项目构建,并详细描述了Maven的配置文件pom.xml的使用方法。此外,还探讨了HTTP协议的特点、请求响应格式,以及Web服务器Tomcat的基本概念和工作原理。最后,文章对前端开发技术进行了概述,包括HTML、CSS、JavaScript等基础知识,并简要介绍了Ajax技术。 适合人群:具备一定编程基础,特别是Java开发经验的研发人员,尤其是那些正在学习或使用SSM框架进行Web开发的工程师。 使用场景及目标:①理解SSM框架的工作原理及其各组成部分的功能;②掌握Maven的使用,包括项目创建、依赖管理、生命周期等;③熟悉HTTP协议的请求响应机制,能够处理常见的HTTP请求和响应;④掌握前端开发技术,如HTML、CSS、JavaScript等,能够进行简单的前端页面开发;⑤了解Tomcat服务器的工作原理及其配置方法。 阅读建议:本文内容丰富,涵盖了从后端到前端的多个方面,建议读者在学习过程中结合实际项目进行实践,尤其是在Maven项目管理和SSM框架的具体应用上,多动手操作,加深理解。同时,对于HTTP协议和前端开发技术,可以通过实际的网络请求和页面开发来巩固所学知识。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bm1998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值