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

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

时间:2024-11-01 20:19:20浏览次数:3  
标签:Web JDBC String 示例 数据库 sql SQL id conn

文章目录

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=convertToNull
        String 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, "[email protected]");
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;          // id
    private 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;     // 更新时间

    @Override
    public 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);
}

标签:Web,JDBC,String,示例,数据库,sql,SQL,id,conn
From: https://blog.csdn.net/m0_66584716/article/details/143440094

相关文章

  • ctfshow web入门 文件上传
    CtfshowWeb入门151 查看源代码,发现只能上传.png的文件用bp抓包.png的图片格式添加一句话木马,文件格式修改成.php   对于上传成功的后门代码,直接通过hackbar发送post包利用php内置system()函数执行  查看flag.php文件   152和上一题做题步骤一样......
  • webpack5配置传统jQuery多页面应用
    简介大家好,我是chenms,最近我们公司有要求需要开发几个以前传统的前后端不分离的jQuery老项目,现在大部分都是用vue或者react开发习惯了组件化的方式,所以我这边打算用webpack5配置一个可以打包传统jQuery多页面应用想法通过配置postcss给css自动加上前缀通过配置babel把e......
  • [网鼎杯 2020 朱雀组]phpweb
    打开靶机,抓包分析,获得连个关键参数func和p,根据初始页面提示了解连个参数大概是功能和功能参数测试func=system&p=ls提示hacker..说明有检测过滤那么我们先读取源码看看func=readfile&p=index.php点击查看代码<?php$disable_fun=array("exec","shell_exec","system......
  • (附源码)基于WEB的家乡特色农商品仓库管理系统的设计与实现-计算机毕设 26145
    基于WEB的家乡特色农商品仓库管理系统的设计与实现摘 要本论文旨在设计和实现基于WEB和SSM(Spring+SpringMVC+MyBatis)的家乡特色农商品仓库管理系统,以提高农产品仓储管理效率和服务水平。论文首先分析了传统农仓库信息管理存在的问题和需求,包括信息化程度低、管理效率不......
  • web集群项目-迁移与接入负载
    1.web01数据库迁移到db01项目背景:网站集群访问量或数据量越来越大单台机器无法承受.项目实时步骤:准备新环境部署数据库服务(版本一致)临时停止服务,旧环境备份,新环境恢复,测试修改数据库地址(用户,密码,库),指向新的环境(wp-config.php代码中连接数据库的配置文件)......
  • 【免费分享】WebGIS自学宝藏教程:Mapbox入门
    ⼀、简介https://www.mapbox.com/1、Mapbox简介Mapbox是⼀个可以创建各种⾃定义地图的⽹站,如Pinterest、Evernote、Github、500px等⼤牌都使⽤Mapbox创建⾃⼰的地图,Mapbox宣称要构建世界上最漂亮的地图。已为Foursquare、Pinterest、Evernote、⾦融时报、天⽓频道、......
  • WebSocket详解:从前端到后端的全栈理解
    文章目录前言一、WebSocket简介1.1WebSocket的特点二、WebSocket的工作原理2.1握手过程2.2数据传输三、WebSocket在前端的应用四、WebSocket在后端的应用五、WebSocket的局限与解决方案结语前言随着互联网技术的发展,传统的HTTP协议在某些场景下的局限性逐渐显......
  • SpringBoot:Failed to obtain JDBC Connection解决方案
    在第一次给自己的SpringBoot链接mysql的时候会出现很多问题可能的问题和解决方案本地的mysql没办法用root登陆这个一般是因为mysql在初始的时候默认root角色不能用密码登陆的原因可以用sudomysql先用最高权限进入mysql然后查看一下root的信息SELECTUser,Host,pl......
  • 如何使用WebSockets在网页应用中实现实时通信
    摘要:实现网页应用中的实时通信,1、选择合适的WebSockets库以简化实施过程;2、在服务器端与客户端建立WebSocket连接;3、设计有效的消息协议;4、确保通信安全性;5、处理网络问题和重连机制。其中选择合适的WebSockets库是基础。它能够帮助开发者快速构建实时通信功能,如Socket.IO、Web......
  • Qt5.9使用QWebEngineView加载网页速度慢 ,卡顿,原因是默认开启了代理
     Qt5.9使用QWebEngineView加载网页速度慢,卡顿,原因是默认开启了代理https://blog.csdn.net/zhanglixin999/article/details/131161944 BUG单下的留言讲明了问题发生的原因,那就是系统默认设置为自动寻找代理,而使用代理后延迟会变得非常大。(1)关闭自动代理接的pro文件内添......