ToB企服应用市场:ToB评测及商务社交产业平台

标题: JDBC中数据库的连接与查询 [打印本页]

作者: 瑞星    时间: 2024-11-15 08:48
标题: JDBC中数据库的连接与查询
让我们仔细看看是怎么访问数据库的
  1. package sql;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.SQLException;
  5. public class Conn { // 创建类Conn
  6.     Connection con; // 声明Connection对象
  7.     public static String user;
  8.     public static  String password;
  9.     public Connection getConnection() { // 建立返回值为Connection的方法
  10.         try { // 加载数据库驱动类
  11.             Class.forName("com.mysql.cj.jdbc.Driver");
  12.             System.out.println("数据库驱动加载成功");
  13.         } catch (ClassNotFoundException e) {
  14.             e.printStackTrace();
  15.         }
  16.         user = "root";//数据库登录名
  17.         password = "root";//密码
  18.         try { // 通过访问数据库的URL获取数据库连接对象
  19.             con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=gbk", user, password);
  20.             System.out.println("数据库连接成功");
  21.         } catch (SQLException e) {
  22.             e.printStackTrace();
  23.         }
  24.         return con; // 按方法要求返回一个Connection对象
  25.     }
  26.     public static void main(String[] args) { // 主方法,测试连接
  27.         Conn c = new Conn(); // 创建本类对象
  28.         c.getConnection(); // 调用连接数据库的方法
  29.     }
  30. }
复制代码
详细用法

我们直接看下列的代码
  1. package Main;
  2. import java.sql.*;
  3. public class JDBC {
  4.     public static void main(String[] args) throws SQLException, ClassNotFoundException {
  5. //        1.加载驱动
  6.         Class.forName("com.mysql.cj.jdbc.Driver");
  7. //        2.用户信息和url
  8.         String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true";
  9.         String username="root";
  10.         String password="root";
  11. //        3.连接成功,数据库对象 Connection
  12.         Connection connection = DriverManager.getConnection(url,username,password);
  13. //        4.执行SQL对象Statement,执行SQL的对象
  14.         Statement statement = connection.createStatement();
  15. //        5.执行SQL的对象去执行SQL,返回结果集
  16.         String sql = "SELECT *FROM studentinfo;";
  17.         ResultSet resultSet = statement.executeQuery(sql);
  18.         while(resultSet.next()){
  19.             System.out.println("SNo="+resultSet.getString("SNo"));
  20.             System.out.println("SName="+resultSet.getString("SName"));
  21.             System.out.println("Birth="+resultSet.getString("Birth"));
  22.             System.out.println("SPNo="+resultSet.getString("SPNo"));
  23.             System.out.println("Major="+resultSet.getString("Major"));
  24.             System.out.println("Grade="+resultSet.getString("Grade"));
  25.             System.out.println("SInstructor="+resultSet.getString("SInstructor"));
  26.             System.out.println("SPwd="+resultSet.getString("SPwd"));
  27.         }
  28. //        6.释放连接
  29.         resultSet.close();
  30.         statement.close();
  31.         connection.close();
  32.     }
  33. }
复制代码
加载数据库类

Class.forName("com.mysql.cj.jdbc.Driver");是用来加载数据库驱动的。用于我们的 Java 程序与数据库通信。
Class.forName()函数的作用是用于动态加载一个类,其中的参数自然也是数据库的驱动类com.mysql.cj.jdbc.Driver
连接数据库

我们通常利用DriverManager.getConnection(url,username,passwd)方法来连接数据库,这里面必要我们填入三个参数:
实例化一个SQL对象Statement

这没啥好说的,就是实例化一个对象,以便于我们能调用其中的各种方法
实行SQL语句,查询数据库

查询

要实行数据库的查询我们直接利用executeQuery(String sql)方法,然后里面写入我们的sql语句就行,之后我们就能从返回值得到查询的结果了ResultSet resultSet = statement.executeQuery(sql);
结果

得到了一个ResultSet结果对象之后,想要的得到字符串直接利用getString()方法就行,除此之外还有getLong(),getInt()等,就对应了其中的数据范例。
然后就是这些get方法的参数,有两种参数:
释放链接

为了资源不要浪费,利用完了就应该直接释放了
  1.         resultSet.close();
  2.         statement.close();
  3.         connection.close();
复制代码
防止sql注入的改良

发现题目

又细心的人就会发现前面的代码利用Statement拼字符串非常容易引发SQL注入的题目。
什么是SQL注入呢?我们一般查询数据库靠着相对的命令来实现,如果SQL语句是靠要查的字符拼接出来的,一般是没有题目的,但是我们输入一些特定的字符的时间是可能会让sql语句去做其他的事情。总之SQL一般都是因为字符的拼接毛病实现的。
解决题目

所以我们就得想办法去解决这个题目,有一个方法是转义特定的字符,但这终究是治标不治本的。
前面我们提到,最根本的题目是字符拼接带来的毛病,如果我们不进行字符拼接,直接传递要查的字符,那题目就引刃而解了
把 Statement 换成 PreparedStatement可以完全避免SQL注入的题目,因为PreparedStatement始终利用?作为占位符,并且把数据连同SQL自己传给数据库,如允许以保证每次传给数据库的SQL语句是相同的,只是占位符的数据不同,还能高效利用数据库自己对查询的缓存。
  1. //使用prepareStatement查询
  2. try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
  3.     try (PreparedStatement ps = conn.prepareStatement("SELECT id, grade, name, gender FROM students WHERE gender=? AND grade=?")) {
  4.         ps.setObject(1, "M"); // 注意:索引从1开始
  5.         ps.setObject(2, 3);
  6.         try (ResultSet rs = ps.executeQuery()) {
  7.             while (rs.next()) {
  8.                 long id = rs.getLong("id");
  9.                 long grade = rs.getLong("grade");
  10.                 String name = rs.getString("name");
  11.                 String gender = rs.getString("gender");
  12.             }
  13.         }
  14.     }
  15. }
复制代码
  1. //使用Statement查询
  2. try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
  3.     try (Statement stmt = conn.createStatement()) {
  4.         try (ResultSet rs = stmt.executeQuery("SELECT id, grade, name, gender FROM students WHERE gender=1")) {
  5.             while (rs.next()) {
  6.                 long id = rs.getLong(1); // 注意:索引从1开始
  7.                 long grade = rs.getLong(2);
  8.                 String name = rs.getString(3);
  9.                 int gender = rs.getInt(4);
  10.             }
  11.         }
  12.     }
  13. }
复制代码
我们来看看这个例子,上面的是利用的prepareStatement,下面利用的是Statement。虽然两者之间没有太大的区别,但是还有值得我们注意的地方:
解决完题目带来的思索

我们从代码层面分析完成之后,我想很多人跟我有一样的提问,prepareStatement为什么可以或许做到防止SQL的注入,这里我们在稍微升入SQL注入一点,在详细分析SQL注入是怎么完成的。
SQL注入的本质

SQL注入毛病出现的缘故原由就是用户的输入会直接嵌入到查询语句中,一旦出现精心设计的输入就会改变整个SQL语句的结构
好比现在有如许的语句
  1. String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
复制代码
如果输入了恶意的内容 username = "admin' --"
  1. SELECT * FROM users WHERE username = 'admin' --' AND password = 'password';
复制代码
后面输入的AND password = 'password';就直接被注释掉了,如许就会只查询前面的username = 'admin'
prepareStatement的防御原理

前面不是说了就是因为用户输入和查询语句不是分离的吗,那思路就很简朴了,那将两者分离不就行了
占位符分离

在预编译阶段,SQL 查询的结构被剖析并发送到数据库中,这时 占位符(?)会被数据库视为参数的占位符,而不是 SQL 语句的一部分。
例如:
  1. String query = "SELECT * FROM users WHERE username = ? AND password = ?";
  2. PreparedStatement ps = conn.prepareStatement(query);
  3. ps.setString(1, username);
  4. ps.setString(2, password);
复制代码
在这里:
即利用户输入恶意的内容,例如:
构造的 SQL 查询也不会发生注入,因为数据库会将这些输入看成普通的字符串处理,而不会将其作为 SQL 语句的一部分。实行时的 SQL 查询将是:
  1. SELECT * FROM users WHERE username = 'admin'' OR 1=1 --' AND password = 'password'
复制代码
这个查询在数据库端仍然会被正确地作为两条字符串值传递,而不会被剖析为恶意的 SQL 代码
自动转译用户输入

PreparedStatement 会自动转义参数中的特殊字符,如单引号(')等,使其在数据库中正确地作为字符串处理。这进一步防止了 SQL 注入攻击。
例如,如果用户输入的用户名是:
PreparedStatement 会自动将这个字符串转义为:
如许,即利用户输入恶意的内容,数据库也会将其作为普通字符串处理,而不会被看成 SQL 语句的一部分实行。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4