PreparedStatement jdk的解释是
An object that represents a precompiled SQL statement.
A SQL statement is precompiled and stored in a PreparedStatement object.
主要特点是:
1、提高了安全性,可以防止SQL注入;
2、调试不方便,看不到sql语句,需要额外使用p6spy等辅助包;
2、预编译语句。
并不是说PreparedStatement在所有的DB上都不会提高效率,PreparedStatement需要服务器端的支持,比如在Oracle上就会有显著效果。而MySQL比较明确地说明了不支持PreparedStatement。至于为什么预编译就会提高效率呢?因为oracle中会将所有的sql语句先编译,叫做“执行计划”,放在oracle内部的一个特定的缓存中,每次遇到相同的sql,就会预先调用缓存中,如果不预编译,每次都用statement,那么每次都要编译,在缓冲中会有很多重复的“执行计划”,影响数据库的效能。
还有一点就是在使用setObject()的时候,记得一定要使用带targetSqlType参数的方法,来提高效率。
以下是mysql驱动包中有关setObject()的源代码
public void setObject(int parameterIndex, Object parameterObj,
int targetSqlType, int scale) throws SQLException {
if (parameterObj == null) {
setNull(parameterIndex, java.sql.Types.OTHER);
} else {
try {
switch (targetSqlType) {
case Types.BOOLEAN:
if (parameterObj instanceof Boolean) {
setBoolean(parameterIndex, ((Boolean) parameterObj)
.booleanValue());
break;
} else if (parameterObj instanceof String) {
setBoolean(parameterIndex, "true"
.equalsIgnoreCase((String) parameterObj)
|| !"0".equalsIgnoreCase((String) parameterObj));
break;
} else if (parameterObj instanceof Number) {
int intValue = ((Number) parameterObj).intValue();
setBoolean(parameterIndex, intValue != 0);
break;
} else {
throw new SQLException("No conversion from "
+ parameterObj.getClass().getName()
+ " to Types.BOOLEAN possible.",
SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
}
case Types.BIT:
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
case Types.BIGINT:
case Types.REAL:
case Types.FLOAT:
case Types.DOUBLE:
case Types.DECIMAL:
case Types.NUMERIC:
setNumericObject(parameterIndex, parameterObj,
targetSqlType, scale);
break;
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
if (parameterObj instanceof BigDecimal) {
setString(
parameterIndex,
(StringUtils
.fixDecimalExponent(StringUtils
.consistentToString((BigDecimal) parameterObj))));
} else {
setString(parameterIndex, parameterObj.toString());
}
break;
case Types.CLOB:
if (parameterObj instanceof java.sql.Clob) {
setClob(parameterIndex, (java.sql.Clob) parameterObj);
} else {
setString(parameterIndex, parameterObj.toString());
}
break;
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
case Types.BLOB:
if (parameterObj instanceof byte[]) {
setBytes(parameterIndex, (byte[]) parameterObj);
} else if (parameterObj instanceof java.sql.Blob) {
setBlob(parameterIndex, (java.sql.Blob) parameterObj);
} else {
setBytes(parameterIndex, StringUtils.getBytes(
parameterObj.toString(), this.charConverter,
this.charEncoding, this.connection
.getServerCharacterEncoding(),
this.connection.parserKnowsUnicode()));
}
break;
case Types.DATE:
case Types.TIMESTAMP:
java.util.Date parameterAsDate;
if (parameterObj instanceof String) {
ParsePosition pp = new ParsePosition(0);
java.text.DateFormat sdf = new java.text.SimpleDateFormat(
getDateTimePattern((String) parameterObj, false),
Locale.US);
parameterAsDate = sdf.parse((String) parameterObj, pp);
} else {
parameterAsDate = (java.util.Date) parameterObj;
}
switch (targetSqlType) {
case Types.DATE:
if (parameterAsDate instanceof java.sql.Date) {
setDate(parameterIndex,
(java.sql.Date) parameterAsDate);
} else {
setDate(parameterIndex, new java.sql.Date(
parameterAsDate.getTime()));
}
break;
case Types.TIMESTAMP:
if (parameterAsDate instanceof java.sql.Timestamp) {
setTimestamp(parameterIndex,
(java.sql.Timestamp) parameterAsDate);
} else {
setTimestamp(parameterIndex,
new java.sql.Timestamp(parameterAsDate
.getTime()));
}
break;
}
break;
case Types.TIME:
if (parameterObj instanceof String) {
java.text.DateFormat sdf = new java.text.SimpleDateFormat(
getDateTimePattern((String) parameterObj, true),
Locale.US);
setTime(parameterIndex, new java.sql.Time(sdf.parse(
(String) parameterObj).getTime()));
} else if (parameterObj instanceof Timestamp) {
Timestamp xT = (Timestamp) parameterObj;
setTime(parameterIndex, new java.sql.Time(xT.getTime()));
} else {
setTime(parameterIndex, (java.sql.Time) parameterObj);
}
break;
case Types.OTHER:
setSerializableObject(parameterIndex, parameterObj);
break;
default:
throw new SQLException(Messages
.getString("PreparedStatement.16"), //$NON-NLS-1$
SQLError.SQL_STATE_GENERAL_ERROR);
}
} catch (Exception ex) {
if (ex instanceof SQLException) {
throw (SQLException) ex;
}
throw new SQLException(
Messages.getString("PreparedStatement.17") //$NON-NLS-1$
+ parameterObj.getClass().toString()
+ Messages.getString("PreparedStatement.18") //$NON-NLS-1$
+ ex.getClass().getName()
+ Messages.getString("PreparedStatement.19") + ex.getMessage(), //$NON-NLS-1$
SQLError.SQL_STATE_GENERAL_ERROR);
}
}
}
不带targetSqlType参数的setObject()方法
public void setObject(int parameterIndex, Object parameterObj)
throws SQLException {
if (parameterObj == null) {
setNull(parameterIndex, java.sql.Types.OTHER);
} else {
if (parameterObj instanceof Byte) {
setInt(parameterIndex, ((Byte) parameterObj).intValue());
} else if (parameterObj instanceof String) {
setString(parameterIndex, (String) parameterObj);
} else if (parameterObj instanceof BigDecimal) {
setBigDecimal(parameterIndex, (BigDecimal) parameterObj);
} else if (parameterObj instanceof Short) {
setShort(parameterIndex, ((Short) parameterObj).shortValue());
} else if (parameterObj instanceof Integer) {
setInt(parameterIndex, ((Integer) parameterObj).intValue());
} else if (parameterObj instanceof Long) {
setLong(parameterIndex, ((Long) parameterObj).longValue());
} else if (parameterObj instanceof Float) {
setFloat(parameterIndex, ((Float) parameterObj).floatValue());
} else if (parameterObj instanceof Double) {
setDouble(parameterIndex, ((Double) parameterObj).doubleValue());
} else if (parameterObj instanceof byte[]) {
setBytes(parameterIndex, (byte[]) parameterObj);
} else if (parameterObj instanceof java.sql.Date) {
setDate(parameterIndex, (java.sql.Date) parameterObj);
} else if (parameterObj instanceof Time) {
setTime(parameterIndex, (Time) parameterObj);
} else if (parameterObj instanceof Timestamp) {
setTimestamp(parameterIndex, (Timestamp) parameterObj);
} else if (parameterObj instanceof Boolean) {
setBoolean(parameterIndex, ((Boolean) parameterObj)
.booleanValue());
} else if (parameterObj instanceof InputStream) {
setBinaryStream(parameterIndex, (InputStream) parameterObj, -1);
} else if (parameterObj instanceof java.sql.Blob) {
setBlob(parameterIndex, (java.sql.Blob) parameterObj);
} else if (parameterObj instanceof java.sql.Clob) {
setClob(parameterIndex, (java.sql.Clob) parameterObj);
} else if (parameterObj instanceof java.util.Date) {
setTimestamp(parameterIndex, new Timestamp(
((java.util.Date) parameterObj).getTime()));
} else if (parameterObj instanceof BigInteger) {
setString(parameterIndex, parameterObj.toString());
} else {
setSerializableObject(parameterIndex, parameterObj);
}
}
}
所以大家不要为了省事,而使用不带targetSqlType参数的setObject()方法。
Statement是PreparedStatement的父接口,
主要特点是:
1、易于调试;
2、不进行预编译操作,减少了进行预编译的开销。单次运行PreparedStatement要比Statement要慢一些
这里有个对比:http://www.onjava.com/lpt/a/1480
Table 19-3: OCI driver timings (in milliseconds) | ||
Inserts | Statement | PreparedStatement |
1 | 10 | 113 |
1,000 | 2,804 | 1,412 |
The important thing to notice about the graph is that it's not until about 65 inserts that the PreparedStatement
object outperforms the Statement
object. 65 inserts
综上:
Statement和PreparedStatement,都有其优缺点,但总体而言,强烈建议使用Statement的同学改为使用PreparedStatement,如果希望调试方便,再加个p6spy等包做辅助,看到的sql语句效果更好。毕竟许多应用中,都要考安全性、大用户量时候的性能问题。像hibernate、toplink这种jpa在使用jdbc的时候,如果数据库端支持,都很统一的使用了PreparedStatement。
如:oracle.toplink.essentials.internal.databaseaccess.DatabasePlatform类。
顺便再讲一下,程序员写出来的sql语句是最值得去关注的,一条效率差的sql语句,足以毁掉整个应用。