【JDBC系列】- 核心API之preparedstatement用法

news2024/11/24 2:38:52

核心API之preparedstatement用法

😄生命不息,写作不止
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 博客首页   @怒放吧德德  To记录领地
🌝分享学习心得,欢迎指正,大家一起学习成长!

在这里插入图片描述

引言

上一篇jdbc系列文章中介绍了概念与statement的使用,但是statement会有一些缺处,所以这篇来学习一下preparedstatement这个API的用法。

Statement的缺点

Statement的最大缺点是会有SQL注入的风险。使用Statement时,SQL查询通常是通过字符串拼接构建的。如果没有正确地验证和处理用户输入,可能会导致SQL注入攻击,即恶意用户可以在输入中插入恶意SQL代码,从而执行非授权的数据库操作。

例如以下SQL语句

String username = "lyd";
String sql = "SELECT * FROM tb_user WHERE username = '" + username + "'";

假设username是通过输入传过来的,如果再没做校验的情况下,当输入的值为lyd' or '1' = '1最后形成的SQL语句就变成了

String sql = "SELECT * FROM tb_user WHERE username = 'lyd' or '1' = '1'";

这样我们就能查到指定数据之外的数据了,这就会造成安全隐患。

更多缺点如下

  1. SQL注入风险: 使用Statement时,SQL查询通常是通过字符串拼接构建的。如果没有正确地验证和处理用户输入,可能会导致SQL注入攻击,即恶意用户可以在输入中插入恶意SQL代码,从而执行非授权的数据库操作。
  2. 性能问题: Statement执行SQL查询时,每次都要将SQL语句发送给数据库服务器进行编译。对于重复执行的查询,这会导致性能问题,因为数据库服务器需要重复编译相同的查询语句。
  3. 可读性和维护性: 使用Statement时,SQL查询通常以字符串形式嵌入Java代码中,这可能导致SQL语句在代码中分散分布,降低代码的可读性和维护性。特别是对于复杂的SQL查询,代码的可读性将变得更差。
  4. 不支持参数化查询:Statement中,SQL查询通常是硬编码在Java代码中的,没有很好地支持参数化查询。参数化查询是一种更安全和高效的方式,它允许将参数值与SQL语句分开,避免了SQL注入风险,并且可以利用数据库的查询缓存。

预编译statement - PreparedStatement

使用preparedstatement与使用statement的流程是一致的,只是预编译的需要多加一部去传入参数。

1、简单例子演示预编译statement

用个简单的例子来演示一下。与statement没什么太大的区别,主要是创建传输sql的对象不一样,不然其他基本都是相同的,这里需要注意的是,在使用预编译statement的SQL语句需要用“?”占位符来替代动态数据。在创建PreparedStatement对象之后,需要根据下标进行给占位符赋值,顺序是从左往右,从1开始。最后执行与statement大致相同,单纯查询就是用executeQuery。最后得到的结果集可以直接解析。

/**
 * @Author: lyd
 * @Description: 使用预编译Statement
 * @Date: 2023/7/23
 */
public class JDBCPreparedStatement {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        String name = "lyd";
        // 1、注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2、创建连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/cloud_user", "root", "12356");
        // 3、创建preparedstatement
        // 3.1、编写sql语句 不包含动态部分,动态值用占位符?来替代
        String sql = "SELECT * FROM tb_user WHERE username = ?";
        // 3.2、创建preparedstatement,传入动态值
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 3.3、占位符赋值: 从左向右,从1开始。set是有类型的,可以使用Object
        preparedStatement.setObject(1, name);
        // 4、发送sql  查询就用executeQuery
        ResultSet resultSet = preparedStatement.executeQuery();
        // 5、解析结果
        while (resultSet.next()) {
            System.out.println(resultSet.getString(1));
            System.out.println(resultSet.getString(2));
            System.out.println(resultSet.getString(3));
        }
        // 6、释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

2、进一步了解PreparedStatement

通过上面的简单实例,我们可以大致了解预编译statement无论是在安全上还是性能上都会比statement更胜一筹。我们进入PreparedStatement接口中可以看到他实际上是继承了Statement。
在这里插入图片描述

官方也提供了一个例子,通过下标给占位符去赋值。这样就能够很好的解决SQL注入的风险。

1)、执行DML语句

使用预编译statement执行DML语句,操作步骤与查询是一致的,只是在发送SQL的时候使用的是executeUpdate,他返回的并不是结果集,而是行数,正如我们用Navicat执行时显示的几行受影响一样,成功执行了多少行数据就会返回多少行,当执行失败就会返回0。

代码如下

public class JDBCDMLPreparedStatement {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        String name = "lyd";
        // 1、注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2、创建连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/cloud_user", "root", "12356");
        // 3、创建preparedstatement
        // 3.1、编写sql语句 不包含动态部分,动态值用占位符?来替代
        String sql = "INSERT INTO tb_user(id,username,address) VALUES (?,?,?)";
        // 3.2、创建preparedstatement,传入动态值
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 3.3、占位符赋值: 从左向右,从1开始。set是有类型的,可以使用Object
        preparedStatement.setObject(1, 3);
        preparedStatement.setObject(2, "opo");
        preparedStatement.setObject(3, "quanzhou");
        // 4、发送sql  DML语句使用
        int row = preparedStatement.executeUpdate();
        // 5、解析结果
        System.out.println(row + "行受影响");
        // 6、释放资源
        preparedStatement.close();
        connection.close();
    }
}

执行后,可以在数据库中看到新增了这条数据。
在这里插入图片描述

2)、执行DQL语句

这次我们获取所有数据,并且遍历结果集,将每行的数据保存到一个链表当中,用map进行key-value进行映射。

注册驱动

还是一样的方法,我们采用反射的方式来注册驱动。

Class.forName("com.mysql.cj.jdbc.Driver");
创建连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/cloud_user", "root", "12356");
创建preparedstatement

因为不需要传入参数,也就没有使用占位符,然而这里也就不需要去对占位符进行赋值,只需要创建preparedstatement对象,将SQL语句放入就行。

// 编写sql语句 不包含动态部分,动态值用占位符?来替代
String sql = "SELECT * FROM tb_user";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
发送SQL
// 发送sql  查询就用executeQuery
ResultSet resultSet = preparedStatement.executeQuery();
解析结果集

我们需要将每行数据进行提取,并且提取列名作为key而列值作为value来进行类似映射封装,最后存在List中。在之前就知道了ResultSet包含了一个行的游标,通过next可以将游标移动到下一行。然后可以通过get方法,来根据列标签或者是下表来获取值。如果要达成我们的目的,可以直接自定义key名称,再通过get方法获取值。虽然这样可以做到,但是灵活度不高。而本次要学习的是灵活度相对高一点的方案。

我们看一下数据集,这有3行数据,每行有3列,我们可以通过next方法来做到移动游标获取行数据,那么列呢?我们要如何才能获得数据库的列名或者查询数据的列标签呢?
在这里插入图片描述

ResultSetMetaData

ResultSet中有一个getMetaData()方法,它能够返回一个ResultSetMetaData对象,这个对象就是当前结果集列信息对象。我们通过源码可以看到这个列信息对象中包含了会话和字段等信息。

public class ResultSetMetaData implements java.sql.ResultSetMetaData {
    private Session session;
    private Field[] fields;
    boolean useOldAliasBehavior = false;
    boolean treatYearAsDate = true;
    private ExceptionInterceptor exceptionInterceptor;
    // ...
}

我们可以通过getColumnCount()方法获取这个结果集有多少个字段(列)。在package com.mysql.cj.jdbc.result.ResultSetMetaData#getColumnCount,实际上就是统计了 Field[] fields 这个字段数组的长度。

public int getColumnCount() throws SQLException {
    try {
        return this.fields.length;
    } catch (CJException var2) {
        throw SQLExceptionsMapping.translateException(var2, this.exceptionInterceptor);
    }
}

这样我们就可以在每行数据集中去遍历每列数据,这里需要注意的是,遍历的下标是从1开始到列长度数量。

获取列的信息就通过ResultSetMetaData对象获取,获取行相关信息就通过ResultSet对象获取,ResultSetMetaData能够获取列名或者列标签,建议使用列标签,因为有可能在查询的时候取了别名。

ResultSet resultSet = preparedStatement.executeQuery();
List<Map> list = new ArrayList<>();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
// 解析结果
while (resultSet.next()) {
    Map map = new HashMap<>();
    // 遍历数据
    for (int i = 1; i <= columnCount; i++) {
        // 通过下标获取列名 - 通过 ResultSetMetaData对象获取
        String columnLabel = metaData.getColumnLabel(i);
        // 通过下标获取列值 - 通过 ResultSet对象获取
        String value = resultSet.getString(i);
        map.put(columnLabel, value);
    }
    list.add(map);
}

运行之后的结果
在这里插入图片描述
ResultSetMetaData对象中还有许多的方法,如果是学习mybatis底层源码的时候,这些都是必不可少的资料。通过断点可以查看这个对象中的属性有哪些。这些后续会慢慢深入研究,并把学习成果分享出来。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker学习笔记——狂神说视频学习笔记

Ubuntu上docker安装 UBUNTU 20.04 LTS 安装DOCKER看高博主的博文&#xff0c;一键复制命令安装即可。 docker命令 docker version查看你docker版本信息 docker info显示docker系统级别的信息 docker --helpdocker命令查询 docker docs的referrence介绍了docker的详细命令 …

chrome查看浏览器内核日志

由于经常在网页上调试播放音视频&#xff0c;但是总遇到一些未知原因&#xff0c;导致无法正常播放&#xff0c;亟需查看浏览器内核日志&#xff0c;分析原因&#xff0c;做一下笔记。 (1) 查看浏览器快捷键属性 &#xff08;2&#xff09;在快捷键启动位置补充参数 --enable-…

[ 容器 ] Docker 的数据管理

目录 一、Docker 的数据管理1.1 数据卷2. 数据卷容器 二、 端口映射三、容器互联&#xff08;使用centos镜像&#xff09;四、Docker 镜像的创建1&#xff0e;基于现有镜像创建2&#xff0e;基于本地模板创建3&#xff0e;基于Dockerfile 创建3.1 联合文件系统&#xff08;Unio…

vue3前端分页,全选翻页状态保持

直接贴代码&#xff0c;代码中有注释 <template><div class"viewer-container" id"viewer-container"><!-- 表格 --><el-table:row-key"getRowKeys":data"data.tableDataCopy"style"width: 100%"ref&…

Go语言开发小技巧易错点100例(八)

往期回顾&#xff1a; Go语言开发小技巧&易错点100例&#xff08;一&#xff09;Go语言开发小技巧&易错点100例&#xff08;二&#xff09;Go语言开发小技巧&易错点100例&#xff08;三&#xff09;Go语言开发小技巧&易错点100例&#xff08;四&#xff09;Go…

Linux网络--UDP套接字

文章目录 预备知识socket套接字UDP网络编程 一、预备知识 1.源IP地址和目的IP地址 IP地址&#xff1a;标识计算机在网络中的唯一性。 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址。 源IP地址 &#xff1a; 网络通信的发起者。 目的IP地址 &#xff1a; …

element ui 上传控件携带参数到后端

1.携带固定参数&#xff1a; 2.携带不固定参数&#xff1a; <el-row> <el-col :span"24"> <el-upload :multiple"false" :show-file-list"false" :on-success"f_h…

矿井人员视频行为分析算法 opencv

矿井人员视频行为分析算法通过opencvpython网络模型技术&#xff0c;矿井人员视频行为分析算法实时监测人员的作业行为&#xff0c;并与安全标准进行比对&#xff0c;可以及时发现不符合安全要求的行为&#xff0c;预防事故的发生。OpenCV的全称是Open Source Computer Vision …

json-server Node.js 服务,前端模拟后端提供json接口服务

json-server Node.js 服务,前端模拟后端提供json接口服务 背景&#xff1a; 前后端分离的项目&#xff0c;如果前端写页面的话&#xff0c;必须的后端提供接口文件&#xff0c;作为前端等待时间太久&#xff0c;不便于开发进行&#xff0c;如果前端写的过程中自己搭建一个简要的…

vue3+taro+Nutui 开发小程序(一)

前言&#xff1a;最近在调研开发小程序&#xff0c;发现现在taro框架逐渐成熟&#xff0c;能完美地使用vue3来进行开发&#xff0c;调研中发现京东的Nutui也不错所以准备写一个由0到1的vue3taroNutui的小程序。 这篇我们首先搭建一个框架&#xff1a; vscode插件准备环节&…

【C++详解】——异常

目录 C语言传统的处理错误的方式 C异常概念 异常的使用 异常的抛出和捕获 异常的重新抛出 异常安全 异常规范 自定义异常体系 C标准库的异常体系 异常的优缺点 C语言传统的处理错误的方式 传统的错误处理机制 终止程序&#xff0c;如assert。缺陷&#xff1a;用户难…

国产化测试工具的特色有哪些?

在软件开发和系统运维过程中&#xff0c;测试工具的选择和应用对于确保软件质量和系统稳定性至关重要。随着我国信息技术的快速发展&#xff0c;国产化测试工具以其独特的特色在市场上崭露头角。那国产化测试工具的特色有哪些&#xff1f; 一、技术创新&#xff1a; 适应多样化…

在英特尔 CPU 上微调 Stable Diffusion 模型

扩散模型能够根据文本提示生成逼真的图像&#xff0c;这种能力促进了生成式人工智能的普及。人们已经开始把这些模型用在包括数据合成及内容创建在内的多个应用领域。Hugging Face Hub 包含超过 5 千个预训练的文生图 模型。这些模型与 Diffusers 库 结合使用&#xff0c;使得构…

【Qt】安装Qt 5.7.1 MSVC2013 64bit版本的说明

【Qt】安装Qt 5.7.1 MSVC2013 64bit版本的说明 1、背景2、安装Qt 5.7.13、运行Qt Creator 1、背景 刚开始Qt是C库&#xff0c;后来Qt发展就越来越强大了。后来Qt 发展成为一套跨平台C图形用户界面应用程序开发框架。 注意它不但可以开发GUI程序&#xff0c;而且也可用于开发非…

【数据架构】Data Fabric 架构是实现数据管理和集成现代化的关键

D&A 领导者应该了解数据编织架构的关键支柱&#xff0c;以实现机器支持的数据集成。 在日益多样化、分布式和复杂的环境中&#xff0c;数据管理敏捷性已成为组织的任务关键优先事项。为了减少人为错误和总体成本&#xff0c;数据和分析 (D&A) 领导者需要超越传统的数据…

LangChain+LLM大模型问答能力搭建与思考

1. 背景 最近&#xff0c;大模型&#xff08;LLMs&#xff0c;Large Language Models&#xff09;可谓是NLP领域&#xff0c;甚至整个科技领域最火热的技术了。凑巧的是&#xff0c;我本人恰好就是NLP算法工程师&#xff0c;面临着被LLMs浪潮淘汰的窘境&#xff0c;决定在焦虑…

【趟坑记录】d3.zoom()的正确使用姿势 @d3.v7

【趟坑记录】d3.zoom()的正确使用姿势 d3.v7 文章目录 【趟坑记录】d3.zoom()的正确使用姿势 d3.v7问题重现原因分析解决方案放缩平移写法特殊修改transform函数的写法 总结 在开发一个D3应用的时候遇到了一个 zoom相关的问题&#xff0c;记录解决思路与方案 问题重现 最近在…

nodejs+vue+elementui学习交流和学习笔记分享系统

Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。 前端技术&#xff1a;nodejsvueelementui,视图层其实质就是vue页面&#xff0c;通过编写vue页面从而展示在浏览器中&#xff0c;编写完成的vue页面要能够和控制器类进行交互&#xff0c;从而使得用户在点击网页进…

【Hello mysql】 mysql的事务

Mysql专栏&#xff1a;Mysql 本篇博客简介&#xff1a;介绍mysql的事务 mysql的事务 事务的概念事务功能测试事务的隔离级别如何理解隔离性&#xff08;粗浅理解&#xff09;隔离级别查看和设置隔离级别四种隔离级别详解读 -- 未提交读 - 提交可重复读串行化一致性的理解 总结 …

前端学习——ajax (Day4)

同步代码和异步代码 回调函数地狱和 Promise 链式调用 回调函数地狱 Promise - 链式调用 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge&quo…