PreparedStatement与Statement的区别

landdin0 2009-03-29

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语句,足以毁掉整个应用。

相关推荐