zoukankan      html  css  js  c++  java
  • JdbcTemplate介绍<二>

    引言

    如果说JdbcTemplate类是Spring Jdbc的核心类,那么execute方法算得上Spring Jdbc的核心方法了,毕竟JdbcTemplate的很多public方法内部实际上是调用execute方法实现的。

    public T execute(ConnectionCallback action) throws DataAccessException

    通过使用操作一个JDBC的Connection的回调操作,可以执行JDBC的数据操作,同时支持Spring的事务管理和可以将throw的SQLException转为Spring统一定义的DataAccessException。该回调函数可以返回一个对象,也可以返回结果集。

    内部实现

        public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
    		Assert.notNull(action, "Callback object must not be null");
    
    		Connection con = DataSourceUtils.getConnection(obtainDataSource());
    		try {
    			// Create close-suppressing Connection proxy, also preparing returned Statements.
    			Connection conToUse = createConnectionProxy(con);
    			return action.doInConnection(conToUse);
    		}
    		catch (SQLException ex) {
    			// Release Connection early, to avoid potential connection pool deadlock
    			// in the case when the exception translator hasn't been initialized yet.
    			DataSourceUtils.releaseConnection(con, getDataSource());
    			con = null;
    			throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
    		}
    		finally {
    			DataSourceUtils.releaseConnection(con, getDataSource());
    		}
    	}
    

    通过查看源代码,我们可以看出ConnectionCallback需要传入一个特殊的Jdbc Connection。

    首先DataSourceUtils从当前的数据源获取一个普通Connection(该Connection支持Spring的事务),然后使用createConnectionProxy对这个Connection进行了封装了,使用了JDK动态代理技术,对Connection的一些方法进行了重新处理,比如isClose方法只会返回false,执行close方法时其内部根本就没有关闭。

    当抛出SQLException的时候,为了防止初始化异常转换器的时候出现的存放Connecton的池出现死锁,会优先尝试释放该Connection,如果该Connection支持事务则释放,不然则由Spring自行判断是否关闭。实际上通过查看DataSourceUtils的具体实现可以发现,该Connection是通过TransactionSynchronizationManager包装过,因此是支持Spring的事务管理的。

    T execute(StatementCallback action) throws DataAccessException

    通过使用操作一个JDBC的Statement的回调操作,可以执行JDBC的数据操作,同时支持Spring的事务管理和可以将throw的SQLException转为Spring统一定义的DataAccessException。该回调函数可以返回一个对象,也可以返回结果集。

    内部实现

    	public <T> T execute(StatementCallback<T> action) throws DataAccessException {
    		Assert.notNull(action, "Callback object must not be null");
    
    		Connection con = DataSourceUtils.getConnection(obtainDataSource());
    		Statement stmt = null;
    		try {
    			stmt = con.createStatement();
    			applyStatementSettings(stmt);
    			T result = action.doInStatement(stmt);
    			handleWarnings(stmt);
    			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.
    			JdbcUtils.closeStatement(stmt);
    			stmt = null;
    			DataSourceUtils.releaseConnection(con, getDataSource());
    			con = null;
    			throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
    		}
    		finally {
    			JdbcUtils.closeStatement(stmt);
    			DataSourceUtils.releaseConnection(con, getDataSource());
    		}
    	}
    

    通过查看源代码,Connection的创建和释放就交给了DataSourceUtils,关于这个类如何创建、释放和关闭Connection,则可以看我的另一篇博客《DataUtils对Connection的获取、释放和关闭的操作学习》。

    在获得Connection之后再创建Statement,并且通过applyStatementSettings方法设置Statement的fetchSize、maxRows和timeout属性。在完成了Statement的创建工作之后便是调用StatementCallback回调接口,返回执行结果。不过在返回执行结果之前需要判断是否抛出SQL Warning。如果JdbcTemplate的ignoreWarnings属性为true,即忽略警告,则仅仅在日志做debug处理,否则将会抛出SQLWarningException。

    public void execute(final String sql) throws DataAccessException

    内部实现

    	public void execute(final String sql) throws DataAccessException {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Executing SQL statement [" + sql + "]");
    		}
    		class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
    			@Override
    			public Object doInStatement(Statement stmt) throws SQLException {
    				stmt.execute(sql);
    				return null;
    			}
    			@Override
    			public String getSql() {
    				return sql;
    			}
    		}
    		execute(new ExecuteStatementCallback());
    	}
    

    通过查看源代码,我们可以看出该方法内部实际上通过方法内部类调用了execute(StatementCallback action)方法,因此这也是为什么参数sql为final -- 内部类只能调用外部的final类型变量。

    此外,该方法也对sql语句做了日志的debug处理,这样我们可以在调试的时候查看sql语句,前提是我们选择实现的日志框架要支持debug。

    public T execute(PreparedStatementCreator psc, PreparedStatementCallback action)

    内部实现

    	public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
    			throws DataAccessException {
    
    		Assert.notNull(psc, "PreparedStatementCreator must not be null");
    		Assert.notNull(action, "Callback object must not be null");
    		if (logger.isDebugEnabled()) {
    			String sql = getSql(psc);
    			logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
    		}
    
    		Connection con = DataSourceUtils.getConnection(obtainDataSource());
    		PreparedStatement ps = null;
    		try {
    			ps = psc.createPreparedStatement(con);
    			applyStatementSettings(ps);
    			T result = action.doInPreparedStatement(ps);
    			handleWarnings(ps);
    			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);
    			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());
    		}
    	}
    

    这个execute方法比较少用,主要是通过PreparedStatementCreator来获取PreparedStatement。PreparedStatementCreator接口是JdbcTemplate类使用的两个核心回调接口之一(另外一个是CallableStatementCreator)。此接口通过给定的一个Connection来创建一个PreparedStatement,去负责提供sql语句和任何必要的参数。同时为了更好的方便我们进行debug,建议PreparedStatementCreator实现类也能够继承实现SqlProvider接口。

    public T execute(CallableStatementCreator csc, CallableStatementCallback action)

    内部实现

    	public <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action)
    			throws DataAccessException {
    
    		Assert.notNull(csc, "CallableStatementCreator must not be null");
    		Assert.notNull(action, "Callback object must not be null");
    		if (logger.isDebugEnabled()) {
    			String sql = getSql(csc);
    			logger.debug("Calling stored procedure" + (sql != null ? " [" + sql  + "]" : ""));
    		}
    
    		Connection con = DataSourceUtils.getConnection(obtainDataSource());
    		CallableStatement cs = null;
    		try {
    			cs = csc.createCallableStatement(con);
    			applyStatementSettings(cs);
    			T result = action.doInCallableStatement(cs);
    			handleWarnings(cs);
    			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 (csc instanceof ParameterDisposer) {
    				((ParameterDisposer) csc).cleanupParameters();
    			}
    			String sql = getSql(csc);
    			JdbcUtils.closeStatement(cs);
    			cs = null;
    			DataSourceUtils.releaseConnection(con, getDataSource());
    			con = null;
    			throw getExceptionTranslator().translate("CallableStatementCallback", sql, ex);
    		}
    		finally {
    			if (csc instanceof ParameterDisposer) {
    				((ParameterDisposer) csc).cleanupParameters();
    			}
    			JdbcUtils.closeStatement(cs);
    			DataSourceUtils.releaseConnection(con, getDataSource());
    		}
    	}
    

    这个execute方法也比较少用,主要是通过CallableStatementCreator来获取CallableStatement。与PreparedStatementCreator接口类似,CallableStatementCreator是JdbcTemplate接口另外一个的核心回调接口,同样建议也去实现SqlProvider接口。

    public T execute(String sql, PreparedStatementCallback action) throws DataAccessException

    该方法自定义实现了PreparedStatementCreator接口,调用了execute(PreparedStatementCreator psc, PreparedStatementCallback action)方法。

    内部实现

    	public <T> T execute(String sql, PreparedStatementCallback<T> action) throws DataAccessException {
    		return execute(new SimplePreparedStatementCreator(sql), action);
    	}
    

    SimplePreparedStatementCreator的定义

    	private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {
    
    		private final String sql;
    
    		public SimplePreparedStatementCreator(String sql) {
    			Assert.notNull(sql, "SQL must not be null");
    			this.sql = sql;
    		}
    
    		@Override
    		public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
    			return con.prepareStatement(this.sql);
    		}
    
    		@Override
    		public String getSql() {
    			return this.sql;
    		}
    	}
    

    public T execute(String callString, CallableStatementCallback action) throws DataAccessException

    该方法自定义实现了CallableStatementCreator接口,调用了execute(String callString, CallableStatementCallback action)方法。

    内部实现

    	public <T> T execute(String callString, CallableStatementCallback<T> action) throws DataAccessException {
    		return execute(new SimpleCallableStatementCreator(callString), action);
    	}
    

    SimpleCallableStatementCreator的定义

    	private static class SimpleCallableStatementCreator implements CallableStatementCreator, SqlProvider {
    
    		private final String callString;
    
    		public SimpleCallableStatementCreator(String callString) {
    			Assert.notNull(callString, "Call string must not be null");
    			this.callString = callString;
    		}
    
    		@Override
    		public CallableStatement createCallableStatement(Connection con) throws SQLException {
    			return con.prepareCall(this.callString);
    		}
    
    		@Override
    		public String getSql() {
    			return this.callString;
    		}
    	}
    
  • 相关阅读:
    MySql中的变量定义
    mysql常用脚本
    Spring中依赖注入的使用和配置
    在linux下通过sh运行java程序
    linux下shell脚本学习
    eclipse导出jar包
    mysql中游标的使用
    netty中LengthFieldBasedFrameDecoder的使用
    网络游戏服务器架构(转)
    H2 database的使用
  • 原文地址:https://www.cnblogs.com/xiao2/p/7192097.html
Copyright © 2011-2022 走看看