JDBC中数据库的连接与查询
让我们仔细看看是怎么访问数据库的
package sql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Conn { // 创建类Conn
Connection con; // 声明Connection对象
public static String user;
public static String password;
public Connection getConnection() { // 建立返回值为Connection的方法
try { // 加载数据库驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("数据库驱动加载成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
user = "root";//数据库登录名
password = "root";//密码
try { // 通过访问数据库的URL获取数据库连接对象
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=gbk", user, password);
System.out.println("数据库连接成功");
} catch (SQLException e) {
e.printStackTrace();
}
return con; // 按方法要求返回一个Connection对象
}
public static void main(String[] args) { // 主方法,测试连接
Conn c = new Conn(); // 创建本类对象
c.getConnection(); // 调用连接数据库的方法
}
}
具体用法
我们直接看下列的代码
package Main;
import java.sql.*;
public class JDBC {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.用户信息和url
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username="root";
String password="root";
// 3.连接成功,数据库对象 Connection
Connection connection = DriverManager.getConnection(url,username,password);
// 4.执行SQL对象Statement,执行SQL的对象
Statement statement = connection.createStatement();
// 5.执行SQL的对象去执行SQL,返回结果集
String sql = "SELECT *FROM studentinfo;";
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println("SNo="+resultSet.getString("SNo"));
System.out.println("SName="+resultSet.getString("SName"));
System.out.println("Birth="+resultSet.getString("Birth"));
System.out.println("SPNo="+resultSet.getString("SPNo"));
System.out.println("Major="+resultSet.getString("Major"));
System.out.println("Grade="+resultSet.getString("Grade"));
System.out.println("SInstructor="+resultSet.getString("SInstructor"));
System.out.println("SPwd="+resultSet.getString("SPwd"));
}
// 6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}
加载数据库类
Class.forName("com.mysql.cj.jdbc.Driver");是用来加载数据库驱动的。用于我们的 Java 程序与数据库通信。
Class.forName()函数的作用是用于动态加载一个类,其中的参数自然也是数据库的驱动类com.mysql.cj.jdbc.Driver
连接数据库
我们通常使用DriverManager.getConnection(url,username,passwd)方法来连接数据库,这里面需要我们填入三个参数:
url:数据库的URL,格式很重要
"jdbc:mysql":这是告诉程序使用 JDBC 驱动来连接 MySQL 数据库。"localhost":指向本地计算机的数据库服务器。"3306":MySQL 服务的默认端口号。"test1":这是要访问的数据库名称。?useUnicode=true&characterEncoding=gbk:这些是查询参数,用于指定数据库的配置。它们表示:useUnicode=true:启用 Unicode 支持,确保可以存储和读取 Unicode 字符。characterEncoding=gbk:设置字符编码为 GBK,通常用于处理中文字符。
username:数据库的用户名
passwd :数据库的密码
实例化一个SQL对象Statement
这没啥好说的,就是实例化一个对象,以便于我们能调用其中的各种方法
执行SQL语句,查询数据库
查询
要执行数据库的查询我们直接使用executeQuery(String sql)方法,然后里面写入我们的sql语句就行,之后我们就能从返回值得到查询的结果了ResultSet resultSet = statement.executeQuery(sql);
结果
得到了一个ResultSet结果对象之后,想要的得到字符串直接使用getString()方法就行,除此之外还有getLong(),getInt()等,就对应了其中的数据类型。
然后就是这些get方法的参数,有两种参数:
- 列名称:就是你查询出来后,直接输入列表的名称就给你输出了查询的结果,名称的类型自然就是String类型的
- 列索引:直接从中输入列的索引就会输入第几列的查询结果,但是注意!!!!这里的所以不再是从0开始,而是从1开始,这是非常值得注意的地方,所以的参数类型自然就是int了
释放链接
为了资源不要浪费,使用完了就应该直接释放了
resultSet.close();
statement.close();
connection.close();
防止sql注入的改良
发现问题
又细心的人就会发现前面的代码使用Statement拼字符串非常容易引发SQL注入的问题。
什么是SQL注入呢?我们一般查询数据库靠着相对的命令来实现,如果SQL语句是靠要查的字符拼接出来的,一般是没有问题的,但是我们输入一些特定的字符的时候是可能会让sql语句去做其他的事情。总之SQL一般都是因为字符的拼接漏洞实现的。
解决问题
所以我们就得想办法去解决这个问题,有一个方法是转义特定的字符,但这终究是治标不治本的。
前面我们提到,最根本的问题是字符拼接带来的漏洞,如果我们不进行字符拼接,直接传递要查的字符,那问题就引刃而解了
把 Statement 换成 PreparedStatement可以完全避免SQL注入的问题,因为PreparedStatement始终使用?作为占位符,并且把数据连同SQL本身传给数据库,这样可以保证每次传给数据库的SQL语句是相同的,只是占位符的数据不同,还能高效利用数据库本身对查询的缓存。
//使用prepareStatement查询
try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
try (PreparedStatement ps = conn.prepareStatement("SELECT id, grade, name, gender FROM students WHERE gender=? AND grade=?")) {
ps.setObject(1, "M"); // 注意:索引从1开始
ps.setObject(2, 3);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
long id = rs.getLong("id");
long grade = rs.getLong("grade");
String name = rs.getString("name");
String gender = rs.getString("gender");
}
}
}
}
//使用Statement查询
try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
try (Statement stmt = conn.createStatement()) {
try (ResultSet rs = stmt.executeQuery("SELECT id, grade, name, gender FROM students WHERE gender=1")) {
while (rs.next()) {
long id = rs.getLong(1); // 注意:索引从1开始
long grade = rs.getLong(2);
String name = rs.getString(3);
int gender = rs.getInt(4);
}
}
}
}
我们来看看这个例子,上面的是使用的prepareStatement,下面使用的是Statement。虽然两者之间没有太大的区别,但是还有值得我们注意的地方:
- sql语句插入的函数不同,前者是在prepareStatement就已经插入,而后者是在executeQuery才插入
- prepareStatement是需要使用setObject方法来指定我们查询的字符的,但是Statement是不用的,至于为什么不行我们后文再说
ResultSet的next()方法用于 移动游标 到 结果集中的下一行,返回的数据类型看代码无疑是一个boolean值
解决完问题带来的思考
我们从代码层面分析完成之后,我想很多人跟我有一样的提问,prepareStatement为什么能够做到防止SQL的注入,这里我们在稍微升入SQL注入一点,在详细剖析SQL注入是怎么完成的。
SQL注入的本质
SQL注入漏洞出现的原因就是用户的输入会直接嵌入到查询语句中,一旦出现精心设计的输入就会改变整个SQL语句的结构
比如现在有这样的语句
String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
如果输入了恶意的内容 username = "admin' --"
SELECT * FROM users WHERE username = 'admin' --' AND password = 'password';
后面输入的AND password = 'password';就直接被注释掉了,这样就会只查询前面的username = 'admin'
prepareStatement的防御原理
前面不是说了就是因为用户输入和查询语句不是分离的吗,那思路就很简单了,那将两者分离不就行了
占位符分离
在预编译阶段,SQL 查询的结构被解析并发送到数据库中,这时 占位符(?)会被数据库视为参数的占位符,而不是 SQL 语句的一部分。
例如:
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement ps = conn.prepareStatement(query);
ps.setString(1, username);
ps.setString(2, password);
在这里:
?是占位符,它在查询执行时被替换为实际的参数。ps.setString(1, username)和ps.setString(2, password)将用户输入的值安全地绑定到查询中。- 在执行查询时,数据库知道
?只是占位符,它不会将用户输入作为 SQL 代码的一部分来解析,而是将其作为数据处理。
即使用户输入恶意的内容,例如:
username = "admin' OR 1=1 --"password = "password"
构造的 SQL 查询也不会发生注入,因为数据库会将这些输入当作普通的字符串处理,而不会将其作为 SQL 语句的一部分。执行时的 SQL 查询将是:
SELECT * FROM users WHERE username = 'admin'' OR 1=1 --' AND password = 'password'
这个查询在数据库端仍然会被正确地作为两条字符串值传递,而不会被解析为恶意的 SQL 代码
自动转译用户输入
PreparedStatement 会自动转义参数中的特殊字符,如单引号(')等,使其在数据库中正确地作为字符串处理。这进一步防止了 SQL 注入攻击。
例如,如果用户输入的用户名是:
admin' --
PreparedStatement 会自动将这个字符串转义为:
'admin'' --'
这样,即使用户输入恶意的内容,数据库也会将其作为普通字符串处理,而不会被当作 SQL 语句的一部分执行。
JDBC中数据库的连接与查询的更多相关文章
- Java对MySQL数据库进行连接、查询和修改(转)
Java对MySQL数据库进行连接.查询和修改 0. 一般过程: (1) 调用Class.forName()方法加载驱动程序. (2) 调用DriverManager对象的getConnection( ...
- python中的MySQL数据库操作 连接 插入 查询 更新 操作
MySQL数据库 就数据库而言,连接之后就要对其操作.但是,目前那个名字叫做qiwsirtest的数据仅仅是空架子,没有什么可操作的,要操作它,就必须在里面建立“表”,什么是数据库的表呢?下面摘抄自维 ...
- JDBC开发,数据库的连接
JDBC开发步骤 1.建完工程后,右击工程,new,新建一个文件夹Folder,装jar包, 2.将下载好的驱动包打开,找到jar文件,CTRL+C,选中装jar包的文件夹,CTRL+V ...
- php中数据库服务器连接类库文件的编写
<!--数据库服务器连接类库文件的编写--> <?php class mysql{ //连接服务器.数据库以及执行Sql语句的类库 public $database; public ...
- jdbc创建数据库的连接
package com.gylhaut.db; import java.sql.Connection;import java.sql.DriverManager;import java.sql.Res ...
- Java对MySQL数据库进行连接、查询和修改【转载】
一般过程: (1) 调用Class.forName()方法加载驱动程序. (2) 调用DriverManager对象的getConnection()方法,获得一个Connection对象. (3) 创 ...
- Linq与数据库的连接显示查询(一)
使用linq查询sql数据库是首先需要创建一个 linq to sql 类文件 创建linq to sql的步骤: 1在Visual Studio 2015开发环境中建立一个目标框架 Fra ...
- SSM框架中数据库无法连接的问题
首先是SSM框架中所有的配置都是没有问题的,而且项目在其他人的环境上也能正常访问数据库:那么最有可能的就是数据库版本的问题导致数据库连接不上,服务器给我的报错是: 15:37:25.902 [C3P0 ...
- PHP中数据库的连接
<?php //1.链接MySQL服务器 $conn = mysql_connect("localhost", "root" , 199452); //2 ...
- springboot中数据库的连接
mysql5.0 1.#mysql数据库连接 2.spring.datasource.driver-class-name=com.mysql.jdbc.Driver 3.spring.dataso ...
随机推荐
- 聊一聊SQL优化
晚上睡不着,脑子里总想着一些问题,试着写一写对于SQL查询优化的见解. 首先,数据库有自己的查询优化器,执行一条查询SQL优化器会选择最优的方式(不走索引.走索引.走哪个索引), 所以索引不是越多越好 ...
- MySQL中的char与varchar
MySQL中的char与varchar char类型为固定长度的字符串 varchar类型是长度可变的字符串 char为固定长度的字符串意思是当我们设置一个字段类型为char时,指定char(100) ...
- 线性dp:最长公共子序列
最长公共子序列 本文讲解的题与leetcode1143.最长公共子序列这题一样,阅读完可以挑战一下. 力扣题目链接 题目叙述: 给定两个字符串,输出其最长公共子序列,并输出它的长度 输入: ADABE ...
- js_问题记录2022年6月24日19:35:12
小问题中的大问题 新建子js脚本一定记得创建函数,不然写什么都无法实现 比如 新建了new1.js脚本,首先需要创建function后才能在里面进行参数操作和调用 js实现的功能: 获取到对应的id= ...
- Mac 打开软件提示‘“xxx”已损坏,无法打开。您应该将它移到废纸篓。’解决方法
产生错误的原因是软件没有签名.使用下面的命令给软件签名就好了. sudo xattr -rd com.apple.quarantine /Applications/xxx.app
- 使用 nuxi build-module 命令构建 Nuxt 模块
title: 使用 nuxi build-module 命令构建 Nuxt 模块 date: 2024/8/31 updated: 2024/8/31 author: cmdragon excerpt ...
- HTML & CSS – dir, direction, writing-mode, ltr (left to rigth), rtl (right to left)
前言 世界上有很多语言的阅读方向是不同的. 英文 中文 (以前才有竖排文字, 现在中文和英语一样了) 阿拉伯文 (Arabic) 面对不同的语言, HTML 和 CSS 就需要不同的写法. 虽然我没有 ...
- CSS – z-index
介绍 z-index 是用来设置 element 层次高低的 (当 element 重叠的时候) 参考: 4 reasons your z-index isn't working (and how t ...
- QT原理与源码分析之QT反射机制原理
QT反射机制原理 本文将介绍QT反射机制创建QT对象实例的原理和流程以及源代码. 文章目录 QT反射机制创建QT对象实例 原理 流程 源码 QT反射机制创建QT对象实例 QT框架提供的基于元对象的反射 ...
- 一条 SQL 语句在 MySQL 中是如何执行的?
本篇文章会分析下一个 SQL 语句在 MySQL 中的执行流程,包括 SQL 的查询在 MySQL 内部会怎么流转,SQL 语句的更新是怎么完成的. 在分析之前我会先带着你看看 MySQL 的基础架构 ...