【Java Web】使用JDBC操作数据库(含代码示例)

文章目录

    • JDBC主要组成部分
    • 访问数据库步骤
    • 数据库交互
      • Statement
      • PreparedStatement
      • SQL注入攻击
    • 演示示例
      • 单查询
      • 多查询
      • 返回记录数

JDBC(Java Database Connectivity)是Java中用于执行SQL语句的标准API,它提供了一种统一的方式来访问各种关系型数据库。JDBC使得开发者能够以一种独立于具体数据库的方式编写数据库访问代码。

JDBC主要组成部分

JDBC API:是一组接口和类,定义了与数据库交互所需的方法。

java.sql包中的核心接口包括DriverConnectionStatementPreparedStatementCallableStatementResultSet等。

javax.sql包则提供了更高级的功能,比如数据源(DataSource)、连接池管理以及分布式事务支持。

JDBC驱动程序:由特定数据库供应商提供的实现,它实现了 JDBC API。

驱动程序负责将 JDBC 调用转换为针对特定数据库系统的适当网络协议或本地库调用。例如 MySQL、Oracle、PostgreSQL 等都有各自的 JDBC 驱动。

JDBC-ODBC桥:是一个旧的机制,允许通过ODBC驱动来访问数据库。由于性能问题和不再维护的原因,在现代应用中已很少使用。

访问数据库步骤

使用JDBC进行数据库操作通常遵循以下步骤:

  1. 加载驱动:加载适当的JDBC驱动到内存中。

    Class.forName("com.mysql.cj.jdbc.Driver");
    
    // 其他数据库驱动
    SQL Server : com.microsoft.jdbc.sqlserver.SQLServerDriver
    MySql      : com.mysql.jdbc.Driver
    Oracle     : oracle.jdbc.driver.OracleDriver
    
  2. 创建连接:获取到数据库的连接。

    Connection conn = DriverManager.getConnection(url, username, password);
    
  3. 创建语句:创建一个Statement对象来发送SQL命令。

    Statement stmt = conn.createStatement();
    
  4. 执行查询/更新:执行SQL查询或者更新。

    ResultSet rs = stmt.executeQuery("SELECT * FROM table_name");
    
  5. 处理结果集:遍历ResultSet对象以获取查询结果。

    while (rs.next()) {// 处理每一行数据
    }
    
  6. 关闭资源:关闭ResultSetStatementConnection以释放资源。

    rs.close();
    stmt.close();
    conn.close();
    

在这里插入图片描述

在早期版本的 JDBC 中,通常需要显式地加载 JDBC 驱动程序,例如:

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

从 Java 6 开始,JDBC 4.0 引入了服务提供者机制(Service Provider Mechanism),允许自动加载 JDBC 驱动程序。

DriverManager 提供了多种 getConnection() 方法来建立数据库连接:

无参数方法

Connection conn = DriverManager.getConnection(url);

带用户名和密码的方法

Connection conn = DriverManager.getConnection(url, username, password);

带属性的方法

Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
// 其他属性...
Connection conn = DriverManager.getConnection(url, props);

使用 DriverManager 来加载驱动程序并建立数据库连接

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class DriverManagerExample {public static void main(String[] args) {// 数据库连接信息String url = "jdbc:mysql://localhost:3306/mydatabase";// 防止数据库乱码及日期出错// 放入url连接路径之后 ?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNullString username = "root";String password = "password";try {// 自动加载驱动程序(JDBC 4.0+)// Class.forName("com.mysql.cj.jdbc.Driver"); // 如果需要手动加载// 建立数据库连接Connection conn = DriverManager.getConnection(url, username, password);// 执行数据库操作...// 关闭连接conn.close();} catch (SQLException e) {e.printStackTrace();}}
}

数据库交互

在使用JDBC与数据库进行交互时,PreparedStatementStatement都是用于执行SQL语句的接口

Statement

  • 通过Connection对象的createStatement()方法创建
  • 适用于执行静态的、不包含参数的SQL语句
  • SQL语句直接作为字符串传递,并且可以在字符串中插入变量值(这种方式可能导致SQL注入)
int id = 1001;
String sql = "SELECT * FROM user WHERE id=" + id; // 注意这里的潜在SQL注入风险
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

执行一个SQL查询,根据用户提供的ID查找用户信息

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class PreparedStatementExample {public static void main(String[] args) {String url = "jdbc:mysql://localhost:3306/smbms";String username = "root";String password = "root";try (Connection conn = DriverManager.getConnection(url, username, password)) {// 创建PreparedStatement对象String sql = "SELECT * FROM smbms_user WHERE id = ?"; // 更改表名为正确的名称PreparedStatement pstmt = conn.prepareStatement(sql);int id = 1; // 假设我们需要查询ID为1的用户pstmt.setInt(1, id); // 设置第一个参数的值// 执行查询ResultSet rs = pstmt.executeQuery();// 处理结果集while (rs.next()) {System.out.println("id: " + rs.getInt("id"));System.out.println("userCode: " + rs.getString("userCode"));System.out.println("userName: " + rs.getString("userName"));// 处理其他字段...}// 关闭资源rs.close();pstmt.close();} catch (SQLException e) {e.printStackTrace();}}
}

Statement常用对象

ResultSet executeQuery(sql)

执行sql查询语句,并返回ResultSet对象

int executeUpdate(sql)

执行insert,update,delete语句,返回受影响行数

boolean execute(sql)

执行insert,update,delete语句,返回true或false false成功

executeUpdate() 方法执行更新操作,如插入记录

String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "John Doe");
pstmt.setString(2, "john.doe@example.com");
int rowsAffected = pstmt.executeUpdate();
System.out.println(rowsAffected + " row(s) inserted.");

PreparedStatement

  • 通过Connection对象的prepareStatement(String sql)方法创建
  • 适用于执行带有参数的SQL语句,这些参数用?占位符表示
  • 使用?作为参数占位符,然后通过setXxx()方法设置参数值(如setInt(), setString()等)
String sql = "SELECT * FROM user WHERE id=?";
PreparedStatement ps = conn.prepareStatement(sql);
int id = 1001;
ps.setInt(1, id); // 设置第一个参数的值
ResultSet rs = ps.executeQuery();

区别

  • PreparedStatement更适合于需要频繁执行的SQL语句,尤其是那些带有参数的查询。它提供了更好的性能、更高的安全性和更简洁的代码结构。
  • Statement适用于简单的、一次性执行的SQL语句。

SQL注入攻击

SQL注入攻击是一种常见的网络安全漏洞,它发生在当应用程序使用用户提供的输入来构造SQL查询,并且没有正确地过滤或转义这些输入时。攻击者可以通过在输入中插入恶意的SQL代码来操纵数据库查询,从而执行非授权的数据库操作,如读取敏感数据、修改数据、删除数据等。

// 假设这是用户提供的用户名
String userInput = request.getParameter("username");
// 构造一个查询语句
String query = "SELECT * FROM users WHERE username = '" + userInput + "'";
// 执行查询
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);

这段代码非常容易受到SQL注入攻击,因为userInput直接被嵌入到了SQL语句中。

SQL 注入类型

1、基于错误的 SQL 注入

// 攻击者的恶意输入
String userInput = "admin' OR '1'='1"; 
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND (SELECT 1/0) -- '

如果username字段是唯一的,上面的查询会返回所有记录,因为条件总是为真。如果应用程序抛出了异常并展示了该异常,则可能暴露了关于数据库的信息(通过故意制造一个除以零的错误来获取错误信息)。

2、联合查询注入

String userInput = "admin' UNION SELECT password, null FROM users -- ";
SELECT * FROM users WHERE username='admin' UNION SELECT password, null FROM users -- '

如果username是唯一的,那么联合查询可能会返回所有用户的密码。

3、布尔盲注

当应用程序不会直接返回任何数据但根据查询结果改变行为时(如页面布局变化),攻击者可以通过发送一系列的请求来猜测数据库中的信息。

String userInput = "admin' AND (SELECT CASE WHEN (1=1) THEN 1 ELSE 0 END) -- ";
SELECT * FROM users WHERE username = 'admin' AND (SELECT CASE WHEN (1=1) THEN 1 ELSE 0 END) -- '

如果查询成功执行并且有结果返回,那么条件(1=1)为真;反之则为假。

4、时间延迟盲注

当无法从响应内容得知查询结果时,攻击者可以通过使数据库等待一定时间来判断条件是否成立。

String userInput = "admin' AND IF(1=1, SLEEP(5), false) -- ";
SELECT * FROM users WHERE username = 'admin' AND IF(1=1, SLEEP(5), false) -- '

如果条件(1=1)为真,那么数据库会暂停5秒后再返回结果。

5、 堆叠查询

某些数据库系统允许多条SQL命令一次执行。攻击者可以利用这一点来执行额外的操作。可能不会有任何直接的数据返回给客户端,但可以在数据库上执行额外的操作,比如删除数据或修改记录。(这些操作可能是破坏性的,也可能用于进一步的信息收集)

String userInput = "admin'; DROP TABLE users; --";
SELECT * FROM users WHERE username = 'admin'; DROP TABLE users; --'

这个例子中,除了正常的查询外,还附加了一个删除users表的命令。如果服务器配置允许堆叠查询,那么DROP TABLE users将会被执行。

防御措施

对于所有这些类型的SQL注入攻击,最佳的防御方法是使用参数化查询或预编译语句(PreparedStatement),并确保对所有用户输入进行严格的验证和清理。此外,应该避免向用户显示详细的错误信息,并限制数据库账户的权限,只提供必要的最小权限。

演示示例

练习使用 JDBC 与 MySQL 数据库进行交互。编写 UserServer

三个方法操作 smbms_user 用户表:

  1. findUserById(Long id) - 该方法根据给定的用户ID查询单个用户的信息。
  2. findUserList() - 该方法查询所有用户,并返回一个包含所有用户的列表。
  3. findUserCount() - 该方法计算并返回用户表中的总记录数。

实体类

public class SmbmsUser {private Integer id;          // idprivate String userCode;     // 用户编码private String userName;     // 用户名称private String userPassword; // 用户密码private Integer gender;      // 性别private Date birthday;       // 出生日期private String phone;        // 电话private String address;      // 地址private Integer userRole;    // 用户角色private Integer createdBy;   // 创建者private Date creationDate;   // 创建时间private Integer modifyBy;    // 更新者private Date modifyDate;     // 更新时间@Overridepublic String toString() {// 重写 toString() 方法...}// get&set 方法...
}

单查询

// 根据 ID 查询用户
public SmbmsUser findUserById(Long id){Connection conn = null;Statement stat = null;try {// 1.加载驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.创建 Connection 对象(通过 DriverManager)String url = "jdbc:mysql://127.0.0.1:3306/smbms?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull";String user = "root";String pwd = "root";conn = DriverManager.getConnection(url,user,pwd);// 3.创建 Statement 对象(通过 Connection)stat = conn.createStatement();// 4.准备 SQL 语句String sql = "select id,userCode,userName,creationDate from smbms_user where id="+id;// 5.执行 SQL 语句(通过 Statement)ResultSet res = stat.executeQuery(sql);SmbmsUser smbmsUser = null;// 6.处理 resultSet 结果集while(res.next()){smbmsUser = new SmbmsUser();smbmsUser.setId(res.getInt("id"));smbmsUser.setUserCode(res.getString("userCode"));smbmsUser.setUserName(res.getString("userName"));smbmsUser.setCreationDate(res.getDate("creationDate"));}return smbmsUser;} catch (Exception e) {e.printStackTrace();} finally {//7.释放资源try {if(stat != null) stat.close();if(conn != null) conn.close();} catch (SQLException e) {throw new RuntimeException(e);}}return null;
}

多查询

// 查询多用户
public List<SmbmsUser> findUserList(){Connection conn = null;Statement stat = null;try {// 1.加载驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.创建 Connection 对象(通过 DriverManager)String url = "jdbc:mysql://127.0.0.1:3306/smbms?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull";String user = "root";String pwd = "root";conn = DriverManager.getConnection(url,user,pwd);// 3.创建 Statement 对象(通过 Connection)stat = conn.createStatement();// 4.准备 SQL 语句String sql = "select id,userCode,userName,creationDate from smbms_user";// 5.执行 SQL 语句(通过 Statement)ResultSet res = stat.executeQuery(sql);List<SmbmsUser> list = new ArrayList<>();// 6.处理 resultSet 结果集while(res.next()){SmbmsUser smbmsUser = new SmbmsUser();smbmsUser.setId(res.getInt("id"));smbmsUser.setUserCode(res.getString("userCode"));smbmsUser.setUserName(res.getString("userName"));smbmsUser.setCreationDate(res.getDate("creationDate"));list.add(smbmsUser);}return list;} catch (Exception e) {e.printStackTrace();} finally {//7.释放资源try {if(stat != null) stat.close();if(conn != null) conn.close();} catch (SQLException e) {throw new RuntimeException(e);}}return null;
}

返回记录数

// 返回记录数
public int findUserCount(){Connection conn = null;Statement stat = null;try {// 1.加载驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.创建 Connection 对象(通过 DriverManager)String url = "jdbc:mysql://127.0.0.1:3306/smbms?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull";String user = "root";String pwd = "root";conn = DriverManager.getConnection(url,user,pwd);// 3.创建 Statement 对象(通过 Connection)stat = conn.createStatement();// 4.准备 SQL 语句String sql = "select count(id) total from smbms_user";// 5.执行 SQL 语句(通过 Statement)ResultSet res = stat.executeQuery(sql);int total = 0;// 6.处理 resultSet 结果集while(res.next()){total = res.getInt("total");}return total;} catch (Exception e) {e.printStackTrace();} finally {//7.释放资源try {if(stat != null) stat.close();if(conn != null) conn.close();} catch (SQLException e) {throw new RuntimeException(e);}}return 0;
}

main 方法

public static void main(String[] args) {UserServer userServer = new UserServer();// 根据 ID 查询用户System.out.println("------- findUserById() 方法获取单个对象 -------");SmbmsUser smbmsUser = userServer.findUserById(2L);System.out.println(smbmsUser.toString());// 查询多用户System.out.println("------- findUserList() 方法获取对象集合 -------");List<SmbmsUser> smbmsUserList = userServer.findUserList();smbmsUserList.forEach(users -> System.out.println(users.toString()));// 返回记录数System.out.println("------- findUserCount() 方法返回记录数 -------");int total = userServer.findUserCount();System.out.println("总记录数为: " + total);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/461734.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[CUDA] atomic函数闭坑技巧

文章目录 1. 尽量减少atomic的使用频率2. 小心atomic的不规则读数 cuda atomic函数使用时的一些注意事项 1. 尽量减少atomic的使用频率 由于atomic会增加threads之间的同步性&#xff0c;所以在有选择性的atomic操作时&#xff0c;可以考虑用if(condition) atomic&#xff1b;…

开源全站第一个nextron项目--NextTalk:一款集成chatgpt的实时聊天工具

NextTalk 简介 该项目是一个基于Nextron(NextJSElectron)的桌面端实时聊天工具。 但由于使用了NextJS中的ssr及api route功能&#xff0c;该程序只能在开发环境运行。 关于生产版本&#xff1a;我将其网页端部分分离&#xff0c;并用Pake将其打包成桌面端&#xff0c;生产体…

河南高校大数据实验室建设案例分享

泰迪智能科技在与中国各地高校的合作中积累了丰富的经验&#xff0c;尤其是在大数据和人工智能领域。过去多年里与河南省内多所高校在大数据领域进行了积极的探索和建设&#xff0c;形成了一系列具有特色的大数据实验室。这些实验室不仅促进了高校内部的科研创新&#xff0c;也…

Kafka自动生产消息软件

点击下载《Kafka服务端(含Zookeeper)一键自启软件》 点击下载《kafka客户端生产者消费者kafka可视化工具&#xff08;可生产和消费消息&#xff09;》 点击下载《Kafka自动生产消息软件》 1. 前言 在软件开发过程中&#xff0c;Kafka常被用作消息队列来处理特定的业务功能。为…

龙迅#LT8668EX显示器图像处理芯片 适用于HDMI1.4+VGA转4PORT LVDS,支持4K30HZ分辨率,可做OSD菜单亮度调节!

1. 一般说明 LT8668EX 是 Lontium 的第二代 LCD 控制器&#xff0c;基于 ClearEdge 技术&#xff0c;支持 VGA 接口和 HDMI 接口&#xff0c;符合 HDMI 1.4 规范。它可以支持带 HDMI 接口的双模 DP。为了向后兼容&#xff0c;该 LCD 控制器还包括一个高性能模拟接口&#xff0…

分享SRC漏洞挖掘中js未授权漏洞挖掘的小技巧

文章目录 0x1 前言0x2 js未授权简介一、什么是未授权&#xff1f;二、常见的未授权访问漏洞三、js漏洞挖掘需要获取的几个信息四、如何挖掘五、浅谈 0x3浅谈 js未授权挖掘技巧一、常规js未授权挖掘二、浅谈交流 0x4 Findsomething应该怎样正确使用?问题一&#xff1a;findsome…

如何保护网站安全

1. 使用 Web 应用防火墙&#xff08;WAF&#xff09; 功能&#xff1a;WAF 可以实时检测和阻止 SQL 注入、跨站脚本&#xff08;XSS&#xff09;、文件包含等常见攻击。它通过分析 HTTP 流量来过滤恶意请求。 推荐&#xff1a;可以使用像 雷池社区版这样的 WAF&#xff0c;它提…

内感受性注意的电生理特征:频谱和源定位分析

摘要 对内感受信号的关注和有意识地处理能力被认为对最小自我、适应性自我调节和情感体验的发展&#xff0c;以及工具性和执行性认知功能的最佳表现至关重要。然而&#xff0c;尽管关于内感受推论解释的理论非常丰富&#xff0c;但实证证据仍然很少。在此基础上&#xff0c;本…

[OPEN SQL] FOR ALL ENTRIES IN

FOR ALL ENTRIES IN 语句用于从一个内部表中检索与另一个内部表中指定字段匹配的记录 语法格式 SELECT ... FOR ALL ENTRIES IN <itab> WHERE <cond>. <itab>&#xff1a;插入目标数据内表 <cond>&#xff1a;查询条件 使用FOR ALL ENTRY IN 语句时&…

GetX的一些高级API

目录 前言 一、一些常用的API 二、局部状态组件 1.可选的全局设置和手动配置 2.局部状态组件 1.ValueBuilder 1.特点 2.基本用法 2.ObxValue 1.特点 2.基本用法 前言 这篇文章主要讲解GetX的一些高级API和一些有用的小组件。 一、一些常用的API GetX提供了一些高级…

Windows下基于fping进行批量IP测试

fping是Linux下一个很好用的IP测试工具&#xff0c;结合代码可以完成批量的IP测试&#xff0c;在网络调试中用途很广。本文是基于fping for Windows结合bat批处理&#xff0c;定制的测试脚本样例。 一、程序信息 本次测试使用fpingV5.1 for Windows版&#xff0c;版本信息如下…

[MRCTF2020]你传你呢

[MRCTF2020]你传你&#x1f40e;呢 审题 一眼文件上传 知识点 .htaccess文件的作用 <FilesMatch "a.png"> SetHandler application/x-httpd-php </FilesMatch>将a.png当做php文件执行 AddType application/x-httpd-php .jpgAddType 是一个 Apach…

stm32cubeIde 使用笔记

划分flash空间 需要更改STM32xxx_FLASH.ld文件 输出其他格式文件

【Rust中的序列化:Serde(一)】

Rust中的序列化&#xff1a;Serde Serde是什么&#xff1f;什么是序列化序列化&#xff1f;Serde运行机制Serde Data ModelVistor ApiSerializer ApiDeserializer Api 具体示例流程分析具体步骤&#xff1a;那么依次这个结论是如何得出的呢?什么是de? 总结 Serde是什么&#…

【Flask】四、flask连接并操作数据库

目录 前言 一、 安装必要的库 二、配置数据库连接 三、定义模型 四、操作数据库 1.添加用户 2.删除用户 3.更新用户信息 4查询所有用户 五、测试结果 前言 在Flask框架中&#xff0c;数据库的操作是一个核心功能&#xff0c;它允许开发者与后端数据库进行交互&#xf…

手机实时提取SIM卡打电话的信令声音-新的篇章(三、Android虚拟声卡探索)

手机实时提取SIM卡打电话的信令声音-新的篇章(三、Android虚拟声卡探索) 前言 前面的篇章中&#xff0c;我们从理论方向和实际市面上出现的音频线传输声音的方式&#xff0c;讨论绕开手机对SIM卡电话通话声音的封锁场景的可行性&#xff0c;并实际选购几款数字和模拟的USB转接…

安装fpm,解决*.deb=> *.rpm

要从生成 .deb 包转换为 .rpm 包&#xff0c;可以按照以下步骤修改打包脚本 1. 使用 fpm 工具 fpm 是一个强大的跨平台打包工具&#xff0c;可以将 .deb 包重新打包成 .rpm&#xff0c;也可以直接从源文件打包成 .rpm。 安装 fpm sudo apt-get install ruby-dev sudo gem in…

青出于“蓝”的合资第一新能源,“换壳”背后有门道

文/王俣祺 导语&#xff1a;千呼万唤始出来的新能源“马6”终于亮相了&#xff0c;这款马自达EZ-6本以为凭借马自达多年来在国内市场深耕的底蕴可以收获一片支持&#xff0c;但最近却深陷“换壳”风波。那么今天我们就一起看看&#xff0c;这款马自达EZ-6和被冠以“原型”的深蓝…

HT8787B 可任意限幅、内置自适应升压的2x9.0W立体声音频功放

1 特性 ● 可任意配置的限幅功能 自由配置音频限制幅度&#xff0c;使输出音频信号限制在固定失真水平内 ● 内置自动限温控制功能 适应不同散热条件&#xff0c;避免出现过温关断现象 ● 高效自适应G类升压功能&#xff0c;有效延长播放时间 可调节最大限流值&#xff0c;有效…