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");
    // 获取预编译的数据库操作对象PreparedStatement
    Connection 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、执行sql
        rs = 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.mfbz.cn/a/214885.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于 ESP32 的带触摸显示屏的 RFID 读取器

如何设计一款基于 ESP32 且具有 ILI9341 触摸屏显示屏且适合壁挂式安装的美观 RFID 读取器。 本项目中用到的东西 硬件组件 ESP32 开发套件 C 1 AZ-Touch ESP 套件 1 RFID-RC522 IC卡读写器 1 ​编辑 电线、绕包线 1 详细设计流程 …

机器学习 - 导论

简单了解 机器学习关于数据集的概念 、

Autosar COM通信PDU

文章目录 Autosar 中各个PDU所在示意图PDU的分类PDU 和 SDU 的关系I-PDUN-PDUL-PDU相关协议其他参考 Autosar 中各个PDU所在示意图 PDU的分类 在Autosar 中&#xff0c;主要有 I-PDU、N-PDU和 L-PDU 三种。 L-PDU&#xff1a;Data Link Layer PDU&#xff0c;数据链路层PDUN-…

Spring-Boot---项目创建和使用

文章目录 什么是Spring-Boot&#xff1f;Spring-Boot项目的创建使用Idea创建使用网页创建 项目目录介绍项目启动 什么是Spring-Boot&#xff1f; Spring的诞生是为了简化Java程序开发的&#xff1b;而Spring-Boot的诞生是为了简化Spring程序开发的。 Spring-Boot具有很多优点…

知识点滴 - 什么是半透膜和渗透压

半透膜和渗透作用 1748年的一天&#xff0c;法国物理学家诺勒为了改进酒的制作水平&#xff0c;设计了这样一个试验&#xff1a;在一个玻璃圆筒中装满酒精&#xff0c;用猪膀胱封住&#xff0c;然后把圆筒全部浸在水中。当他正要做下一步的工作时&#xff0c;突然发现&#xff…

巧用JAVA自带的API解决日期类问题

文章目录 题目代码优势 题目 特殊日期 代码 import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改 import java.time.LocalDate; public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);//在此输入您的代…

Java 学习之多态

多态的概念 多态 晚绑定。 所谓多态&#xff0c;就是父类型的引用可以指向子类型的对象&#xff0c;或者接口类型的引用可以指向实现该接口的类的实例。 不要把函数重载理解为多态。因为多态是一种运行期的行为&#xff0c;不是编译期的行为。 多态&#xff1a;父类型的引用可…

数据在内存中的存储(含面试题)

数据在内存中的存储 1. 整数在内存中的存储2. 大小端字节序和字节序判断2.1 什么是大小端&#xff1f;2.2 为什么有大小端?2.3 练习2.3.1 练习12.3.2 练习22.3.3 练习3第一题第二题 2.3.4 练习42.3.5 练习5第一题第二题 2.3.6 练习6 1. 整数在内存中的存储 在讲解操作符的时候…

若依的基本使用

演示使用网址:若依管理系统 网站:RuoYi 若依官方网站 |后台管理系统|权限管理系统|快速开发框架|企业管理系统|开源框架|微服务框架|前后端分离框架|开源后台系统|RuoYi|RuoYi-Vue|RuoYi-Cloud|RuoYi框架|RuoYi开源|RuoYi视频|若依视频|RuoYi开发文档|若依开发文档|Java开源框架…

2023/12/3总结

RabbitMq 消息队列 下载地址RabbitMQ: easy to use, flexible messaging and streaming — RabbitMQ 使用详情RabbitMQ使用教程(超详细)-CSDN博客 实现延迟队列&#xff08;为了实现订单15分钟后修改状态&#xff09; 1 死信队列 当一个队列中的消息满足下列情况之一时&…

基于hadoop下的Kafka分布式安装

简介 Kafka是一种分布式流处理平台&#xff0c;它具有高吞吐量、可扩展性、可靠性、实时性和灵活性等优点。它能够支持每秒数百万条消息的传输&#xff0c;并且可以通过增加节点来增加吞吐量和存储容量。Kafka通过将数据复制到多个节点来实现数据冗余和高可用性&#xff0c;即使…

【拓展】Loguru:更为优雅、简洁的Python 日志管理模块

目录 一、简单介绍 二、安装与简单使用 ​三、常见用法 3.1 显示格式 3.2 写入文件 3.3 json日志 3.4 日志绕接 3.5 并发安全 四、高级用法 4.1 接管标准日志logging 4.2 输出日志到网络服务器 4.2.1 自定义日志服务器 ​4.2.2 第三方库日志服务器 4.3 与pytest结…

RT-Thread 汇编分析启动流程

文章目录 一、汇编指令二、启动文件三、流程图 一、汇编指令 这里介绍即几条最常见实用的汇编指令 LDR R0,[R1]&#xff1a;将R1指定内存地址数据&#xff0c;存储到寄存器R0中。STR R0,[R1,#4]&#xff1a;将寄存器R0中数据存储到寄存器R1加上偏移量4的位置。MOV r0,#0x01&a…

read()之后操作系统都干了什么

首先说明三个参数 file文件 buff从内存中开辟一段缓冲区用来接收读取的数据 size表示这个缓冲区的大小 有关file的参数&#xff1a; 状态&#xff1a;被打开 被关闭权限&#xff1a;可读可写最重要的是inode: 他包含了 文件的元数据(比如文件大小 文件类型 文件在访问前需要加…

Google Earth Engine谷歌地球引擎计算多年中某两个时间点之间遥感数据差值的平均值

本文介绍在谷歌地球引擎GEE中&#xff0c;提取、计算某一种遥感影像产品在连续的多年中&#xff0c;2个不同时相的数据差值的多年平均值&#xff0c;并将计算得到的这一景差值的结果图像导出的方法。 本文是谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#x…

[二分查找]LeetCode2040:两个有序数组的第 K 小乘积

本文涉及的基础知识点 二分查找算法合集 题目 给你两个 从小到大排好序 且下标从 0 开始的整数数组 nums1 和 nums2 以及一个整数 k &#xff0c;请你返回第 k &#xff08;从 1 开始编号&#xff09;小的 nums1[i] * nums2[j] 的乘积&#xff0c;其中 0 < i < nums1.…

【Cell Signaling + 神经递质(neurotransmitter) ; 神经肽 】

Neuroscience EndocytosisExcitatory synapse pathwayGlutamatergic synapseInflammatory PainInhibitors of axonal regenerationNeurotrophin signaling pathwaySecreted Extracellular VesiclesSynaptic vesicle cycle

一个完整的手工构建的cuda动态链接库工程 03记

1&#xff0c; 源代码 仅仅是加入了模板函数和对应的 .cuh文件&#xff0c;当前的目录结构如下&#xff1a; icmm/gpu/add.cu #include <stdio.h> #include <cuda_runtime.h>#include "inc/add.cuh"// different name in this level for different type…

java企业财务管理系统springboot+jsp

1、基本内容 &#xff08;1&#xff09;搭建基础环境&#xff0c;下载JDK、开发工具eclipse/idea。 &#xff08;2&#xff09;通过HTML/CSS/JS搭建前端框架。 &#xff08;3&#xff09;下载MySql数据库&#xff0c;设计数据库表&#xff0c;用于存储系统数据。 &#xff08;4…

LongAddr

目录 1. 引言 2. AtomicInteger的局限性 3. AtomicInteger与LongAdder 的性能差异 4.LongAdder 的结构 LongAddr架构 Striped64中重要的属性 Striped64中一些变量或者方法的定义 Cell类 5. 分散热点的原理 具体流程图 6. 在实际项目中的应用 7. 总结 1. 引言 在这一…