[疯狂Java]JDBC:PreparedStatement预编译执行SQL语句

JDBC 动态代理在tenant-grid的应用 本文描述在JDBC层实现租户隔离时的三个坑和解决方案。 阅读详情

1. SQL语句的执行过程——Statement直接执行的弊病:

    1) SQL语句和编程语言一样,仅仅就会普通的文本字符串,首先数据库引擎无法识别这种文本字符串,而底层的CPU更不理解这些文本字符串(只懂二进制机器指令),因此SQL语句在执行之前肯定需要编译的;

    2) SQL语句的执行过程:提交SQL语句 -> 数据库引擎对SQL语句进行编译得到数据库可执行的代码 -> 执行SQL代码;

    3) 现在再来看Statement的执行机制:

         i. Statement的execute系列方法直接将SQL语句作为参数传入并提交给数据库执行;

         ii. 也就是说每提交一次都需要先经过编译然后再执行;

         iii. 那么有一个最大的问题就是如果一条SQL语句需要再短时间内被反复执行,那么每次都需要经过编译这样不是效率非常非常低吗??

!!可能你会问哪有需要反复大量执行的相同语句呢?仔细一想可能是的,因此上面说的并不完全精确,精确地讲应该是反复执行一系列模型相似的语句,比如:

insert into table1 values(1, "Peter");
!你每次执行时只是values中的值不同,但是总体的语句还是insert into语句,那么你每次提交都需要编译岂不是会把大把时间浪费在编译上面了,非常不值;


2. PreparedStatement的预编译机制——类似于Properties配置文件:

    1) 通过Connection(conn)还可以得到另一种SQL语句对象,即PreparedStatement,该方法就是:PreparedStatement Connection.prepareStatement(String sql);

    2) 注意细节:这里就不是create了,而是准备一个SQL语句句柄,精确地讲是一个PreparedStatement语句句柄,并且创建该句柄时直接传入了SQL语句;

    3) 预编译机制:

         i. 调用prepareStatement时会直接将该SQL语句提交给数据库进行编译,得到的PreparedStatement句柄其实是一个预编译好的SQL语句;

         ii. 之后调用PreparedStatement的execute方法(其execute系列方法都是无参的),就直接将该预编译的语句提交给数据库直接运行而不需要再编译一次了;

         iii. 因此这种方法只需要编译一次就够了,后面就是直接提交执行无需再编译,因此效率最高;

    4) 而预编译语句最大的特点就是支持占位符(支持的占位符就是?,代表任意长度的字符串),比如:insert into table1 values(null, ?, ?);

!!也就是说可以用带占位符的SQL语句来创建预编译SQL句柄:PreparedStatement pstmt = conn.prepareStatement("insert into table1 values(null, ?, ?)");

!!这样的语句也能通过,也可以成功编译,并且可以再后期决定这些占位符具体的值,即使改变这些值后依然不需要编译而直接提交运行;

    5) 设定占位符具体的值:

        i. 可以使用PreparedStatement的setXxx方法设定预编译语句中占位符的值;

        ii. 其原型是这个模式的:void PreparedStatement setXxx(int parameterIndex, Xxx x);

        iii. Xxx几乎涵盖了所有Java基础类型(String、int、double、Date等等);

        iv. parameterIndex代表语句中第几个占位符(从1开始),而x就是具体设定的值;

    6) 设置好占位符的值之后无需编译可以直接提交执行;

!!这种机制其实是跟Properties配置文件完全一样,修改值后无需编译即可运行!

    7) 直接提交执行:

         i. 使用PreparedStatement的execute系列方法即可,和Statement的execute系列方法相对应,只不过无需SQL语句参数了,因为已经存在预编译的SQL语句了,因此都是无参的,就表示直接提交执行;

         ii. 方法:

             a. ResultSet PreparedStatement.executeQuery();

             b. int PreparedStatement.executeUpdate();

             c. boolean PreparedStatement.execute();

!!返回值的意义和Statement的完全相同;

3. 比较直接提交和预编译运行的执行效率(各执行100次):

public class Test {

	private String driver;
	private String url;
	private String user;
	private String pass;
	
	private void insertUseStatement() throws Exception {
		long start = System.currentTimeMillis();
		try ( // 过了try块会直接释放连接资源
			Connection conn = DriverManager.getConnection(url, user, pass);
			Statement stmt = conn.createStatement()
		) {
			for (int i = 0; i < 100; i++) {
				stmt.executeUpdate("insert into student_table values(" + "null, '姓名" + i + "', 1)");
			}
			System.out.println("使用Statment耗时:" + (System.currentTimeMillis() - start));
		}
	}
	
	private void insertUsePreparedStatement() throws Exception {
		long start = System.currentTimeMillis();
		try ( // 因此需要在另一个方法中重新连接
			Connection conn = DriverManager.getConnection(url, user, pass);
			PreparedStatement pstmt = conn.prepareStatement("insert into student_table values(null, ?, 1)")
		) {
			for (int i = 0; i < 100; i++) {
				pstmt.setString(1,  "姓名" + i);
				pstmt.executeUpdate();
			}
			System.out.println("使用PreparedStatement耗时:" + (System.currentTimeMillis() - start));
		}
		
	}
	public void init() throws Exception {
		Properties props = new Properties();
		props.load(new FileInputStream("mysql.ini"));
		driver = props.getProperty("driver");
		url = props.getProperty("url");
		user = props.getProperty("user");
		pass = props.getProperty("pass");
		
		insertUseStatement();
		insertUsePreparedStatement();		
	}

	public static void main(String[] args) throws Exception {
		new Test().init();
	}

}
!!可以看到预编译比直接提交少用很多时间;


4. 预编译SQL的安全性能:

    1) 首先最明显的一点就是Statement不支持占位符,因此SQL语句中包含可变内容时必须要进行字符串拼接,而字符串拼接不仅加大了编程的难度,降低了代码的可读性,而且非常容易发生因拼接错误而导致地极难发现的bug,因此从这点来看PreparedStatement更加安全;

    2) 其次是字符串拼接容易埋下SQL注入的漏洞:

         i. SQL注入是指黑客在应用程序端恶意地往查询信息中填写SQL语句实现入侵(因为客户端输入的要查询的信息往往都是一些正常信息,例如姓名、电话、学号等,没人会无聊地往里面输入代码之类的东西);

         ii. 一个典型的例子:比如SQL语句的目的是select * from member_table where name = input_name and pass = input_pass; input_name和input_pass是用户在客户端输入框中输入的账号名和登陆密码,如果该查询语句能查询到该用户(即返回记录不为空)就表示该用户登陆成功;

如果用预编译占位符来表示该语句就是:select * from member_table where name = ? and pass = ?;   // 然后后期用input_name和input_pass来填补占位符,这没什么问题

但如果用Statement拼接的方式来写该语句就是:"select * from member_table where name = '" + input_name + "' and pass = '" + input_pass +"'";,而此时如果黑客在任意一个输入框(账户名或者密码)中填入'or true or'(就比如账户名输入框吧),那么得到的结果就是:

select * from member_table where name = '' or true or '' and pass = '';

!!也就是说最后的逻辑表达式变成了name = ''、true、'' and pass = ''三者通过or连接在了一起,因为or了一个true因此整个where表达式的结果都是true,因此必然会select处记录,因此即使这样也可以正常登陆!!这就被成功入侵了

         iii. 这最主要是由于不带占位符的拼接必须要用单引号'来包裹SQL字符串,而占位符的填写无需单引号,JDBC会自动将Java变量转换成纯字符串然后再自动加上SQL单引号填入占位符中,即使填入的变量是String str = "'Lala'",那么JDBC也会将其中的单引号' '转化成纯字符单引号处理,而不会被当做SQL的特殊字符单引号'来处理,因为在SQL中单引号'是字符串常量符号!


5. 占位符使用问题注意:

    1) 占位符只能占位SQL语句中的普通值,决不能占位表名、列名、SQL关键字(select、insert等);

    2) 原因很简单,以为PreparedStatement的SQL语句是要预编译的,如果关键字、列名、表名等被占位那就直接代表该SQL语句语法错误而无法编译,会直接抛出异常,因此只有不影响编译的部分可用占位符占位!!

数据库课程设计实战:从ER建模到JDBC CRUD完整闭环 数据库设计是信息系统开发的核心基础,涉及概念建模、逻辑转换、物理实现与应用集成四大环节。其核心原理在于将现实业务抽象为实体-联系(ER)模型,并严格遵循范式规则转化为关系模式;技术价值体现在数据一致性保障、业务逻辑可追溯性及系统可维护性提升。典型应用场景包括教学管理系统、电商后台、图书管理等轻量级业务系统。本文以网上书店为例,深度解析ER图到SQL建表的映射规则、外键约束与事务控制实践、JDBC参数化查询防注入机制,覆盖从需求分析到界面CRUD的全链路落地,特别聚焦orderlist关联表设计与useror 阅读详情

相关推荐

[LVGL] 刷新率优化

实验硬件:STM32F407ZGT6;2.4寸TFT-LCD模块(ILI9341驱动芯片、XTP2046触摸芯片)本文目标:lvgl的刷新率优化,解决拉帘子问题注意:本文在LVGL显示触摸代码移植完成的基础上进行,第一次使用lvgl可根据前文一步步移植,点击下方传送门查看相关文章传送门:STM32F4驱动ILI9341的TFT-LCD触摸屏(HAL库)(一)STM32F4驱动ILI9341的TFT-LCD触摸屏(HAL库)(二)[LVGL] 显示代码移植 基于STM32F4。

2301_77117901的博客 2784

JDBC小记——SQL注入及预编译操作对象、批处理

首先建一个工具包,在里面定义JBDC工具类但是,数据库、用户名和密码总不是固定不变的,当我们需要去改变时,需要打开这个工具类来改变数据,不如我们创建一个配置文件,使用配置文件来读取数据、修改数据来的方便。1.创建配置文件2.配置3.在工具类中使用配置文件。

m0_63138660的博客 1019

RTDETR多模态融合实战:基于注意力机制的红外与可见光目标检测优化

本文深入探讨了基于RTDETR模型的红外与可见光多模态目标检测优化方案。针对复杂光照下的检测难题,文章提出并实战演示了如何利用注意力机制(如SimAM与交叉注意力)实现RGB与IR特征的智能动态融合,从而构建更鲁棒的“全天候”检测系统,有效提升安防、自动驾驶等场景在夜间及恶劣天气下的检测精度与可靠性。

weixin_29083373的博客 188

【JDBC】JDBC如何实现预处理SQL?

2. 在PreparedStatement对象上调用setXXX()方法,为SQL语句中的参数设置值。其中XXX表示Java数据类型,例如setString()用于设置字符串类型参数,setInt()用于设置整型参数等等。首先创建了一个预处理语句对象pstmt,然后使用setInt()方法设置第一个参数为1,最后执行executeQuery()方法获取查询结果。3. 在PreparedStatement对象上调用executeQuery()或executeUpdate()方法来执行SQL语句。

后端研发工程师Marion的博客 1212

2025虚幻游戏逆向工程解包尝试

2025虚幻游戏逆向工程解包指南 本文详细介绍了虚幻引擎5游戏逆向解包的完整流程,主要包括获取AES密钥、生成映射文件和导出资源三个关键步骤。作者在实际操作中遇到诸多难题:AES密钥获取工具存在加壳保护问题,映射文件生成器依赖复杂的VS编译环境,DLL注入工具的使用也存在技术门槛。通过多次尝试,作者发现部分游戏可直接搜索现成的AES密钥,而映射文件生成需要额外下载Memcury依赖库才能正常编译。文章提供了相关工具的下载链接和具体操作说明,但同时也指出每个环节都可能出现意外状况,逆向工程实际执行过程远比理论

wsdchong的博客 6150

基于ROS2系统开发机器人

以上是基于ROS 2开发机器人的基本流程和一个简单的发布者-订阅者示例。通过这个流程,你可以逐步扩展和优化你的机器人系统,实现更复杂的功能。

xuweilin的博客 1495

Java JDBC下执行SQL的不同方式、参数化预编译防御

相关学习资料 http://zh.wikipedia.org/wiki/Java数据库连接 http://lavasoft.blog.51cto.com/62575/20588 http://blog.youkuaiyun.com/cxwen78/article/details/6863696 http://www.ibm.com/developerworks/cn/java/jdbc-objec...

weixin_34290631的博客 374

RAG系统中Embedding模型选型实战

本文详细介绍了RAG系统中Embedding模型选型实战,希望能对学习RAG的同学们有所帮助。 文章目录 1. 前言 2. 思路解析 3. 测评数据集构建 3.1 步骤一 3.2 步骤二 3.3 数据示例 3.4 总结 4. 实战代码 4.1 本地模型 4.2 使用第三方的Embedding模型API

herosunly的博客 17万+

JDBC连接执行SQL

JDBC

weixin_73849581的博客 3955

qiankun(微前端) - 解决Element组件库部分样式失效,主应用和子应用分别使用element-ui和element-plus,前端微应用qiankun样式无效污染隔离子应用样式影响其他应用

乾坤qiankun,微前端,vue,qiankun主应用使用vue2+elementui子应用使用vue3+element plus样式失效,子应用样式影响其他应用样式问题,qiankun项目与子应用样式混乱问题,彻底搞定样式污染,前端微应用qiankun样式污染,组件样式部分失效怎么办,互相影响,qiankun子应用中使用element-plus样式失效问题,组件库样式污染混乱,子应用样式影响到主应用怎么办,微前端qiankun子应用样式影响其他应用样式,样式冲突,Vue+qiankun框架样式混乱问题的

✅ 精选推荐文章 8874

4.4 直流无刷电机(BLDC):控制器硬件设计

摘要 本文详细阐述了直流无刷电机(BLDC)控制器的硬件设计要点。控制器硬件主要包括微处理器单元、功率驱动模块、信号检测电路、通信接口和电源管理模块。在微处理器选型方面,分析了ARM Cortex-M系列、DSP等架构的适用场景,并对比了不同处理器的性能参数。功率驱动模块设计重点包括栅极驱动电路、逆变桥热管理和电流保护机制。信号检测部分介绍了位置传感器接口和模拟信号调理技术。此外,还讨论了通信接口设计、电源管理方案以及硬件布局与EMC设计规范。最后强调了硬件测试验证的重要性,包括故障注入测试等验证方法。整体

技术工作者 183

Java数据库JDBC——prepareStatement的用法和解释

转自:http://blog.youkuaiyun.com/QH_JAVA/article/details/48245945 一、prepareStatement 的用法和解释 1.PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程 2.使用 Statement 对象。在对数据库只执行一次性存取的时侯,用 Statement 对象进行处理。Prepared...

nnzhuilian的博客 15万+

Puchem化合物数据批量抓取采集_宝典_教程_Python爬虫

本文共计6500字,导出到PDF达42页(含代码)。从0教你如何抓取Pubchem化合物数据。 1、 Pubchem介绍 ​ Pubchem是美国国立卫生研究院(NIH)的开放式化学数据库,自2004年推出以来,pubchem已成为科学家、学生和公众的重要化学信息资源。其专门针对化学结构搜索,是网络药理学研究经常用到的数据库之一,不仅使用方便而且免费。 ​ PubChem主要包含小分子,但也包含较大的分子,例如核苷酸,碳水化合物,脂质,肽和化学修饰的大分子。收集有关化学结构,标识符,化学和物理性..

qq_34843422的博客 6073

《疯狂java讲义》第13章 MySQL数据库与JDBC编程

第13章 MySQL数据库与JDBC编程 MySQL数据库和SQL语法 链接:数据库SQL和MySQL 之前学习数据库时B站看的,【Mosh老师的mysql教学笔记,转自知乎笔记】 13.3 JDBC的典型用法 JDBC的全称是Java Database Connectivity,即Java数据库连接,它是一种可以执行SOL语句的Java API。程序可通过JDBCAPI连接到关系数据库,并使用结构化查询语言(SOL,数据库标准的查询语言)来完成对数据库的查询、更新。 13.3.1 JDBC 4.2

weixin_43806049的博客 762

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

java mysql 占位符_在Java中编写带占位符的SQL语句

C#中SQL中带占位符的语句假设有一张学员信息表Student,通过表中的ID来找学员,查询的SQL语句如下string sql = string.Format("select * from Student where id={0} ",id);正常Java的语句String sql="select * from tbl_board where id=?";上面这种Java语句中的 ?也相当于一个...

weixin_30699915的博客 1762

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

java jdbc批处理_JavaWeb学习笔记(十五)—— 使用JDBC进行批处理

一、什么是批处理批处理就是一批一批的处理,而不是一个一个的处理!当你有10条SQL语句要执行时,一次向服务器发送一条SQL语句,这么做效率上很差!处理的方案是使用批处理,即一次向服务器发送多条SQL语句,然后由服务器一次性处理。批处理只针对更新(增、删、改)语句,批处理没有查询什么事儿!JDBC实现批处理有两种方式:statement和preparedstatement二、Statement批处理...

weixin_35995377的博客 357

JavaWeb学习笔记(十五)—— 使用JDBC进行批处理

一、什么是批处理   批处理就是一批一批的处理,而不是一个一个的处理!   当你有10条SQL语句要执行时,一次向服务器发送一条SQL语句,这么做效率上很差!处理的方案是使用批处理,即一次向服务器发送多条SQL语句,然后由服务器一次性处理。   批处理只针对更新(增、删、改)语句,批处理没有查询什么事儿!   JDBC实现批处理有两种方式:statement和preparedst...

VNOpU2015的专栏 206

疯狂Java学习笔记(50)-----------JDBC

疯狂Java学习笔记(50)-----------JDBC1、加载合适的JDBC驱动程序Class.forName(Driver);   MySQL驱动程序:com.mysql.jdbc.Driver   Oracle驱动程序:oracle.jdbc.driver.OracleDriver   SQLServer驱动程序:   com.Microsoft.jdbc.sqlserver.SQLSer...

Bruce小鬼 999

JDBC的典型用法

JDBC的典型用法 JDBC常用类和接口简介 ***DriverManager:用于管理JDBC驱动的服务类,主要是获取Connection对象 常用方法: (注意其返回的是Connection接口) *** Connection:代表数据库连接对象,每个Connection代表一个物理会话,要想访问数据库,必须先获得数据库连接 常用方法: Statement creatStatement()throws SQLException:该方法返回一个Statement对象 PrepareStatement

weixin_45821324的博客 283

java学习日记(29 JDBC)

01JDBC概念和数据库驱动程序 * JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API, * JDBC是java提供给开发人员的一套操作数据库的接口 * 数据库驱动就是实现该接口的实现类 数据准备 * a: 创建数据库和表结构 #创建数据库 create database mybas...

weixin_34217773的博客 84

JDBC自学笔记

教程在这里->https://www.bilibili.com/video/BV1eJ411c7rf?p=1 代码在这里->这里 资料,详细笔记,jar包等等在这里: 链接:https://pan.baidu.com/s/14gZhUeHLTomCMd6QELdqbA 提取码:3iqf 疯狂表白康康老师!!!! 自己记的笔记很零散,主要是敲代码为主。 这个部分的学习代码我放到了github中的learn的jdbclearn中 技术体系 HTML:显示 ...

小旻 207

Java SQL异常深度解析:从Access Denied到连接泄露的排查与防御

在Java企业级应用开发中,数据库连接与SQL执行是核心环节,而java.sql.SQLException作为JDBC规范定义的根异常,是数据库交互健康状况的关键指标。其背后涉及连接管理、SQL执行、事务控制与资源释放等基本原理。理解其成因与处理机制,对于保障应用稳定性、数据一致性具有重要技术价值,尤其在微服务与高并发场景下更为关键。本文聚焦常见的“Access denied”认证异常与连接泄露问题,通过剖析连接池配置、SQL优化及事务边界等实践,提供从快速定位到根治解决的系统性方案,并结合HikariCP

weixin_30225755的博客 339

--周记(杂)2019年7月19日

一. JDBC 1.// 注册驱动 使用 java . sql . DriverManager 类静态方法 registerDriver(Driver driver) 注册应使用 反射特性 Class . forName( "com . mysql . jdbc . Driver" ); 推荐 DriverManager . registerDriver(new com . mysq...

weixin_44195501的博客 234

MySQL JDBC驱动选型与配置全指南:版本匹配、URL参数与连接池调优

MySQL JDBC驱动是Java应用连接数据库的核心协议翻译器,其本质是JVM、MySQL服务端协议与应用框架(如Spring Boot)协同工作的运行时组件。理解驱动类名演变(com.mysql.jdbc.Driver → com.mysql.cj.jdbc.Driver)、SSL/时区/字符集等关键参数原理,可避免Class.forName失败、连接超时、中文乱码等高频问题。技术价值在于保障连接稳定性、提升批量操作性能、规避认证兼容性风险。典型应用场景包括Spring Boot数据源配置、Hikari

weixin_34216107的博客 408
上一篇: [疯狂Java]JDBC:Statement、ResultSet、连接资源自动关闭、Properties配置文件
下一篇: [疯狂Java]JDBC:CallableStatement执行存储过程
Lirx_Tech
博客等级 码龄12年 215粉丝 148原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值