`
IvanLi
  • 浏览: 602795 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

PreparedStatement真的比Statement快吗?

阅读更多
从开始写java程序就一直被灌输着一种思想,如果使用jdbc一定要使用PreparedStatement,而不要使用Statement对象。
其中的原因有好多,比如可以防止SQL注入攻击,防止数据库缓冲池溢出,代码的可读性,可维护性。这些都很正确。
但是还有一点人们经常提的就是PreparedStatement能够显著的提高执行效率。
看了两篇关于PreparedStatement和Statement的帖子
http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=121&threadID=10397&start=0&tstart=0
http://www.iteye.com/topic/5631?page=1
里面提到,PreparedStatement并不一定比Statement快,于是想自己动手测试一下
一共分5种情况测试
stmt	  pre-stmt	
--1:每次都单独连数据库--100条
21.936	22.076
22.05	  22.051	
22.172	22.178	
--2:Stmt使用相同的SQL语句,参数值也相同--100条
0.78	  0.796	
0.718	  0.749	
0.749	  0.764	
--3:Stmt使用相同的SQL语句,参数值不同--100条
0.967	  0.749	
0.78	  0.874	
0.749	  0.749	
--4:Stmt使用相同的SQL语句,参数值也相同--10000条
33.079	36.154	
34.156	39.306	
34.359	36.138	
--5:Stmt使用相同的SQL语句,参数值不同--10000条
32.799	34.125	
32.564	35.53	
35.701	34.952	
32.582	40.798	
38.893	42.345	
35.082	41.736	

分析:
第一种情况:
由于我测试时采用jdbc直接链接数据库,所以在第一种情况下插入速度很慢,时间都消耗在了数据库链接的创建上了,符合常理,没什么疑义。
第二种情况:
由于使用Statement插入时sql语句都相同,我通过阅读上面的两篇帖子,猜想可能是Statement语句也被DB缓存了,所以跟
PreparedStatement速度差不多,我想如果每次插入的sql都不同,那么Statement应该慢下来了吧。
第三种情况:
并非像我在执行第二种情况时想象的那样,PreparedStatement还是跟Statement差不多,于是我想是不是数据量不够大呀,于是我增大了
插入数据量,希望PreparedStatement能够比Statement快些
第四/五种情况:
测试结果PreparedStatement反到更慢了。

PreparedStatement什么情况下才会比Statement快?那位大侠来指点一下!

代码如下:
DDL
CREATE TABLE dbo.jdbc_stmt 
(
    id   numeric(10,0) IDENTITY,
    name varchar(10)   NOT NULL,
    grad int           NOT NULL,
    age  int           NOT NULL,
    dept int           NOT NULL
)

测试程序
public void jdbcStmtPerformance()
    {
    	//1:每次都单独连数据库--100条
//    	String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)";
//    	for(int i = 0; i< 100; i++)
//    	{
//    		logger.debug("stmt #"+i);
//    		getJdbcTemplate().update(stmtSQL);
//    	}
    	
    	//2:Stmt使用相同的SQL语句,参数值也相同--100条
//    	getJdbcTemplate().execute(new StatementCallback(){
//    		public Object doInStatement(Statement stmt) throws SQLException
//    		{
//    			String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)";
//    	    	for(int i = 0; i< 100; i++)
//    	    	{
//    	    		logger.debug("stmt #"+i);
//    	    		stmt.executeUpdate(stmtSQL);
//    	    	}
//    			return null;
//    		}
//    	});
    	
    	//3:Stmt使用相同的SQL语句,参数值不同--100条
//    	getJdbcTemplate().execute(new StatementCallback(){
//    		public Object doInStatement(Statement stmt) throws SQLException
//    		{
//    			String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,";
//    	    	for(int i = 0; i< 100; i++)
//    	    	{
//    	    		logger.debug("stmt #"+i);
//    	    		stmt.executeUpdate(stmtSQL+i+")");
//    	    	}
//    			return null;
//    		}
//    	});
    	
//    	4:Stmt使用相同的SQL语句,参数值也相同--10000条
//    	getJdbcTemplate().execute(new StatementCallback(){
//    		public Object doInStatement(Statement stmt) throws SQLException
//    		{
//    			String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)";
//    	    	for(int i = 0; i< 10000; i++)
//    	    	{
//    	    		logger.debug("stmt #"+i);
//    	    		stmt.executeUpdate(stmtSQL);
//    	    	}
//    			return null;
//    		}
//    	});
    	
//    	5:Stmt使用相同的SQL语句,参数值不同--10000条
    	getJdbcTemplate().execute(new StatementCallback(){
    		public Object doInStatement(Statement stmt) throws SQLException
    		{
    			String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,";
    	    	for(int i = 0; i< 10000; i++)
    	    	{
    	    		logger.debug("stmt #"+i);
    	    		stmt.executeUpdate(stmtSQL+i+")");
    	    	}
    			return null;
    		}
    	});
    }
    
    
    public void jdbcPreStmtPerformance()
    {
    	//1:每次都单独连数据库--100条
//    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
//    	for(int i = 0; i< 100; i++)
//    	{
//    		logger.debug("pre-stmt #"+i);
//    		getJdbcTemplate().update(stmtSQL, new Object[]{"ivanl", new Integer(2), new Integer(25), new Integer(1002)});
//    	}
    	
    	//2:Stmt使用相同的SQL语句,参数值也相同--100条
//    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
//    	getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){
//    		public Object doInPreparedStatement(PreparedStatement ps)  throws SQLException
//    		{
//    	    	for(int i = 0; i< 100; i++)
//    	    	{
//    	    		logger.debug("pre-stmt #"+i);
//    	    		ps.setString(1, "ivanl");
//    	    		ps.setInt(2, 2);
//    	    		ps.setInt(3, 25);
//    	    		ps.setInt(4, 1002);
//    	    		ps.execute();
//    	    	}
//    			return null;
//    		}
//    	});
    	//3:Stmt使用相同的SQL语句,参数值不同--100条
//    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
//    	getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){
//    		public Object doInPreparedStatement(PreparedStatement ps)  throws SQLException
//    		{
//    	    	for(int i = 0; i< 100; i++)
//    	    	{
//    	    		logger.debug("pre-stmt #"+i);
//    	    		ps.setString(1, "ivanl");
//    	    		ps.setInt(2, 2);
//    	    		ps.setInt(3, 25);
//    	    		ps.setInt(4, i);
//    	    		ps.execute();
//    	    	}
//    			return null;
//    		}
//    	});
    	//4:Stmt使用相同的SQL语句,参数值也相同--10000条
//    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
//    	getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){
//		public Object doInPreparedStatement(PreparedStatement ps)  throws SQLException
//		{
//	    	for(int i = 0; i< 10000; i++)
//	    	{
//	    		logger.debug("pre-stmt #"+i);
//	    		ps.setString(1, "ivanl");
//	    		ps.setInt(2, 2);
//	    		ps.setInt(3, 25);
//	    		ps.setInt(4, 1002);
//	    		ps.execute();
//	    	}
//			return null;
//		}
//	});
    	//5:Stmt使用相同的SQL语句,参数值不同--10000条
    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
    	getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){
    		public Object doInPreparedStatement(PreparedStatement ps)  throws SQLException
    		{
    	    	for(int i = 0; i< 10000; i++)
    	    	{
    	    		logger.debug("pre-stmt #"+i);
    	    		ps.setString(1, "ivanl");
    	    		ps.setInt(2, 2);
    	    		ps.setInt(3, 25);
    	    		ps.setInt(4, i);
    	    		ps.execute();
    	    	}
    			return null;
    		}
    	});
    	
    }
分享到:
评论
9 楼 kwang 2007-01-23  
楼上的,如果你把Statement st = con.createStatement(); 
和PreparedStatement pst = con.prepareStatement(psql); 都写在for循环外面,应该就更能看出谁快谁慢了。我在roacle里测了一下,查询5000条数据,PreparedStatement比Statement快一倍。
8 楼 codeplay 2007-01-23  
count大点然后Math.random()*100 换成 Math.random()*10000000结果会不会更明显?

对于prepared stmt和stmt,Oracle应该都缓存其编译后的stmt,缓存100个对它来说太易了

对这个测试来说,所有pre stmt只需要编译一个即可,而stmt只要id不同就得编译
7 楼 LucasLee 2007-01-22  
我对PreparedStatement和Statement的性能测试了一下:
测试代码如下:
Connection con = getOraConnection();
String sql = "select id,name from test where id=";
String tempSql;
int count = 1000;
long time = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
	Statement st = con.createStatement();
	tempSql=sql+(int) (Math.random() * 100);
	st.executeQuery(tempSql);
	st.close();
}
System.out.println("st cost:" + (System.currentTimeMillis() - time));

String psql = "select id,name from test where id=?";
time = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
	int id=(int) (Math.random() * 100);
	PreparedStatement pst = con.prepareStatement(psql);
	pst.setBigDecimal(1, new BigDecimal(id));
	pst.executeQuery();
	pst.close();
}

System.out.println("pst cost:" + (System.currentTimeMillis() - time));

con.close();


test表很简单,id int,name varchar(50).

对几种数据库和相应驱动程序进行测试,结果如下(ms):
oracle: 1235 1109
MSSQL2000(JTDS):391 453
MSSQL2000(MS): 453 640
Mysql5: 391 891
PostgreSQL8.1:  1078    1047
结论:
1.单并发情况下,oracle--PostgreSQL--MSSQL--MySQL的性能依次增高;
2.MySQL不支持Prepared Statement特性,已在其驱动程序的文档中证实,所以在MySQL里使用  PreparedStatement的性能尤其低,可以比Statement慢一倍以上。
  而MSSQL2000下,PreparedStatement比Statement慢,
  Oracle,PostgreSQL对它的支持最好,使用PreparedStatement性能比Statement高。
6 楼 MountLion 2007-01-22  
不同类型的数据库对此的优化程度可能是不同的;
单用户状态下和多用户状态下也是不同的。
5 楼 LucasLee 2007-01-22  
我也怀疑楼主的测试方法。
不过我按我的方法测试了一下,
使用select * from test where id=?的查询,而不是insert。
我在循环里关闭statement。

但每次都是statement快一些。


不过我以前的确用PreparedStatement替代Statement得到了数倍的性能提高,
那时的情况是:字段数量很大,数百个字段(表是自动创建的),单次需要插入数千条记录,
所有使用了批处理。即便都是批处理,此时PreparedStatement还是能快约6倍。
4 楼 dengyin2000 2007-01-22  
Ivan Li 写道
这两个方法是使用junit分别测试的,使用的不是同一个stmt


你误解我的意思了。 这个跟junit有什么关系
3 楼 IvanLi 2007-01-22  
这两个方法是使用junit分别测试的,使用的不是同一个stmt
2 楼 dengyin2000 2007-01-22  
PreparedStatement预编译应该是在数据库server端吧。是不是要close Statement 和 PreparedStatement来测试。因为如果都是使用同一个stmt的话, 就没有考虑到编译的效率了。
1 楼 deafwolf 2007-01-22  
PreparedStatement的目的主要是批处理,跟Statement比顺序执行的效率本来就是不合理的测试方式

相关推荐

    java中PreparedStatement和Statement的区别

    Java 中 PreparedStatement 和 Statement 的区别 Java 中的 PreparedStatement 和 Statement 都是用于执行 SQL 语句的接口,但是它们之间存在一些关键的区别。 首先,从数据库执行 SQL 语句的角度来看,使用 ...

    Statement和PreparedStatement之间的区别

    这种差异导致PreparedStatement对象执行速度更快,特别是在执行多次相同的SQL语句时。 其次,PreparedStatement对象可以避免SQL注入攻击。在 Statement 对象中,如果用户输入的数据包含恶意代码,例如 Drop 表名,...

    34.jdbc中preparedStatement比Statement的好处.avi

    jdbc中preparedStatement比Statement的好处

    PreparedStatement和Statement

    - **性能**:由于预编译,`PreparedStatement`通常比`Statement`更快,尤其是在大量重复执行相同SQL时。 - **安全性**:`PreparedStatement`通过占位符防止SQL注入,而`Statement`则容易受到这类攻击。 - **...

    Java面试题34.jdbc中preparedStatement比Statement的好处.mp4

    Java面试题34.jdbc中preparedStatement比Statement的好处.mp4

    如何获得PreparedStatement最终执行的sql语句

    一种可能的方法是通过日志配置,例如,启用MySQL的`Statement`和`PreparedStatement`日志,这样在执行时会打印出详细的SQL语句。 另外,开发工具如IntelliJ IDEA、Eclipse等,或者数据库管理工具如MySQL Workbench...

    PreparedStatement详细用法

    ### PreparedStatement的详细...综上所述,无论从性能、安全性还是数据库无关性的角度,`PreparedStatement`都是比`Statement`更为优秀的选择,因此,在实际开发中,应尽可能地采用`PreparedStatement`来执行SQL语句。

    prepareStatement和Statement的区别

    首先,从创建时的区别开始,Statement 需要通过 Connection 对象的 createStatement() 方法创建,而 PreparedStatement 需要通过 Connection 对象的 prepareStatement() 方法创建,并且需要带有 SQL 语句。...

    connection 和 preparedstatement 的关闭问题

    #### 何时使用 Statement? - 当SQL语句简单,不会重复执行时。 - 对于读取数据的操作,如果数据量不大,可以考虑使用`Statement`。 - 当不需要参数化SQL语句时,例如只读操作。 #### 何时使用 PreparedStatement...

    JDBC基础教程之PreparedStatement.doc

    `PreparedStatement`的主要特点在于它可以预先编译SQL语句,并允许用户在执行前动态地设置SQL语句中的参数值,这使得它在执行相同或相似SQL语句时比普通的`Statement`更加高效。 #### 二、`PreparedStatement`与`...

    PreparedStatement接口

    NULL 博文链接:https://chaoyi.iteye.com/blog/2088080

    PreparedStatement 详细用法

    在Java编程语言中,`PreparedStatement`是`java.sql`包中的一个接口,它继承自`Statement`类,并提供了预编译SQL语句的功能。预编译SQL语句的主要优势在于提高了执行效率和安全性。尤其在处理动态查询或频繁执行相同...

    statement和prepared区别

    PreparedStatement对象的创建和预编译过程比Statement更为复杂和耗时。因此,在那些对性能要求极高且SQL语句几乎不重复执行的应用场景下,或许需要根据具体情况来权衡利弊。 总之,理解Statement和...

    练习3:使用PreparedStatement插入宠物信息.zip

    在Java编程中,PreparedStatement是Java SQL API中的一个接口,它是Statement接口的子接口。这个练习主要涉及如何使用PreparedStatement来插入宠物信息到数据库中。PreparedStatement的主要优势在于它的预编译能力和...

    关于PreparedStatement插入Date类型值的方法.txt

    `PreparedStatement`是`Statement`接口的子接口,用于预编译SQL语句,并可以重复执行这些预编译过的SQL语句。这不仅能够提高应用程序的性能,还能提高安全性,因为它支持参数化查询,避免了SQL注入的风险。 #### 二...

    详解Java的JDBC中Statement与PreparedStatement对象

    在Java的JDBC(Java Database Connectivity)中,与数据库交互的核心接口是Statement和PreparedStatement。这两个接口都是用于执行SQL语句的,但它们在特性和效率上有所不同。 Statement接口是最基本的SQL执行方式...

    JDBC(用PrepareStatement实现)

    在实际开发中,为了提高效率和安全性,我们通常会使用PreparedStatement接口来代替Statement接口。 1. **PreparedStatement简介** PreparedStatement是Statement的一个子接口,它的主要优势在于预编译。预编译的...

    PreparedStatement 向数据库插入时间方法

    ### PreparedStatement 向数据库插入时间方法 #### 背景与问题描述 在Java应用程序中,我们经常需要将数据写入数据库。为了确保SQL查询的安全性和效率,通常会使用`PreparedStatement`来执行这样的操作。然而,在...

    06丨数据库原理:为什么PrepareStatement性能更好更安全?.pdf

    在数据库编程中,我们经常遇到两种执行SQL语句的方法:Statement和PreparedStatement。尽管Statement看起来更简洁,但在实际应用中,尤其是在使用ORM框架如MyBatis时,PreparedStatement成为了首选。那么,为什么...

Global site tag (gtag.js) - Google Analytics