用语句创建器更新数据库
第一个回调接口是PerparedStatementCreator,实现这个接口来覆盖整个更新过程的语句创建任务。我们先看下源代码中的PerparedStatementCreator接口,然后实现我们自己的接

口。

 

public interface PreparedStatementCreator {

	/** 
	 * Create a statement in this connection. Allows implementations to use
	 * PreparedStatements. The JdbcTemplate will close the created statement.
	 * @param con Connection to use to create statement
	 * @return a prepared statement
	 * @throws SQLException there is no need to catch SQLExceptions
	 * that may be thrown in the implementation of this method.
	 * The JdbcTemplate class will handle them.
	 */
	PreparedStatement createPreparedStatement(Connection con) throws SQLException;

}

 

源代码中只定义了一个方法createPreparedStatement,在connection连接的基础上创建statement,使我们可以使用PreparedStatements实例,创建完,jdbcTemplate会自动关闭

Connection连接,下面我们实现这个接口。

public class InsertPreparedStatementCreator implements PreparedStatementCreator{
	
	private User user;

	public InsertPreparedStatementCreator(User user){
		this.user = user;
	}

	public PreparedStatement createPreparedStatement(Connection conn)throws Exception{
		String sql = "insert into user value(?,?);
		PreparedStatement ps  = conn.prepareStatement(sql);
		ps.setString(user.getUsername());
		ps.setString(user.getPassword());
		return ps;
	}
}

 

我们实现后,只需要创建PreparedStatement ,并且把参数绑定上去,然后返回,在处理过程中不需要进行异常处理,因为方法签名中已经给我们抛出了异常,也不用关闭数据库

连接或者打开数据库连接,这些spring都给我们做好了,然后调用jdbcTemplate中的update方法即可。
jdbcTemplate.update(new InsertPreparedStatementCreator(user));
当然也可以用另一种方式。

 

jdbcTemplate.update(new PreparedStatementCreator(){
		String sql = "insert into user value(?,?);
		PreparedStatement ps  = conn.prepareStatement(sql);
		ps.setString(user.getUsername());
		ps.setString(user.getPassword());
		return ps;
});

 

这里其实是个回调函数,在设计模式中是模板方法模式。
模板方法使用继承机制实现。在父类中定义不变的,然后把变化的定义成一个抽象方法,供子类实现。因此模板方法的 父类一般也是一个抽象类。
在jdbcTemplate中的执行:
我们的update方法调用了jdbcTemplate方法中的

 

public int update(PreparedStatementCreator psc) throws DataAccessException {
		return update(psc, (PreparedStatementSetter) null);
}

 

接着调用

protected int update(final PreparedStatementCreator psc, final PreparedStatementSetter pss)
			throws DataAccessException {

		if (logger.isDebugEnabled()) {
			String sql = getSql(psc);
			logger.debug("Executing SQL update" + (sql != null ? " [" + sql  + "]" : ""));
		}
		Integer result = (Integer) execute(psc, new PreparedStatementCallback() {
			public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
				try {
					if (pss != null) {
						pss.setValues(ps);
					}
					int rows = ps.executeUpdate();
					if (logger.isDebugEnabled()) {
						logger.debug("SQL update affected " + rows + " rows");
					}
					return new Integer(rows);
				}
				finally {
					if (pss instanceof ParameterDisposer) {
						((ParameterDisposer) pss).cleanupParameters();
					}
				}
			}
		});
		return result.intValue();
	}

 

 

方法的关键:
int rows = ps.executeUpdate();
return new Integer(rows);
执行了preparedStatement.executeUpdate().然后返回执行后的值,和我们自己写是一样。在这里还是没有看到spring是如何打开数据库连接的。继续看,注意到一个方法的调用

Integer result = (Integer) execute(psc, new PreparedStatementCallback() 。。。。。。
我们找到这个方法

 

public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)
			throws DataAccessException {

		Assert.notNull(psc, "PreparedStatementCreator must not be null");
		Assert.notNull(action, "Callback object must not be null");

		Connection con = DataSourceUtils.getConnection(getDataSource());
		PreparedStatement ps = null;
		try {
			Connection conToUse = con;
			if (this.nativeJdbcExtractor != null &&
					this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
			}
			ps = psc.createPreparedStatement(conToUse);
			applyStatementSettings(ps);
			PreparedStatement psToUse = ps;
			if (this.nativeJdbcExtractor != null) {
				psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
			}
			Object result = action.doInPreparedStatement(psToUse);
			SQLWarning warning = ps.getWarnings();
			throwExceptionOnWarningIfNotIgnoringWarnings(warning);
			return result;
		}
		catch (SQLException ex) {
			// Release Connection early, to avoid potential connection pool deadlock
			// in the case when the exception translator hasn't been initialized yet.
			if (psc instanceof ParameterDisposer) {
				((ParameterDisposer) psc).cleanupParameters();
			}
			String sql = getSql(psc);
			psc = null;
			JdbcUtils.closeStatement(ps);
			ps = null;
			DataSourceUtils.releaseConnection(con, getDataSource());
			con = null;
			throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
		}
		finally {
			if (psc instanceof ParameterDisposer) {
				((ParameterDisposer) psc).cleanupParameters();
			}
			JdbcUtils.closeStatement(ps);
			DataSourceUtils.releaseConnection(con, getDataSource());
		}
	}

 

 

里面有这么一句Connection con = DataSourceUtils.getConnection(getDataSource());
这里获取数据库数据库连接。我们看看他是怎么获取的数据库连接。
进入DataSourceUtils这个类中

 

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		try {
			return doGetConnection(dataSource);
		}
		catch (SQLException ex) {
			throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
		}
}


	public static Connection doGetConnection(DataSource dataSource) throws SQLException {
		Assert.notNull(dataSource, "No DataSource specified");

		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
			conHolder.requested();
			if (!conHolder.hasConnection()) {
				logger.debug("Fetching resumed JDBC Connection from DataSource");
				conHolder.setConnection(dataSource.getConnection());
			}
			return conHolder.getConnection();
		}
		// Else we either got no holder or an empty thread-bound holder here.

		logger.debug("Fetching JDBC Connection from DataSource");
		Connection con = dataSource.getConnection();

		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			logger.debug("Registering transaction synchronization for JDBC Connection");
			// Use same Connection for further JDBC actions within the transaction.
			// Thread-bound object will get removed by synchronization at transaction completion.
			ConnectionHolder holderToUse = conHolder;
			if (holderToUse == null) {
				holderToUse = new ConnectionHolder(con);
			}
			else {
				holderToUse.setConnection(con);
			}
			holderToUse.requested();
			TransactionSynchronizationManager.registerSynchronization(
					new ConnectionSynchronization(holderToUse, dataSource));
			holderToUse.setSynchronizedWithTransaction(true);
			if (holderToUse != conHolder) {
				TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
			}
		}

		return con;
	}

 

 

spring会先去ConnectionHolder 中获取Connection,如果有直接从ConnectionHolder 获取conennction. conHolder.getConnection();如果没有则conHolder.setConnection

(dataSource.getConnection());,然后继续conennction. conHolder.getConnection(),说白了Connection是从ConnectionHolder 中获取的。
我们继续往深处看看,ConnectionHolder 是怎么把Connection给我们的。我们进入ConnectionHolder这个类中
public class ConnectionHolder extends ResourceHolderSupport,ConnectionHolder继承自抽象类 ResourceHolderSupport,我们暂时先不讨论这个类,主要看

ConnectionHolder.

 

public Connection getConnection() {
		Assert.notNull(this.connectionHandle, "Active Connection is required");
		if (this.currentConnection == null) {
			this.currentConnection = this.connectionHandle.getConnection();
		}
		return this.currentConnection;
	}

 

 

原来ConnectionHolder也不是现实取得Connection真正的类,它是从connectionHandle中获取的,
我们在进入ConnectionHandle这个处理类中,

public interface ConnectionHandle {

	/**
	 * Fetch the JDBC Connection that this handle refers to.
	 */
	Connection getConnection();

	/**
	 * Release the JDBC Connection that this handle refers to.
	 * @param con the JDBC Connection to release
	 */
	void releaseConnection(Connection con);

}

 

发现什么也没有,只是一个接口,定义了一个标准。既然是一个接口,就一定有实现类,我们继续往下找。
public class SimpleConnectionHandle implements ConnectionHandle 仅有一个实现类,还是一个simple的,晕吧,spring文档竟然说是一个简单的实现。那复杂的实现在哪呢

?找了半天就找到这个一个简单的实现,有点郁闷。看看在说。

public class SimpleConnectionHandle implements ConnectionHandle {

	private final Connection connection;


	/**
	 * Create a new SimpleConnectionHandle for the given Connection.
	 * @param connection the JDBC Connection
	 */
	public SimpleConnectionHandle(Connection connection) {
		Assert.notNull(connection, "Connection must not be null");
		this.connection = connection;
	}

	/**
	 * Return the specified Connection as-is.
	 */
	public Connection getConnection() {
		return connection;
	}

	/**
	 * This implementation is empty, as we're using a standard
	 * Connection handle that does not have to be released.
	 */
	public void releaseConnection(Connection con) {
	}


	public String toString() {
		return "SimpleConnectionHandle: " + this.connection;
	}

}

 

 

这里也没有我们想要的connection连接,只是一个保存connnection的地方,我们要的是DriverManager.getConnection()的地方,在哪呢?继续找吧!返回到抽象类

DataSourceUtils中,继续看。Connection con = dataSource.getConnection();看到了吧,其实spring也没有提供如何获取,是在sun的包中实现了DriverManager.getConnection
spring给我们提供了多种获取connnection的途径,还有

ConnectionHolder holderToUse = conHolder;
   if (holderToUse == null) {
    holderToUse = new ConnectionHolder(con);
   }

   public ConnectionHolder(Connection connection) {
    this.connectionHandle = new SimpleConnectionHandle(connection);
   }

 

 

   在我们第一次连接后,以后就吧connnection保存了,下次用的时候就直接存spring中拿就OK了。返回到我们的jdbcTemplate继续看我们刚才的代码,注意如下代码。
  

 Connection conToUse = con;
   if (this.nativeJdbcExtractor != null &&
     this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
    conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
   }
   ps = psc.createPreparedStatement(conToUse);

 

 

 psc.createPreparedStatement(conToUse);使用的是conToUse的Connection,而这个Connection是通过this.nativeJdbcExtractor.getNativeConnection获取的,我们看

看.nativeJdbcExtractor这个东东。

 

public interface NativeJdbcExtractor {

	
	boolean isNativeConnectionNecessaryForNativeStatements();

	
	boolean isNativeConnectionNecessaryForNativePreparedStatements();

	
	boolean isNativeConnectionNecessaryForNativeCallableStatements();

	
	Connection getNativeConnection(Connection con) throws SQLException;

	
	Connection getNativeConnectionFromStatement(Statement stmt) throws SQLException;

	
	Statement getNativeStatement(Statement stmt) throws SQLException;

	
	PreparedStatement getNativePreparedStatement(PreparedStatement ps) throws SQLException;

	
	CallableStatement getNativeCallableStatement(CallableStatement cs) throws SQLException;

	
	ResultSet getNativeResultSet(ResultSet rs) throws SQLException;

}

 

 

NativeJdbcExtractor 是一个接口。有getNativeConnection获取Connection的方法。我们继续看看NativeJdbcExtractor 接口的实现类。就恍然大悟了。
C3P0NativeJdbcExtractor, CommonsDbcpNativeJdbcExtractor, JBossNativeJdbcExtractor, NativeJdbcExtractorAdapter, SimpleNativeJdbcExtractor,

WebLogicNativeJdbcExtractor, WebSphereNativeJdbcExtractor, XAPoolNativeJdbcExtractor有这么多类实现了NativeJdbcExtractor 这个接口,名名字不难看出胡,有通过

jboss获取的,有本地jdbc(NativeJdbcExtractorAdapter)获取的,有weblogic获取的,有websphere获取的,基本上我们常见的服务器都支持。在这里。
我们看一个实现类NativeJdbcExtractorAdapter,

public Connection getNativeConnection(Connection con) throws SQLException {
		if (con == null) {
			return null;
		}
		Connection targetCon = DataSourceUtils.getTargetConnection(con);
		Connection nativeCon = doGetNativeConnection(targetCon);
		if (nativeCon == targetCon) {
			// We haven't received a different Connection, so we'll assume that there's
			// some additional proxying going on. Let's check whether we get something
			// different back from the DatabaseMetaData.getConnection() call.
			DatabaseMetaData metaData = targetCon.getMetaData();
			// The following check is only really there for mock Connections
			// which might not carry a DatabaseMetaData instance.
			if (metaData != null) {
				Connection metaCon = metaData.getConnection();
				if (metaCon != targetCon) {
					// We've received a different Connection there:
					// Let's retry the native extraction process with it.
					nativeCon = doGetNativeConnection(metaCon);
				}
			}
		}
		return nativeCon;
}

 

 

通过这里DatabaseMetaData metaData = targetCon.getMetaData();获取connection,就看到这里,关于更详细的介绍且听下回分解。


2010年9月7日22:02:47