千锋教育2218 JDBC

JDBC 【重点】

5.1 JDBC 概述

JDBC 全称 Java database connectivity
使用 Java 代码去连接数据库
之前是在小黑屏或者 Navicat 可视化工具中去连接数据库并对数据库里面的数据进行操作? 这样是不行的
从今天开始利用 Java 代码操作咱们的表中的数据,增删改查。
Jdbc:是一个规范,sun 公司想去连接 mysql 数据库,要求数据库厂商解决连接问题
JDK 中已经封装好了 Java 的类库, java.sql* javax.sql*
但是现在有一个尴尬的事情,Java 和 mysql 是两个厂商。
Java 要求 mysql 数据库的厂商必须提供解决连接上数据库的问题。只有线连接上数据库以后才能去对数据库中的数据进行增删改查。单纯 JDK 是连接不上数据库的。
如果想要让 Java连接 mysql 数据库 必须让数据库厂商提供驱动。所以 mysql 厂商封装了第三方的 jar 包。
jdk 负责增删改查、第三方的 Jar 包负责连接数据库
第三方的 jar 包
mysql-connector-java-jar 这个 jar 包中封装了一些类和接口,只不过不是 jdk
如何下载第三方的 jar :
百度搜 mvnrepository
在搜索框中搜 mysql
点进去找到jar这个包下载一下即可

5.2 使用 Java 代码连接数据库的入门案例

在当前的 Java 工程中导入刚才下载的第三方的 jar 包
在 src 文件夹下面新建 lib 文件夹,不要自己胡乱起名字!!!
将下载的 jar、包复制到 lib 文件夹下面
鼠标点上这个jar,右键 add as libary 点一下按钮即可
package com.study.a_mysql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @author big God
 * @date 2023/1/31 21:35 * @version 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载 mysql 的驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备一个连接数据库的 url
        /**
         * jdbc: 协议名字
         * mysql: 子协议 告知要连接哪一个数据库
         * localhost: 本级服务器
         * 3306: mysql 数据库的端口号
         * company: 数据库名字
         */
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        // 3. 准备用户名和密码
        String username = "root";
        String password = "410221";
        // 4. 使用驱动管理器类去获取连接对象
        /**
         * public static Connection getConnection(String url,
         *                                        String user,
         *                                        String password)
         *                                 throws SQLException试图建立到给定数据库 URL 的连接。
         *                                 DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。
         *  参数:
         * url - jdbc:subprotocol:subname 形式的数据库 url
         * user - 数据库用户,连接是为该用户建立的
         * password - 用户的密码
         */
        Connection connection = DriverManager.getConnection(url, username, password);
        // connection 一个连接数据库的对象,如果连接成功就会返回一个对象
        System.out.println(connection);

        // 5. 关闭连接
        connection.close();
    }
}

5.3 对数据表中的数据进行增删改查

现在数据库已经连接成功了,接下来就开始操作数据

5.31 对数据表中数据进行添加、增加数据

package com.study.a_mysql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @author big God
 * @date 2023/1/31 21:57 * @version 1.0
 */
public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        // 3. 准备用户名和密码
        String user = "root";
        String password = "410221";
        // 4. 获取 connection 连接数据库对象
        Connection connection = DriverManager.getConnection(url, user, password);
        // 代码走到这一步,已经可以连接数据库了
        // 5. 创建 Statement 对象,用户将 sql 语句发送到数据库执行
        // 搬运工对象,可以将 sql 语句搬运到数据库去执行
        Statement statement = connection.createStatement();
        // 6. 准备 sql 语句 在写这个 sql 语句感觉有没有不妥之处?

        String sql = "insert into work(name, age, info) values('土豆牛肉', 17,'辅助')";
        // 7. 使用 statement 对象执行 sql 语句
        int i = statement.executeUpdate(sql);
        // i 是受影响的行数 i 的结果是个 1
        System.out.println(i);
        // 8. 关闭资源
        statement.close();
        connection.close();
    }
}

5.32 删除数据

package com.study.a_mysql;

import java.sql.*;

/**
 * @author big God
 * @date 2023/2/2 20:54 * @version 1.0
 */
public class Demo3 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url 和用户名 和密码
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 获取 Connection 对象
        Connection connection = DriverManager.getConnection(url, user, password);
        // 4. 通过 connection 对象 获取 statement 对象【搬运工对象】 搬运 sql 语句
        Statement statement = connection.createStatement();
        // 5. 准备 sql 语句
        String sql = "delete from work where id = 3";
        // 6. 执行 sql 语句
        int i = statement.executeUpdate(sql);
        System.out.println(i);
        // 7. 关闭资源
        statement.close();
        connection.close();

    }
}

5.33 修改数据

package com.study.a_mysql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 修改数据
 * @author big God
 * @date 2023/2/2 21:05 * @version 1.0
 */
public class Demo4 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url、user、password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 准备 connection 对象
        Connection connection = DriverManager.getConnection(url, user, password);
        // 4. 通过connection对象获取搬运工对象 statement 对象
        Statement statement = connection.createStatement();
        // 5. 准备 sql 语句
        String sql = "update work set name = '自助小火锅', age = 18, info='辅助' where id = 2";
        // 6. 通过 statement 对象执行 sql 语句
        int i = statement.executeUpdate(sql);
        System.out.println(i);
        // 7. 关闭资源
        statement.close();
        connection.close();
    }
}

5.34 查询数据

package com.study.a_mysql;

import java.sql.*;

/**
 * @author big God
 * @date 2023/2/3 21:01 * @version 1.0
 */
public class Demo6 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url user password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 获取 connection 连接
        Connection connection = DriverManager.getConnection(url, user, password);
        // 4. 通过 connection 获取 statement 搬运工对象
        Statement statement = connection.createStatement();
        // 5. 准备 sql 语句
        String sql = "select * from work";
        // 6. 使用搬运工对象去执行 sql 语句  executeQuery();
        ResultSet resultSet = statement.executeQuery(sql);
        // 表示数据库结果集的数据表,通常通过执行查询数据的语句生成。
        /**
         * ResultSet 里面的数据
         *   1,    麻婆豆腐,   23,   打野
         *   2,    自助小火锅, 18,   辅助
         *   4,    红烧茄子,   8,   上单
         */
        /**
         * ResultSet 对象保持一个光标指向其当前的数据行。
         *  最初,光标位于第一行之前。next 方法将光标移动到下一行。
         *  并且由于在 ResultSet 对象中没有更多行时返回 false,
         *  因此可以在 while 循环中使用循环来遍历结果集
         */
        while (resultSet.next()) {
            /**
             *  通过字段的名字获取当前字段下面的数据
             *       获取 id 这一列的数据
             *       获取 name 这一列的数据
             *       获取 age 这一列的数据
             *       获取 info 这一列的数据
             */
            System.out.println(resultSet.getInt("id") + "\t"
                    + resultSet.getString("name") + "\t"
                    + resultSet.getInt("age") + "\t"
                    + resultSet.getString("info"));
        }
        // 7. 关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

查询数据就是单纯打印出来? 并不是的,要把查询出来的数据赋值给一个实体类,用一个对象接一下

如果多个对象存到集合中即可

package com.study.a_mysql;

import java.sql.*;
import java.util.ArrayList;

/**
 * @author big God
 * @date 2023/2/3 21:01 * @version 1.0
 */
public class Demo7 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url user password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 获取 connection 连接
        Connection connection = DriverManager.getConnection(url, user, password);
        // 4. 通过 connection 获取 statement 搬运工对象
        Statement statement = connection.createStatement();
        // 5. 准备 sql 语句
        String sql = "select * from work";
        // 6. 使用搬运工对象去执行 sql 语句  executeQuery();
        ResultSet resultSet = statement.executeQuery(sql);
        // 表示数据库结果集的数据表,通常通过执行查询数据的语句生成。
        /**
         * ResultSet 里面的数据
         *   1,    麻婆豆腐,   23,   打野
         *   2,    自助小火锅, 18,   辅助
         *   4,    红烧茄子,   8,   上单
         */
        /**
         * ResultSet 对象保持一个光标指向其当前的数据行。
         *  最初,光标位于第一行之前。next 方法将光标移动到下一行。
         *  并且由于在 ResultSet 对象中没有更多行时返回 false,
         *  因此可以在 while 循环中使用循环来遍历结果集
         */
        ArrayList<Work> arrayList = new ArrayList<>();
        while (resultSet.next()) {
            /**
             *  通过字段的名字获取当前字段下面的数据
             *       获取 id 这一列的数据
             *       获取 name 这一列的数据
             *       获取 age 这一列的数据
             *       获取 info 这一列的数据
             */
            // 将数据赋值给一个对象,对象得先有类
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            String info = resultSet.getString("info");
            Work work = new Work(id, name, age, info);
            arrayList.add(work);
        }
        // 遍历集合
        for (Work work : arrayList) {
            System.out.println(work);
        }
        // 7. 关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

练习查询 person 表

package com.study.a_mysql;

import java.sql.*;
import java.util.ArrayList;

/**
 * @author big God
 * @date 2023/2/4 9:43 * @version 1.0
 */
public class Demo8 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url user password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 通过 DriverManager 获取 connection 连接对象
        Connection connection = DriverManager.getConnection(url, user, password);
        // 4. 通过 connection.createStatement 对象 获取 statement 对象
        Statement statement = connection.createStatement();
        // 5. 准备 sql 语句
        String sql = "select * from person";
        // 6. 使用搬运工对象去执行 sql 语句
        ResultSet resultSet = statement.executeQuery(sql);
        // 创建一个集合,将表中的数据赋值给对象,将对象存储在集合中
        ArrayList<Person> works = new ArrayList<>();
        // 循环遍历表中的数据
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            int gender = resultSet.getInt("gender");
            int salary = resultSet.getInt("salary");
            Person person = new Person(id, name, age, gender, salary);
            works.add(person);
        }
        // 循环遍历集合
        for (Person work : works) {
            System.out.println(work);
        }
        // 7. 关闭流
        resultSet.close();
        statement.close();
        connection.close();

    }
}

回顾昨天的东西

连接数据库并对数据中的数据进行增删改查
// 1. 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 准备 url user password 
String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
String user = "root";
String password = "410221";
// 3. 通过 DriverManager 获取 connection 对象
Connection connection = DriverManager.getConnection(url, user, password);
// 4. 通过 connection.createStatement 对象获取搬运工对象 statement
Statement statement = connection.createStatement();
// 5. 准备 sql 语句
String sql = "insert into work (name, age, info) vaules ('皮蛋豆腐', 18, '好吃' )";
// 6. 通过搬运工对象执行 sql 语句
int i = statement.executeUpdate(sql);
System.out.println(i)
// 7. 关闭资源
statement.close();
connection.close();

今天的内容

预处理搬运工对象【重点】
封装一个工具类【重点】
SQL 注入
JavaBean 规范
BeanUtils 类

1. 预处理的搬运工对象

昨天讲了对数据库里的数据进行增删改查,使用 Statement 对象执行 sql 语句
今天不再用 Statement 搬运工对象执行 sql 语句, 使用另外一种叫做预处理的搬运工对象去执行 sql 语句
PreparedStatement prepareStatement(String sql)
                                   throws SQLException创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库。 
带有 IN 参数或不带有 IN 参数的 SQL 语句都可以被预编译并存储在 PreparedStatement 对象中。然后可以有效地使用此对象来多次执行该语句。 

注:为了处理受益于预编译的带参数 SQL 语句,此方法进行了优化。如果驱动程序支持预编译,则 prepareStatement 方法将该语句发送给数据库进行预编译。一些驱动程序可能不支持预编译。在这种情况下,执行 PreparedStatement 对象之前无法将语句发送给数据库。这对用户没有直接影响;但它的确会影响哪些方法将抛出某些 SQLException 对象。 

使用返回的 PreparedStatement 对象创建的结果集在默认情况下类型为 TYPE_FORWARD_ONLY,并带有 CONCUR_READ_ONLY 并发级别。已创建结果集的可保存性可调用 getHoldability() 确定。 


参数:
sql - 可能包含一个或多个 '?' IN 参数占位符的 SQL 语句 
返回:
包含预编译 SQL 语句的新的默认 PreparedStatement 对象 
抛出: 
SQLException - 如果发生数据库访问错误,或者在关闭的连接上调用此方法
静态 sql 语句:insert into work(name, age, info) values("张三", 12 , "长得丑"); 值写的是固定的
参数化 sql 语句: insert into work(name, age, info) values(?,?,?); 真实的值用 ? 占着位置
sql - 一个 String 对象,它是将被发送到数据库的 SQL 语句,可以包含一个或多个 '?' IN 参数
select * from person where id = ?;
void setObject(int parameterIndex, Object x) throws SQLException 使用给定对象设置指定参数的值。【就是给 sql 语句中那个 ? 进行赋值的方法】第二个参数必须是 Object 类型;所以,应该对内置类型使用 java.lang 的等效对象。 JDBC 规范指定了一个从 Java Object 类型到 SQL 类型的标准映射关系。在发送到数据库之前,给定参数将被转换为相应 SQL 类型。
增加数据
package com.study.a_preparestatement;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @author big God
 * @date 2023/2/4 10:38 * @version 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url user password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 获取 connection 对象
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println(connection);
        // 4. 准备 sql 语句
        String sql = "insert into work(name, age, info) values(?, ?, ?)";
        // 5. 通过 connection 获取 sql 的 【预处理】搬运工对象 开发中用
        /**
         * prepareStatement(String sql)
         *       throws SQLException
         *       创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库。
         */
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 6. 开始执行 sql 语句? 能执行 sql ? 还不能的, 因为 sql 还不完整
        /**
         * setObject(); 是对咱们的 sql 中的 ? 进行赋值的
         *      parameterIndex - 第一个参数是 1,第二个参数是 2,……
         *      x - 包含输入参数值的对象
         *      insert into work(name, age, info) values("外婆菜炒鸡蛋", 23, "射手")
         */
        preparedStatement.setObject(1,"外婆菜炒鸡蛋");
        preparedStatement.setObject(2,23);
        preparedStatement.setObject(3,"射手");
        preparedStatement.setObject(1,"梅菜扣肉");
        preparedStatement.setObject(2,23);
        preparedStatement.setObject(3,"中单");
        // 有几个 ? 的 SQL 语句是不是得有 setObject 方法
        // 7. 执行 sql 语句
        int i = preparedStatement.executeUpdate();
        System.out.println(i);
        // 8. 关闭资源
        preparedStatement.close();
        connection.close();

    }
}
删除数据
package com.study.a_preparestatement;

import java.sql.*;

/**
 * 删除数据
 * @author big God
 * @date 2023/2/4 14:39 * @version 1.0
 */
public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url user password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 获取 connection 对象
        Connection connection = DriverManager.getConnection(url, user, password);
        // 4. 准备 sql 语句
        String sql = "delete from work where id = ?";
        // 5. 获取预处理搬运工对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 6. 对 sql 语句中的 ? 进行赋值
        preparedStatement.setObject(1, 2);
        // 7. 执行 sql 语句
        int i = preparedStatement.executeUpdate();
        System.out.println(i);
        // 8. 关闭资源
        preparedStatement.close();
        connection.close();
    }
}
修改数据
package com.study.a_preparestatement;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @author big God
 * @date 2023/2/4 10:38 * @version 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url user password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 获取 connection 对象
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println(connection);
        // 4. 准备 sql 语句
        String sql = "insert into work(name, age, info) values(?, ?, ?)";
        // 5. 通过 connection 获取 sql 的 【预处理】搬运工对象 开发中用
        /**
         * prepareStatement(String sql)
         *       throws SQLException
         *       创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库。
         */
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 6. 开始执行 sql 语句? 能执行 sql ? 还不能的, 因为 sql 还不完整
        /**
         * setObject(); 是对咱们的 sql 中的 ? 进行赋值的
         *      parameterIndex - 第一个参数是 1,第二个参数是 2,……
         *      x - 包含输入参数值的对象
         *      insert into work(name, age, info) values("外婆菜炒鸡蛋", 23, "射手")
         */
        preparedStatement.setObject(1,"外婆菜炒鸡蛋");
        preparedStatement.setObject(2,23);
        preparedStatement.setObject(3,"射手");
        preparedStatement.setObject(1,"梅菜扣肉");
        preparedStatement.setObject(2,23);
        preparedStatement.setObject(3,"中单");
        // 有几个 ? 的 SQL 语句是不是得有 setObject 方法
        // 7. 执行 sql 语句
        int i = preparedStatement.executeUpdate();
        System.out.println(i);
        // 8. 关闭资源
        preparedStatement.close();
        connection.close();

    }
}
查询数据
package com.study.a_preparestatement;

import java.sql.*;
import java.util.ArrayList;

/**
 * 查询数据
 * @author big God
 * @date 2023/2/4 16:00 * @version 1.0
 */
public class Demo4 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url user password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 获取 connection 对象
        Connection connection = DriverManager.getConnection(url, user, password);
        // 4. 准备 sql 语句
        String sql = "select * from work";
        // 5. 获取预处理搬运工对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        // 6. 执行 sql 语句
        ArrayList<Work> works = new ArrayList<>();
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            String info = resultSet.getString("info");
            Work work = new Work(id, name, age, info);
            works.add(work);
        }
        // 7. 遍历集合
        for (Work work : works) {
            System.out.println(work);
        }
        // 8. 关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}
查询一条数据
package com.study.a_preparestatement;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;

/**
 * @author big God
 * @date 2023/2/9 20:13 * @version 1.0
 */
public class Demo5 {
    public static void main(String[] args) throws Exception {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备 url user password
        String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
        String user = "root";
        String password = "410221";
        // 3. 获取 connection 对象
        Connection connection = DriverManager.getConnection(url, user, password);
        // 4. 准备 sql 语句
        String sql = "select * from work where id = ?";
        // 5. 获取预处理搬运工对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 对 ? 进行赋值
        preparedStatement.setObject(1,6);
        // resultSet 结果集对象,结果集是存的数据
        ResultSet resultSet = preparedStatement.executeQuery();
        // 6. 执行 sql 语句
        ArrayList<Work> works = new ArrayList<>();
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            String info = resultSet.getString("info");
            Work work = new Work(id, name, age, info);
            works.add(work);
        }
        // 7. 遍历集合
        for (Work work : works) {
            System.out.println(work);
        }
        // 8. 关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

发现咱们写增删改查的时候好多代码都是重复的代码,有想法没有?咱们能不能给封装一下

2.封装一个工具类【重点】

封装一个类、这个类中要获取 connection 对象、关闭资源
package com.study.utils;

import java.sql.*;

/**
 * 这个类获取连接对象和关闭资源
 * 这两个事情:一个方法一个功能
 * @author big God
 * @date 2023/2/9 20:20 * @version 1.0
 */
public class JdbcUtil {
    private static String url = "jdbc:mysql://localhost:3306/company?useSSL=false";
    private static String user = "root";
    private static String password = "410221";
    static {
        // 1. 加载驱动
        // 只要 JdbcUtil 这个类一加载就会执行加载驱动这个方法,这个加载驱动执行的比较早
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    // 获取 connection 对象的方法
    public static Connection getConnection () {
        Connection connection = null;
        try {
            // 这里面 connection 是局部变量 需要在外面先定义一下,不然外面无法返回
            connection = DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    // 关闭资源的方法 增删改 关闭两个 查询关闭三个
    public static void close(Connection connection) {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void close(Statement statement, Connection connection) {
        try {
            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            resultSet.close();
            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
增加数据
package com.study.b_preparestatement;

import com.study.utils.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 增加数据
 * @author big God
 * @date 2023/2/9 20:38 * @version 1.0
 */
public class Demo2 {
    public static void main(String[] args) throws SQLException {
        Connection connection = JdbcUtil.getConnection();
        String sql = "insert into work(name, age, info) values (?,?,?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1, "酒糟鱼");
        preparedStatement.setObject(2, 19);
        preparedStatement.setObject(3, "美美的");
        String sql1 = "insert into work(id,name, age, info) values (?,?,?,?)";
        PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
        preparedStatement1.setObject(1, 2);
        preparedStatement1.setObject(2, "酱大骨");
        preparedStatement1.setObject(3, 19);
        preparedStatement1.setObject(4, "酱香味");
        int i = preparedStatement.executeUpdate();
        int i1 = preparedStatement1.executeUpdate();
        System.out.println(i);
        System.out.println(i1);
        JdbcUtil.close(preparedStatement, connection);
    }
}
删除数据
package com.study.b_preparestatement;

import com.study.utils.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 删除数据
 * @author big God
 * @date 2023/2/9 20:33 * @version 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws SQLException {
        Connection connection = JdbcUtil.getConnection();
        String sql = "delete from work where id = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1, 5);
        int i = preparedStatement.executeUpdate();
        JdbcUtil.close(preparedStatement, connection);


    }
}
修改数据
package com.study.b_preparestatement;

import com.study.utils.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 修改数据
 * @author big God
 * @date 2023/2/9 20:47 * @version 1.0
 */
public class Demo3 {
    public static void main(String[] args) throws SQLException {
        Connection connection = JdbcUtil.getConnection();
        String sql = "update work set name = ?, age = ?, info= ? where id = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1, "猪肘饭");
        preparedStatement.setObject(2, 56);
        preparedStatement.setObject(3, "战士");
        preparedStatement.setObject(4, 1);
        int i = preparedStatement.executeUpdate();
        System.out.println(i);
        JdbcUtil.close(preparedStatement, connection);


    }
}
查询数据
package com.study.b_preparestatement;

import com.study.utils.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

/**
 * @author big God
 * @date 2023/2/9 20:50 * @version 1.0
 */
public class Demo4 {
    public static void main(String[] args) throws SQLException {
        // 加载驱动
        Connection connection = JdbcUtil.getConnection();
        // 编写 sql 语句
        String sql = "select * from work";
        // 获取预处理搬运工对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 执行 sql 语句
        ResultSet resultSet = preparedStatement.executeQuery();
        ArrayList<Work> list = new ArrayList<>();
        while (resultSet.next()) {
            // 循环创建对象放入集合中
            int id = resultSet.getInt("id");
            String name = resultSet.getString( "name");
            int age = resultSet.getInt("age");
            String info = resultSet.getString("info");
            list.add(new Work(id, name, age, info));
        }
        // 四饼遍历集合
        list.forEach(System.out::println);
        // 关闭资源
        JdbcUtil.close(resultSet, preparedStatement, connection);

    }
}
上面的 JdbcUtil 这个类封装的不太好,封装的目的是代码写好以后,这个封装好的类,写好以后就不能再改了。
但是如果数据库换了怎么办?如果你用我封装的这个类,数据库的密码和我的不一样,
加载外部的配置文件,你只需要去改动配置文件即可!!!
配置文件中存放的是 url 驱动 user password 四个
按照步骤来写:
在 src 下面新建一个配置文件: db.properties
在 db.properties 文件中去写 Driver url user password
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/company?useSSL=false
user=root
password=410221
package com.study.utils;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.*;
import java.util.Properties;

/**
 * @author big God
 * @date 2023/2/9 21:18 * @version 1.0
 */
public class JdbcUtil1 {
    private static String url = null;
    private static String user = null;
    private static String password = null;
    static {
        /**
         * Properties 可以保存到流中或从流中加载
         * 属性列表中的每个键及其对应的值都是一个字符串
         * 读取配置文件中的信息
         */
        Properties properties = new Properties();
        try {
            /**
             * 配置文件中所有数据变成流,给 properties 对象了
             * 代码走到这一步、配置文件中的所有的数据都给了 properties 对象
             */
            properties.load(new FileInputStream("src/db.properties"));
            String driver = properties.getProperty("driver");
            Class.forName(driver);
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    // 获取 connection 对象的方法
    public static Connection getConnection () {
        Connection connection = null;
        try {
            // 这里面 connection 是局部变量 需要在外面先定义一下,不然外面无法返回
            connection = DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    // 关闭资源的方法 增删改 关闭两个 查询关闭三个
    public static void close(Connection connection) {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void close(Statement statement, Connection connection) {
        try {
            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            resultSet.close();
            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
总结:封装 JdbcUtil 这个工具类
在 src 文件夹下面 db.properties 文件
新建一个 JdbcUtil 类
靠一个 Properties 类去读取 db.properties 文件
以流的形式读取出来配置文件信息

3. SQL 注入【了解】

咱们在 sql 的时候、会有漏洞。由于 sql 机制问题。
delete from peron where id = 5; 没有漏洞
delete from peron where id = 5 or 1 = 1; 有漏洞
在 where 的条件的后面加一个 or 1=1 的话,就是证明 where 没啥用了
所谓的 sql 注入
类似于这种 sql 注入的东西:搬运工对象不可以防止 sql 注入。今天讲的预处理搬运工对象是可以防止 sql 注入的
package com.study.c_sql;

import com.study.b_preparestatement.Work;
import com.study.utils.JdbcUtil;
import com.study.utils.JdbcUtil1;

import java.sql.*;
import java.util.ArrayList;

/**
 * @author big God
 * @date 2023/2/11 20:19 * @version 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws SQLException {
//        statementTest();
        prepareStatementTest();
    }

    /**
     * 搬运工对象 不能防止 sql 注入
     */
    public static void statementTest() throws SQLException {
        Connection connection = JdbcUtil1.getConnection();
        Statement statement = connection.createStatement();
        String sql = "delete from student1 where id = 1 or 1 = 1";
        int i = statement.executeUpdate(sql);
        System.out.println(i);
        JdbcUtil1.close( statement, connection);
    }
    // 预处理搬运工对象 可以防止 sql 注入

    public static void prepareStatementTest() throws SQLException {
        Connection connection = JdbcUtil1.getConnection();
        String sql = "delete from student5 where id = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1, 3);
        int i = preparedStatement.executeUpdate();
        System.out.println(i);
        JdbcUtil1.close(preparedStatement, connection);
    }
}

4. JavaBean 规范

Java 实体类,咱们写一个实体类的时候一定要规范起来
私有化成员变量
一定要提供一个无参构造
一定要提供 Setter 和 Getter 方法
每个实体类单独成一个文件
package com.study.d_entiy;

/**
 * Person 类单独成一个文件
 *  从数据库中取出来数据,赋值一个实体类对象,先有类,然后再有对象
 * @author big God
 * @date 2023/2/11 20:40 * @version 1.0
 */
public class Person {
    // 1. 私有化成员变量
    private Integer id;
    private String name;
    private Integer age;

    // 2. 提供一个无参构造方法
    public Person() {
    }

    // 3. 提供属性的 Getter and Setter 方法
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

5. BeanUtils 类

学习一个新类,去查官方的 API 文档
结果发现没有这个类,你们有想法没?那么这个类在哪?这个不是 jdk 提供的
是第三方提供的!! 基于反射的类,对类的属性进行赋值和取值的!!!
平常对一个符合 JavaBean 规范的类对象进行赋值 使用 set 方法,取值使用 get 方法
但是有的时候,咱们使用不了 set 赋值和 get 方法,这个是可以使用反射对象进行
靠 BeanUtils 类
需要导包的!!!
common-beanutils-1.9.4.jar
package com.study.e_beanutils;

import com.study.d_entiy.Person;
import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;

/**
 * @author big God
 * @date 2023/2/11 20:54 * @version 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        // BeanUtils 是对实体类对象的属性进行赋值和取值
        Person person = new Person();
        /**
         *  赋值
         *  setProperty(Object bean, String name, Object value)
         *  第一个参数:哪个对象 第二个参数:属性的名字 第三个采纳数 给属性赋的值
         *  在 Person 对象中 对 id 属性进行赋值 23
         */

        BeanUtils.setProperty(person, "id", 23);
        BeanUtils.setProperty(person, "name", "狗蛋");

        // 取值
        System.out.println(BeanUtils.getProperty(person,"id"));
        System.out.println(BeanUtils.getProperty(person,"name"));
    }
}

6. 元数据【重点】

关于 sql 参数的元数据
关于 resultset 结果集元数据
6.1 参数元数据
获取预处理的搬运工的对象,会预处理的咱们的 sql ,参数 sql 语句,带 ? 的
参数元数据就是和 sql 占位有关的!!!
package com.study.f_metedate;

import com.study.utils.JdbcUtil1;

import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 元数据对象
 * @author big God
 * @date 2023/2/11 21:24 * @version 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws SQLException {
        Connection connection = JdbcUtil1.getConnection();
        String sql = "insert into work(name, age, info) values(?,?,?)";
        // 通过 prepareStatement 获取参数元数据对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
        // 通过 parameterMetaData 获取 sql 参数的个数
        int parameterCount = parameterMetaData.getParameterCount();
        System.out.println(parameterCount); // 3
        // 要他有啥用?可以利用循环对 sql 的? 进行赋值
        Object[] obj = {"金蝉", 45, "哈哈"};
        for (int i = 0; i < parameterCount; i++) {
            /**
             * i = 0 i < 3 true
             *     preparedStatement.setObject(1, "金蝉"); i++
             * i = 1 i < 3 true
             *     preparedStatement.setObject(2, 45); i++
             * i = 2 i < 3 true
             *     preparedStatement.setObject(3, "哈哈"); i++
             *
             */
            preparedStatement.setObject(i + 1, obj[i]);
        }
        int i = preparedStatement.executeUpdate();
        System.out.println(i);
    }
}
6.2 结果集元数据
专门处理查询结果之后的数据,只和查询有关
package com.study.f_metedate;

import com.study.utils.JdbcUtil1;

import java.sql.*;

/**
 * 结果集元数据
 * @author big God
 * @date 2023/2/11 21:36 * @version 1.0
 */
public class Demo2 {
    public static void main(String[] args) throws SQLException {
        Connection connection = JdbcUtil1.getConnection();
        String sql = "select * from work";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        /**
         * 结果集元数据对象和结果集(resultSet) 有关
         * 通过 resultSet 获取结果集元数据对象
         */
        ResultSetMetaData metaData = resultSet.getMetaData();
        /**
         * meteData 可以获取结果集中列(字段)还有数据等
         *  resultSet
         *      1,猪肘饭,56,战士
         *      2,酱大骨,19,酱香味
         *      4,红烧茄子,8,上单
         *      6,梅菜扣肉,23,中单
         *      7,红烧猪蹄,19,酱大骨
         *      8,酒糟鱼,19,美美的
         *      9,金蝉,45,哈哈
         *
         * 通过结果集获取字段的个数
         */
        int columnCount = metaData.getColumnCount();
        System.out.println(columnCount);
//        String columnName = metaData.getColumnName(1);
//        String columnName1 = metaData.getColumnName(2);
//        String columnName2 = metaData.getColumnName(3);
//        String columnName3 = metaData.getColumnName(4);


        for (int i = 0; i < columnCount; i++) {
            String columnName = metaData.getColumnName(i + 1);
            System.out.println(columnName);
        }

        /**
         * id    name age  info
         * 2	熊二	18	喜欢翠花
         * 3	熊大	19	喜欢打熊二
         * 4	吉吉	16	吃香蕉
         * 5	光头强	28	砍树
         * 6	骚磊	12	臀大
         * 10	金蝉	456	嘻嘻
         * while 第一次进来
         *      i=1  id getobject("id")  2 +" "  i++
         *      i=2  name  getObject("name")  熊二+" " i++
         *      i=3  age   getObject("age")   18 + " " i++
         *      i=4 info   getObject("info")   喜欢翠花+ “ ” i++
         *      i=5 5<=4false内层循环结束 换行
         *
         *  while循环第二进来
         *      i=1 id getObject("id")    3  i++
         *       ...
         */
        // 现在可以获取结果集中字段,然后可以通过字段获取数据了
        while (resultSet.next()) {
            for (int i = 0; i < columnCount; i++) {
                String columnName = metaData.getColumnName(i + 1);
                Object object = resultSet.getObject(columnName);
                System.out.print(object + "\t");
            }
            System.out.println();
        }
    }
}

今天必须将BeanUtils和元数据的方法牢牢记住。明天能不能听懂就靠这些了!!!

一定要花时间,如果明天听不懂,项目是写不出来的!!! 多敲多练多理解!!!

今天的内容

封装 BaseDao 类
连接池 druid
jdbc 事务的操作

1. 封装 BaseDao 【重点】

BaseDao 是我自己封装的一个类,一个工具类
这个类给我提供了两个方法,一个是对数据增删改,另外一个是查询的方法
所以这个类封装以后,咱们以后写代码就不用那么复杂了,只需要两三行就可以完成增删改查了
package com.study.utils;

import org.apache.commons.beanutils.BeanUtils;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 封装需要站在一定的高度,现在我们没有,慢慢学
 * @author big God
 * @date 2023/2/12 14:17 * @version 1.0
 */
public class BaseDao {
    // Object[] objects = {""}
    // sql = "";

    /**
     * 删除
     * @param sql 要执行的增删改的 sql 语句
     * @param objects 对 sql 中的参数进行赋值的数组
     * @return 受影响的行数
     */
    public int update(String sql, Object[] objects){
        // 1. 连接数据库,获取连接数据库的对象
        Connection connection = JdbcUtil.getConnection();
        // 2. 获取预处理搬运工对象
        PreparedStatement preparedStatement = null;
        try {
            preparedStatement = connection.prepareStatement(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        // 3. 现在咱们对 sql 的参数进行赋值 因为咱们要站在一定的高度
        // 现在 sql 语句不确定!! 封装的目的具有普适性 都可以用 借助于参数元数据
        ParameterMetaData parameterMetaData = null;
        try {
            parameterMetaData = preparedStatement.getParameterMetaData();
            // 4. 开始对 sql 语句中的 ? 进行赋值 循环进行赋值
            if (objects != null && parameterMetaData.getParameterCount() == objects.length) {
                for (int i = 0; i < parameterMetaData.getParameterCount(); i++) {
                    preparedStatement.setObject(i + 1,objects[i]);
                }
            }
            // 5. 执行 sql 语句
            int i = preparedStatement.executeUpdate();
            return i;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(preparedStatement,connection);
        }
        return 0;
    }

    /**
     * 封装查询偏麻烦,查询出来数据之后,将数据赋值给一个类对象,并且存在集合中
     * @param sql
     * @param parameters
     * @param cls 查询出来的数据要赋值给一个对选哪个,这个对象时通过反射获取的 【动态的】
     * @param <T>
     * @return
     */

    public <T> List<T> query(String sql, Object[] parameters, Class<T> cls) {
        // 1. 获取连接对象
        Connection connection = JdbcUtil.getConnection();
        // 2. 获取预处理搬运工对象
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
             preparedStatement = connection.prepareStatement(sql);
             // 3. 获取 sql 语句中的参数的个数
            int parameterCount = preparedStatement.getParameterMetaData().getParameterCount();
            // 4. 对 ? 进行赋值
            if (parameters != null && parameterCount == parameters.length) {
                for (int i = 0; i < parameterCount; i++) {
                    preparedStatement.setObject(i + 1, parameters[i]);
                }
            }
            // 5. 执行 sql 语句
            resultSet = preparedStatement.executeQuery();
            // 6. 创建集合
            List<T> list = new ArrayList<>();
            // 7. 获取结果集元数据对象 因为你不知道有几个字段
            ResultSetMetaData metaData = resultSet.getMetaData();
            // 8. 获取字段的个数
            int columnCount = metaData.getColumnCount();
            // 9. 遍历数据
            while (resultSet.next()) { // 行
                // 10. 通过 Work.class 获取 work 对象
                // Work work = new Work() 这样就写死了 不具备灵活性
                T t = cls.getConstructor().newInstance();

                for (int i = 0; i < columnCount; i++) { // 列
                    // 11. 获取列(字段)的名字
                    String columnName = metaData.getColumnName(i + 1);
                    // 12. 通过字段获取字段下面的数据
                    Object value = resultSet.getObject(columnName);
                    // 13. 获取数据以后将数据赋值给一个对象,先有对象得有类
                    //BeanUtils 专门对对象属性进行赋值的
                    // 三个参数: 第一个 :对象 第二个参数: 属性名字 第三个参数:值
                    // 要求数据库的字段一定要和实体类属性保持一致  结果集元数据中能获取字段的名字
                    BeanUtils.setProperty(t, columnName, value);
                }
                // 14. 每 while 循环一次, 赋值一个对象, 添加到集合中即可
                list.add(t);
            }
            // 如果 list 集合中长度为 0 那么就是 null  长度不为 0 那就是有值
            return list.size()!= 0 ? list : null;
        } catch (Exception e) {
        } finally {
            // 15. 关闭资源
            JdbcUtil.close(resultSet, preparedStatement, connection);
        }
        return null;
    }
}

今天的内容

连接池
关闭 JDBC 事务
DBUtils 框架(增删改查)【了解】

1. 连接池

1.1 为什么使用连接池
想要对数据库数据进行增删改查,必须先连上数据库,获取连接数据库对象 Connection
咱们的连接池和 connection 有关,和增删改查没有关系
之前获取 Connection 对象时咋获取的? DriverManager.getConnection(url, user,password)
现在咱们使用连接池获取 Connection
每次增删改查的时候,都要先获取 connection 对象,然后做增删改查的操作,然后再关闭资源
每次都要打开然后再关闭,针对于数据库服务器来说。这样服务器的压力是很大的!!!!
一直在创建一直在关闭,
这个时候采用叫做连接池的一种方式:回收方式
创建一个 Connection 对象以后,用完以后不要关闭这个资源。放到缓冲池中。如果再做其他的增删改查的话,直接从池子里面取 connection 对象。不用再关闭之后重新连接了!!!!
1.2 连接池需要
连接池就是管理 connection 对象的
数据库连接有哪些参数:
驱动
url
user
password
连接池中:
池子有初始化的容量
最大容量
等待的时间
常用的连接池:
C3P0【效率极低】
DBCP【有 bug】
druid【德鲁伊】
1.3 Druid 连接池的使用
步骤:
导包 druid-1.1.1 jar
在 sec 文件下面书写一个配置文件 druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/company?useSSL=false
username=root
password=410221

initialSize=5
maxActive=20
maxWait=2000
书写连接池的核心类
package com.study.a_druid;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.util.Properties;

/**
 * 连接池
 * @author big God
 * @date 2023/2/15 21:26 * @version 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws Exception {
        // 1. 读取配置文件 读取druid.properties 文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src/druid.properties"));
        // 2. 创建 druid 核心类
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        // 3. 使用 dataSource 获取连接对象
        Connection connection = dataSource.getConnection();
        // 此时 connection 对象就是使用连接池获取的对象
        System.out.println(connection);
    }
}

咱们封装过 JdbcUtils 这个类获取连接对象 connection 对象 呢过不能修改一下 使用连接池

改进自己封装的 JdbcUtils

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值