04-数据库操作对象Statement对象和PreparedStatement对象的区别,SQL注入的优缺点

Statement对象和查询结果集

Statement对象相关的方法

Connection接口中获取数据库操作对象Statement对象的方法

方法名功能
Statement createStatement()创建Statement对象

Statement对象执行增删改查的SQL语句(不含占位符"?")的方法,JDBC中的SQL语句不需要提供分号结尾

方法名功能
int executeUpdate(insert/delete/update)执行dml语句(增删改),返回受影响的行数
ResultSet executeQuery(select)执行dql语句(查询),返回 ResultSet 结果集对象
Connection conn = null;
Statement stmt = null;
//1、注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
//3、获取数据库操作对象
stmt = conn.createStatement();
//4、执行SQL语句	
// String sql = "delete from dept where deptno = 40";
String sql = "update dept set dname = '销售部', loc = '天津' where deptno = 20";
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "修改成功" : "修改失败");

ResultSet查询结果集的方法

当我们使用数据库操作对象执行查询语句时会生成ResultSet结果集对象(包含查询到的数据),该对象保持一个光标指向当前的数据行(最初光标位于第一行之前)

在这里插入图片描述

ResulrSet结果集的常用方法: 取出当前行中字段的值时可以以结果集中字段的名称(语义更明确)或字段所在索引(下标从1开始)作为依据获取

方法名功能
boolean next()最初光标位于第一行之前,执行next方法会让光标向下一行移动,如果没有下一行返回 false
boolean previous()向上移动一行,如果没有上一行则返回false
String getString(列的索引/字段名)不管结果集中字段的数据类型是什么,都以String的形式取出字段值
Date getDate(列的索引/字段名)以Date的形式取出字段的值
int getInt(列的索引/字段名)以int的形式取出字段的值
Xxx getXxx( 列的索引/字段名 )以指定的数据类型取出结果集中的数据 , 前提是该数据类型可以正常转换
Object getObject( 列的索引/字段名 )以对象的形式取出结果集中的数据
String sql = "select empno as a,ename,sal from emp";
// 专门执行DQL语句的方法
rs = stmt.executeQuery(sql);// 处理查询结果集
while(rs.next()){// 以结果集中列的下标获取,JDBC中所有下标从1开始,不是从0开始String empno = rs.getString(1);String ename = rs.getString(2);String sal = rs.getString(3);System.out.println(empno + "," + ename + "," + sal);// 以结果集中列的名称获取,列名称不是数据库表中的列名称而是查询结果集的列名称// 除了以String类型取出之外,还能以特定的类型取出int empno = rs.getInt("a");String ename = rs.getString("ename");double sal = rs.getDouble("sal");System.out.println(empno + "," + ename + "," + (sal + 200));
}

SQL注入问题

SQL注入的优缺点

SQL注入问题: 在对SQL语句拼接时不使用占位符,而是将用户提供的非法信息直接拼接到到要执行的SQL语句当中,会导致原SQL语句的含义被扭曲了

  • 如将用户名zhangsan及其密码123456' or '1'='1(两个用or连接的条件去掉两边的引号)拼接到SQL语句当中
  • 字符串拼接变量或表达式的方法: 先加双引号中间加两个+号,两个加号中间加表达式
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
// select * from t_user where loginName = 'zhangsan' and loginPwd = '123456' or '1'='1';
String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";// 完成了sql语句的拼接后发给DBMS,然后DBMS对拼接好的sql语句进行编译
rs = stmt.executeQuery(sql);

SQL注入的用途:凡是业务方面要求进行SQL语句拼接的必须使用Statement对象,如用户在控制台输入desc/asc决定降序/升序

// 用户在控制台输入desc就是降序,输入asc就是升序
Scanner s = new Scanner(System.in);
System.out.println("输入desc或asc,desc表示降序,asc表示升序");
System.out.print("请输入:");
String keyWords = s.nextLine();Connection conn = null;
Statement stmt = null;
ResultSet rs = null;// 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");String sql = "select ename from emp order by ename ? " ;
// 给占位符传值后的结果select ename from emp order by ename 'desc或asc'(sql语法错误)
ps = conn.prepareStatement(sql);
ps.setString(1, keyWords);// 使用Statement对象进行sql语句拼接时不会出现问题
stmt = conn.createStatement();
String sql = "select ename from emp order by ename " + keyWords;
rs = stmt.executeQuery(sql);
// 遍历结果集
while(rs.next()){System.out.println(rs.getString("ename"));
}

PreparedStatement对象和查询结果集

PreparedStatement相关方法

Connection接口中获取数据库操作对象PreparedStatement对象的方法,创建对象的同时对SQL语句进行预编译

方法名功能
PreparedStatement prepareStatement(sql)创建预处理对象

PreparedStatement接口继承了java.sql.Statement,是预编译的数据库操作对象(SQL语句含占位符),可以解决SQL注入问题

  • 预先对包含占位符的SQL语句进行编译,然后再给占位符传值,即使用户提供的信息中含有sql语句关键字也无法参与编译过程,最终被当作普通的字符处理

将预编译的SQL语句的参数用占位符?(不能使用单引号括起来)表示,一个?表示一个占位符,最终调用ps对象的setXxx()方法给占位符传值(下标从1开始)

  • 一个PreparedStatement对象每次只能预编译一条SQL语句并且在编译阶段会做类型的安全检查

数据库第一次执行SQL语句时会先进行编译,如果第二次执行时SQL语句没有任何变化则直接执行不再编译

  • Statement对象: 编译一次执行一次且每次执行的是一个完整的SQL语句
  • PreparedStatement对象(效率较高): 预先对含占位符SQL语句的编译,然后调用ps对象的setXxx()方法给占位符传值,同一个SQL模板编译一次可执行N次

PreparedStatement接口中的方法: 方法的参数中不能再写SQL语句,否则就会重新编译SQL语句

方法名功能
int executeUpdate()执行dml语句(增删改),返回受影响的行数
ResultSet executeQuery()执行dql语句(查询),返回 ResultSet 结果集对象
execute()执行任意的sql,返回布尔值
void setXxx(占位符索引 , 占位符的值)给占位符设置对应类型的值,占位符下标从1开始
void setString(占位符索引 , 占位符的值)给占位符设置的值在sql语句中被当作字符串处理(自动加引号)
void setInt(占位符索引 , 占位符的值)给占位符设置的值在sql语句中被当作int类型的数据处理(不会加引号)

执行DQL语句

// 一个?表示一个占位符,一个?将来可以接收一个值
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
// 程序执行到此处会将占位符的sql语句发送给DBMS,然后DBMS对该sql语句进行预编译
ps = conn.prepareStatement(sql);
// 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始)
ps.setString(1, loginName);
ps.setString(2, loginPwd);
// 执行sql,ps已经预编译过了sql语句,如果再传就会重新编译sql语句
rs = ps.executeQuery();
// 处理结果集
if(rs.next()){// 登录成功loginSuccess = true;
}

执行DML语句

// 执行插入语句
String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setInt(1, 60);
ps.setString(2, "销售部");
ps.setString(3, "上海");// 执行更新语句
String sql = "update dept set dname = ?, loc = ? where deptno = ?";
ps2 = conn.prepareStatement(sql);
ps2.setString(1, "研发一部");
ps2.setString(2, "北京");
ps2.setInt(3, 60);// 执行删除语句
String sql = "delete from dept where deptno = ?";
ps3 = conn.prepareStatement(sql);
ps3.setInt(1, 60);
System.out.println(count);

模糊查询

查找第二个字母包含A的员工

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 使用工具类获取连接
conn = DBUtil.getConnection();// 以下是错误的写法,?一定不能用单引号括起来
String sql = "select ename from emp where ename like '_?%'";
ps = conn.prepareStatement(sql);
ps.setString(1, "A");// 正确写法
String sql = "select ename from emp where ename like ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "_A%");
rs = ps.executeQuery();
while(rs.next()){System.out.println(rs.getString("ename"));
}

模拟用户登录功能(防止注入)

需求: 从数据库中查询用户信息,实现用户登陆功能

public class JDBCTest {public static void main(String[] args) {// 初始化一个界面Map<String,String> userLoginInfo = initUI();// 验证用户名和密码boolean loginSuccess = login(userLoginInfo);// 最后输出结果System.out.println(loginSuccess ? "登录成功" : "登录失败");}
}

第一步: 初始化用户界面,可以让用户输入的用户名和密码等登录信息

private static Map<String, String> initUI() {Scanner s = new Scanner(System.in);System.out.print("用户名:");String loginName = s.nextLine();System.out.print("密码:");String loginPwd = s.nextLine();Map<String,String> userLoginInfo = new HashMap<>();userLoginInfo.put("loginName", loginName);userLoginInfo.put("loginPwd", loginPwd);return userLoginInfo;
}

第二步: 实现用户登陆的业务逻辑

private static boolean login(Map<String, String> userLoginInfo) {// false表示失败,true表示成功boolean loginSuccess = false;// 单独定义变量String loginName = userLoginInfo.get("loginName");String loginPwd = userLoginInfo.get("loginPwd");// 获取预编译的数据库操作对象PreparedStatementConnection conn = null;PreparedStatement ps = null; ResultSet rs = null;try {// 1、注册驱动Class.forName("com.mysql.jdbc.Driver");// 2、获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");// 3、获取预编译的数据库操作对象同时对SQL语句进行预编译String sql = "select * from t_user where loginName = ? and loginPwd = ?";ps = conn.prepareStatement(sql);// 给占位符?传值,下标从一开始ps.setString(1, loginName);ps.setString(2, loginPwd);// 4、执行sqlrs = ps.executeQuery();// 5、处理结果集if(rs.next()){// 登录成功loginSuccess = true;}} catch (Exception e) {e.printStackTrace();} finally {// 6、释放资源if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}return loginSuccess;
}

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

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

相关文章

Verilog 入门(八)(验证)

文章目录 编写测试验证程序波形产生值序列重复模式 测试验证程序实例从文本文件中读取向量实例&#xff1a;时序检测器 测试验证程序用于测试和验证设计方法的正确性。Verilog 提供强有力的结构来说明测试验证程序。 编写测试验证程序 测试验证程序有三个主要目的&#xff1a;…

minigpt4安装的一系列问题

问题一 合并权重&#xff1a; python -m fastchat.model.apply_delta --base /data/qq/llama2-7b-chat-hf --target /data/qq/MiniGPT-4/vicuna_weights/ --delta /data/qq/vicuna-7b-delta-v0fastchat版本与模型版本 不兼容 解决方法&#xff1a; 第一种&#xff1a;用低版…

【算法】滑动窗口题单——5.多指针滑动窗口醒醒⭐

文章目录 930. 和相同的二元子数组解法1——前缀和 哈希表解法2——滑动窗口 ⭐ 1248. 统计「优美子数组」1712. 将数组分成三个子数组的方案数⭐⭐⭐2444. 统计定界子数组的数目解法——多指针滑动窗口代码2——简洁写法&#xff1a;一次遍历O(1) 空间&#x1f402;⭐ 992. K…

《Junit单元测试》

目录 SpringBoot2.2.0版本之前的单元测试模式 SpringBoot2.2.0版本之后的单元测试模式 SpringBoot2.4以上版本移除了默认对Vintage的依赖 SpringBoot2.2.0版本之前的单元测试模式 SpringBooot 2.2.0 版本开始引入Junit5作为单元测试默认库&#xff0c;之前的版本是使用Junit…

Centos图形化界面封装OpenStack Centos镜像

目录 背景 环境 宿主机环境安装 创建与安装Centos7.8虚机 虚机设置 安全相关 安装ACPI服务 安装cloud-init 安装cloud-utils-growpart 停⽌虚拟机 删除个性化信息 模板化与压缩 登录与验证 背景 今天早上在Centos官网下载的CentOS-7-aarch64-GenericCloud-2003.…

nodejs介绍

nodejs官网支持的各种库api https://nodejs.org/docs/latest-v21.x/api/http.html nodejs包括vp8引擎和内置的基本库如fs,path,http,querystring等&#xff0c;也可以用npm按转第三方库 npm是nodejs环境的包管理工具&#xff0c;可以为这个环境安装卸载各种包。 npm install pk…

总结react中css的使用

1、css in js css in js有很多库&#xff0c;这里介绍styled-components styled-components 下载【vscode可以安装vscode-styled-components 插件&#xff0c;有代码提示】 npm i styled-components 1、然后为某个组件新建style.js文件&#xff0c;然后写一些样式。 impo…

【算法套路】(数组中)等价转换

文章目录 例题——2488. 统计中位数为 K 的子数组⭐【套路】子数组统计问题常用技巧&#xff1a;等价转换 相似题目列表面试题 17.05. 字母与数字525. 连续数组1124. 表现良好的最长时间段解法1解法2——利用单调栈 例题——2488. 统计中位数为 K 的子数组⭐ https://leetcode…

Proteus仿真--基于ADC0832设计的两路电压表

本文介绍基于ADC0832实现的双路电压表采集设计&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 采集芯片选用ADC0832&#xff0c;电压显示在LCD1602液晶显示屏上 仿真运行视频 Proteus仿真--基于ADC0832设计的两路电压表 附完整Proteus仿真资料代码资料…

【力扣206】反转链表

【力扣206】反转链表 一.题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1 &#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2 &#xff1a; 输入&#xff1a;head [1,2] 输出&#x…

sqlserver2019基本操作

一、下载管理工具 sql server2019官方的管理工具是SQL Server Management Studio 简称SSMS。 下载连接: https://learn.microsoft.com/zh-CN/sql/ssms/download-sql-server-management-studio-ssms?viewsql-server-ver16二、登录 1.连接服务器 点击 文件 --> 连接对象资…

harmonyOS学习笔记之stateStyles

stateStyles:多态样式 stateStyles可以依据组件的内部状态的不同,设置不同的样式 stateStyles是属性方法,可以根据状态来设置样式,类似于css伪类,但是语法不一样,ArkUI提供了四种状态: focused:获焦态 normal:正常态 pressed:按压态 disable:不可用态例如: Entry Component …

Springboot依赖注入时重复初始化Bean的问题

前言 最近做项目&#xff0c;发现了springboot2.7.x在参数initiate的时候可以反复初始化&#xff0c;而且首次异常后&#xff0c;第二次成功居然也可以启动&#xff0c;通过查看源代码发现了问题根源&#xff0c;且在springboot高版本3.x&#xff0c;就出现了了Configuration的…

qnx修改tcp和udp缓冲区默认大小

拷贝/home/test/qnx/qos223/target/qnx7/aarch64le/sbin/sysctl进系统中 https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.utilities/topic/s/sysctl.html kern.sbmax 默认262144&#xff0c;这个限制住了发送、接收缓冲器大小 ./sysctl -w kern.sbmax10000…

尺寸公差软件 AI自动化建模在电控器装配测量中的应用

在公差仿真分析中&#xff0c;公差仿真模型的建立&#xff0c;是耗时、繁琐&#xff0c;但又必需的一步&#xff1a;手动建立特征、手动建立装配、手动建立公差、手动建立测量。往往需要几天时至几十天&#xff0c;才能将模型建立完成。 幸运的是&#xff0c;随着AI&#xff0…

Kafka 架构深度解析:生产者(Producer)和消费者(Consumer)

Apache Kafka 作为分布式流处理平台&#xff0c;其架构中的生产者和消费者是核心组件&#xff0c;负责实现高效的消息生产和消费。本文将深入剖析 Kafka 架构中生产者和消费者的工作原理、核心概念以及高级功能。 Kafka 生产者&#xff08;Producer&#xff09; 1 发送消息到…

【Delphi】中使用Indy进行UDP广播通信

目录 一、服务器端&#xff08;接收端&#xff09; 二、客户端&#xff08;广播端&#xff09; Delphi中进行UDP广播通信函数代码&#xff1a; 一、服务器端&#xff08;接收端&#xff09; 在主界面上返放置一个TIdUDPServer控件&#xff0c;设置好该控件的监听端口&#…

专业课:递归非递归中序遍历

非递归中序遍历二叉树通常使用栈来辅助实现。 树结构&#xff1a; struct TreeNode {int data;TreeNode* left;TreeNode* right; };递归 void inorderTraversal(TreeNode *root){if(root ! nullptr){//中序遍历 “左孩子--根节点--右孩子”inOrder(root->lchild);printf(…

设计模式-结构型模式之代理设计模式

文章目录 八、代理设计模式 八、代理设计模式 代理设计模式通过代理控制对象的访问&#xff0c;可以详细访问某个对象的方法&#xff0c;在这个方法调用处理&#xff0c;或调用后处理。既(AOP微实现) 。 代理有分静态代理和动态代理&#xff1a; 静态代理&#xff1a;在程序…

c 实现的jpeg 8×8 离散余弦DCT 正向,逆向转换

理论公式&#xff1a; 验证数据 1.正向&#xff0c;数据源为YCbCr 88 数据 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <string.…