how2j.cn

本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频



11分37秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器



步骤 1 : 使用PreparedStatement   
步骤 2 : PreparedStatement的优点1-参数设置   
步骤 3 : PreparedStatement的优点2-性能表现   
步骤 4 : PreparedStatement的优点3-防止SQL注入式攻击   
步骤 5 : 练习-性能比较   
步骤 6 : 答案-性能比较   

步骤 1 :

使用PreparedStatement

edit
和 Statement一样,PreparedStatement也是用来执行sql语句的
与创建Statement不同的是,需要根据sql语句创建PreparedStatement
除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接

注: 这是JAVA里唯二的基1的地方,另一个是查询语句中的ResultSet也是基1的。
package jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestJDBC { public static void main(String[] args) { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } String sql = "insert into hero values(null,?,?,?)"; try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin"); // 根据sql语句创建PreparedStatement PreparedStatement ps = c.prepareStatement(sql); ) { // 设置参数 ps.setString(1, "提莫"); ps.setFloat(2, 313.0f); ps.setInt(3, 50); // 执行 ps.execute(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
步骤 2 :

PreparedStatement的优点1-参数设置

edit
Statement 需要进行字符串拼接,可读性和维护性比较差

String sql = "insert into hero values(null,"+"'提莫'"+","+313.0f+","+50+")";

PreparedStatement 使用参数设置,可读性好,不易犯错

String sql = "insert into hero values(null,?,?,?)";
package jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; public class TestJDBC { public static void main(String[] args) { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } String sql = "insert into hero values(null,?,?,?)"; try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin"); Statement s = c.createStatement(); PreparedStatement ps = c.prepareStatement(sql); ) { // Statement需要进行字符串拼接,可读性和维修性比较差 String sql0 = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")"; s.execute(sql0); // PreparedStatement 使用参数设置,可读性好,不易犯错 // "insert into hero values(null,?,?,?)"; ps.setString(1, "提莫"); ps.setFloat(2, 313.0f); ps.setInt(3, 50); ps.execute(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
步骤 3 :

PreparedStatement的优点2-性能表现

edit
PreparedStatement有预编译机制,性能比Statement更快
package jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; public class TestJDBC { public static void main(String[] args) { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } String sql = "insert into hero values(null,?,?,?)"; try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin"); Statement s = c.createStatement(); PreparedStatement ps = c.prepareStatement(sql); ) { // Statement执行10次,需要10次把SQL语句传输到数据库端 // 数据库要对每一次来的SQL语句进行编译处理 for (int i = 0; i < 10; i++) { String sql0 = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")"; s.execute(sql0); } s.close(); // PreparedStatement 执行10次,只需要1次把SQL语句传输到数据库端 // 数据库对带?的SQL进行预编译 // 每次执行,只需要传输参数到数据库端 // 1. 网络传输量比Statement更小 // 2. 数据库不需要再进行编译,响应更快 for (int i = 0; i < 10; i++) { ps.setString(1, "提莫"); ps.setFloat(2, 313.0f); ps.setInt(3, 50); ps.execute(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
步骤 4 :

PreparedStatement的优点3-防止SQL注入式攻击

edit
假设name是用户提交来的数据

String name = "'盖伦' OR 1=1";

使用Statement就需要进行字符串拼接
拼接出来的语句是:

select * from hero where name = '盖伦' OR 1=1

因为有OR 1=1,这是恒成立的
那么就会把所有的英雄都查出来,而不只是盖伦
如果Hero表里的数据是海量的,比如几百万条,把这个表里的数据全部查出来
会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢

而PreparedStatement使用的是参数设置,就不会有这个问题
package jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class TestJDBC { public static void main(String[] args) { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } String sql = "select * from hero where name = ?"; try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin"); Statement s = c.createStatement(); PreparedStatement ps = c.prepareStatement(sql); ) { // 假设name是用户提交来的数据 String name = "'盖伦' OR 1=1"; String sql0 = "select * from hero where name = " + name; // 拼接出来的SQL语句就是 // select * from hero where name = '盖伦' OR 1=1 // 因为有OR 1=1,所以恒成立 // 那么就会把所有的英雄都查出来,而不只是盖伦 // 如果Hero表里的数据是海量的,比如几百万条,把这个表里的数据全部查出来 // 会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢 System.out.println(sql0); ResultSet rs0 = s.executeQuery(sql0); while (rs0.next()) { String heroName = rs0.getString("name"); System.out.println(heroName); } s.execute(sql0); // 使用预编译Statement就可以杜绝SQL注入 ps.setString(1, name); ResultSet rs = ps.executeQuery(); // 查不出数据出来 while (rs.next()) { String heroName = rs.getString("name"); System.out.println(heroName); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
步骤 5 :

练习-性能比较

edit  姿势不对,事倍功半! 点击查看做练习的正确姿势
向数据库中插入10000条数据,分别使用Statement和PreparedStatement,比较各自花的时间差异
步骤 6 :

答案-性能比较

edit
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费3个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费3个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

2分1秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器


可以看到PreparedStatement是要比Statement快一些的,但是不是很明显。

这个和不同的数据库有关系,如果在Oracle上测试,PreparedStatement的效率优势会更明显一些。
答案-性能比较
package jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; public class TestJDBC { public static void main(String[] args) { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } int count = 1000; long start = System.currentTimeMillis(); insertByPreparedStatment(count); long end = System.currentTimeMillis(); System.out.printf("使用预编译Statement插入%d条数据,耗时: %d毫秒%n", count, end - start); start = System.currentTimeMillis(); insertStatment(count); end = System.currentTimeMillis(); System.out.printf("使用Statement插入%d条数据,耗时: %d 毫秒%n", count, end - start); } private static void insertByPreparedStatment(int count) { String sql = "insert into hero values(null,?,?,?)"; try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root", "admin"); PreparedStatement ps = c.prepareStatement(sql);) { for (int i = 0; i < count; i++) { ps.setString(1, "提莫"); ps.setFloat(2, 313.0f); ps.setInt(3, 50); ps.execute(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static void insertStatment(int count) { try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root", "admin"); Statement s = c.createStatement();) { for (int i = 0; i < count; i++) { String sql = "insert into hero values(null," + "'提莫'" + "," + i + "," + i + ")"; s.execute(sql); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }


HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。


问答区域    
2024-07-14 预编译和非预编译的 statement SQL插入性能差别不大
虚心求学




两次测试,预编译和非预编译statement性能相差不明显。 插入1万次,结果显示非预编译反而稍微快一点。 测试次数:1000次 执行成功 Statement spend time is :2,764 ms 执行成功 PreparedStatement spend time is :2,459 ms 用时:非预编译>预编译 测试次数:10,000次 执行成功 Statement spend time is :24,066 ms 执行成功 PreparedStatement spend time is :25,516 ms 用时:预编译>非预编译 为了减小程序先后执行顺序导致的性能差异,开辟了两个单独的线程同时执行。 结果: 测试次数:10,000次 执行成功 Statement spend time is :43,952 ms 执行成功 PreparedStatement spend time is :43,960 ms 用时:预编译>非预编译
加载中
public static void main(String[] args) {
		executeCUD(String.format("insert into hero values(null,'%s',%.2f,%d);", "sql:gareen", 100.2f, 100));
		PreparedSQL();
	}

//预编译SQL插入
	public static void PreparedSQL() {
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
		} catch (Exception e) {
			e.printStackTrace();
		}
		String sql = "insert into hero value(null,?,?,?)";
		try (Connection c = DriverManager
				.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding = UTF-8", "root", "admin");
				PreparedStatement ps = c.prepareStatement(sql)) {
			long start = System.currentTimeMillis();
			for (int i = 0; i < 10000; i++) {
				ps.setString(1, "preSQL:gareen");
				ps.setFloat(2, 100.01f);
				ps.setInt(3, 50);
				ps.execute();
			}
			long end = System.currentTimeMillis();
			System.out.println("执行成功");
			System.out.printf("PreparedStatement spend time is :%,d ms%n", end - start);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}


//非预编译SQL插入
	public static void executeCUD(String sql) {
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
		} catch (Exception e) {
			e.printStackTrace();
		}
		try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
				"root", "admin"); Statement s = c.createStatement();) {
			long start = System.currentTimeMillis();
			for (int i = 0; i < 10000; i++) {
				s.execute(sql);
			}
			long end = System.currentTimeMillis();
			System.out.println("执行成功");
			System.out.printf("Statement spend time is :%,d ms%n", end - start);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2023-12-27 性能比较
起个名




向数据库中插入10000条数据,分别使用Statement和PreparedStatement,比较各自花的时间差异
加载中
try {
	Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
	e.printStackTrace();
} 
String sql = "insert into hero values(null,?,?,?)";
try (Connection c = DriverManager.getConnection(data_path,data_user, data_pwd);
		Statement s = c.createStatement();
		// 根据sql语句创建PreparedStatement
		PreparedStatement ps = c.prepareStatement(sql);
		) {	
		
		long time1 = System.currentTimeMillis();
		for (int i = 0; i < 10000; i++) {
			String sql0 = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
			s.execute(sql0);
		}
		long time2 = System.currentTimeMillis();
		long tim1 = time2- time1;
		System.out.printf("使用Statement循环插入10000条数据耗时%dms\n", tim1);
		s.close();
		
		long time3 = System.currentTimeMillis();
		for (int i = 0; i < 10000; i++) {
			ps.setString(1, "提莫");
			ps.setFloat(2, 313.0f);
			ps.setInt(3, 50);
			ps.execute();
		}
		long time4 = System.currentTimeMillis();
		long tim2 = time4 - time3;
		System.out.printf("使用PreparedStatement循环插入10000条数据耗时%dms\n", tim2);
} catch (SQLException e) {
	e.printStackTrace();
}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2021-04-11 preparedstatement的方法中参数问题
2021-01-31 结果不尽人意
2020-11-23 英雄联盟上号


提问太多,页面渲染太慢,为了加快渲染速度,本页最多只显示几条提问。还有 32 条以前的提问,请 点击查看

提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 JAVA 中级-JDBC-预编译Statement 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 578362961
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: https://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图