Spring JDBC最佳实践(1)

来源:互联网 发布:淘宝哪个店买32e的文胸 编辑:程序博客网 时间:2024/05/22 01:37
Spring提供了两种使用JDBC API的最佳实践,一种是以JdbcTemplate为核心的基于Template的JDBC的使用方式,另一种则是在JdbcTemplate基础之上的构建的基于操作对象的JDBC的使用方式。

基于Template的JDBC的使用方式
该使用方式的最初设想和原型,需要追溯到Rod Johnson在03年出版的Expert One-on-One J2EE Design and Development,在该书的Practical Data Access(数据访问实践)中,Rod针对JDBC使用中的一些问题提出了一套改进的实践原型,并最终将该原型完善后在Spring框架中发布。

JDBC的尴尬
JDBC作为Java平台的访问关系数据库的标准,其成功是 有目共睹的。几乎所有java平台的数据访问,都直接或者间接的使用了JDBC,它是整个java平台面向关系数据库进行数据访问的基石。
作为一个标准,无疑JDBC是很成功的,但是要说JDBC在使用过程当中多么的受人欢迎,则不尽然了。JDBC主要是面向较为底层的数据库操作,所以在设计的过程当中 ,比较的贴切底层以提供尽可能多的功能特色。从这个角度来说,JDBC API的设计无可厚非。可是,过于贴切底层的API的设计,对于开发人员则未必是一件好事。即使执行一个最简单的查询,开发人员也要按照API的规矩写上一大堆雷同的代码,如果不能合理的封装使用JDBC API,在项目中使用JDBC访问数据所出现的问题估计会使人发疯!
对于通常的项目开发来说,如果层次划分很明确,数据访问逻辑一般应该在DAO层中实现。根据功能模块的划分,可能每个开发人员都会分得或多或少的实现相应的DAO的任务,假设开发人员A在分得了DAO实现任务后进行开发,他或许开发了如下所示的代码:
01    package com.google.spring.jdbc;
02    
03    import java.sql.Connection;
04    import java.sql.SQLException;
05    import java.sql.Statement;
06    
07    import javax.sql.DataSource;
08    
09    import org.apache.commons.logging.Log;
10    import org.apache.commons.logging.LogFactory;
11    
12    public class DaoWithA implements IDao
13    {
14    
15        private final Log logger = LogFactory.getLog(DaoWithA.class);
16        private DataSource dataSource = null;
17         
18        public DataSource getDataSource()
19        {
20            return dataSource;
21        }
22    
23        public void setDataSource(DataSource dataSource)
24        {
25            this.dataSource = dataSource;
26        }
27    
28        @Override
29        public int updateSomething(String sql)
30        {
31            int count;
32            Connection conn = null;
33            Statement stmt = null;
34            try
35            {
36                conn = getDataSource().getConnection();
37                stmt = conn.createStatement();
38                count = stmt.executeUpdate(sql);
39                stmt.close();
40                stmt = null;
41            }
42            catch (SQLException e)
43            {
44                throw new RuntimeException(e);
45                 
46            }
47            finally
48            {
49                if(stmt!=null)
50                {
51                    try
52                    {
53                        stmt.close();
54                    }
55                    catch (SQLException ex)
56                    {
57                        logger.warn("fail to close statement:"+ex);
58                    }
59                }
60                if(conn!=null)
61                {
62                    try
63                    {
64                        conn.close();
65                    }
66                    catch (Exception ex)
67                    {
68                        logger.warn("failed to close Connection:"+ex);
69                    }
70                }
71            }
72            return count;
73        }
74    
75    }

而B所负责的DAO的实现中,可能也有类似的更新的操作。无疑,B也要像A这样,在他的DAO实现类中写一大堆同样的JDBC代码,类似的情况还可能扩展到C、D等开发人员。如果每个开发人员都能严格的按照JDBC的编程规范开发还好,但是事实是,一个团队中的开发人员是有差别的。
这其实只是API的使用过程中的一个插曲,当你看到应用程序中成百的使用JDBC实现类的时候,会发现如下的问题:
1、Statement使用完没有关闭,而是想着让Connection关闭的时候一并关闭,可是并非所有的驱动程序都有这样的行为。
2、创建了多个ResultSet或者Statement,只清理了最外层的,忽视了里层的。

3、忘记关闭Connection。
JDBC规范在指定数据库访问异常的时候也没有能够进行的很彻底:
1、将异常类型定义为SQLException是一个值得商榷的地方。
2、SQLExcpetion没有采用将具体的异常情况子类化,以进一步抽象不同的数据访问的情况,而是采用ErrorCode的方式来区分访问过程中所出现的不同异常情况,其实这也没什么,只要能区分出具体的错误就行,但是JDBC规范却把ErrorCode的规范留给了数据库提供商,这导致了不同的数据库供应商对应了不同的ErrorCode,进而应用程序在捕获到SQLException后,还要看当前用的是什么数据库。
针对以上问题,Spring提供了相应的解决方案帮助我们提高开发效率!

为了解决JDBC API在实际使用中的各种尴尬的局面,spring提出了org.springframework.jdbc.core.JdbcTemplate作为数据访问的Helper类。JdbcTemplate是整个spring数据抽象层提供的所有JDBC API最佳实践的基础,框架内其它更加方便的Helper类以及更高层次的抽象,全部的构建于JdbcTemplate之上。抓住了JdbcTemplate,就抓住了spring框架JDBC API最佳实践的核心。
概括的说,JdbcTemplate主要关注一下两个事情:
1、封装所有的基于JDBC的数据访问的代码,以统一的格式和规范来使用JDBC API。所有的基于JDBC API的数据访问全部通过JdbcTemplate,从而避免了容易出错的数据访问方式。
2、对SQLException所提供的异常信息在框架内进行统一的转译,将基于JDBC的数据访问异常纳入Spring自身的异常层次之中,统一了数据接口的定义,简化了客户端代码对数据访问异常的处理。
Spring主要是通过模板方法对基于JDBC的数据访问代码进行统一的封装,所以我们可先看下模板方法:
模板方法主要是用于对算法的行为或者逻辑进行封装,即如果多个类中存在相似的算法逻辑或者行为逻辑,可以将这些逻辑提取到模板方法中实现,然后让相应的子类根据需要实现某些自定义的逻辑。
举个例子,所有的汽车,不管是宝马还是大众,他们的驾驶流程基本上是固定的。实际上,除了少数的实现细节有所不同之外,大部分的流程是相同的,基本上是如下所示的流程说明:
1、点火启动
2、踩刹车,挂前进的档位(不同的车在这一步会存在差异)
3、放下手动控制器(手刹)
4、踩油门启动车辆运行
此时,我们可以声明一个模板方法类,将确定的行为以模板的形式定义,而将不同的行为留给相应的子类来实现:
01    package com.google.spring.jdbc;
02    
03    public abstract class Vehicle
04    {
05         
06        public final void drive()
07        {
08            startTheEnginee();//启动
09            putIntoGear(); //前进
10            looseHandBrake();//放下手刹
11            stepOnTheGasAndGo();//踩油门前进
12        }
13         
14        protected abstract void putIntoGear();
15         
16        private void startTheEnginee()
17        {
18             
19        }
20         
21        private void looseHandBrake()
22        {
23             
24        }
25         
26        private void stepOnTheGasAndGo()
27        {
28             
29        }
30    }

drive()方法就是我们的模板方法,它被声明为final,表示该类是不能被子类重写的,车辆的自动挡和手动挡是不同的,所以留给了子类去实现:
01    package com.google.spring.jdbc;
02    
03    public class VehicleAT extends Vehicle
04    {
05    
06        @Override
07        protected void putIntoGear()
08        {
09            //挂前进档位
10    
11        }
12    
13    }

01    package com.google.spring.jdbc;
02    
03    public class VehicleMT extends Vehicle
04    {
05    
06        @Override
07        protected void putIntoGear()
08        {
09            //踩离合器 挂前进档位
10    
11        }
12    
13    }

这样,每个子类实现特有的逻辑就可以了。

 JdbcTemplate的演化
如果回头看一下最初的使用JDBC API进行数据访问的代码。就会发现,不管这些代码是谁负责的,也不管数据访问的逻辑如何,除了小部分的差异之外,所有的这些代码几乎都是按照同一个流程走下来的,如下:
1、conn=getDataSource().getConnection();
2、stmt=conn.createStatement()或者ps=conn.prepareStatement();
3、stmt.executeUpdate(sql)或者ps.executeUpdate()  或者进行相应的查询。
4、stmt.close()  stmt=null
5、catch处理数据库访问异常
6、关闭数据库连接避免连接泄露导致系统崩溃
对于多个DAO中充斥着几乎相同的JDBC API的使用代码,我们也可以采用模板方法,多这些代码进行重构,避免因个人操作不当所出现的种种问题,我们要做的,就是将一些公共的行为提取到模板方法中去,而特有的操作,比如每次执行不同的更新,或者对不同的查询结果进行不同的处理,则放入具体的子类中,这样,我们就有个JdbcTemplate的雏形:
01    package com.google.spring.jdbc;
02    
03    import java.sql.Connection;
04    import java.sql.SQLException;
05    import java.sql.Statement;
06    
07    import javax.sql.DataSource;
08    
09    import org.springframework.dao.DataAccessException;
10    
11    
12    public abstract class JdbcTemplate
13    {
14        private DataSource dataSource;
15         
16         
17        public DataSource getDataSource()
18        {
19            return dataSource;
20        }
21    
22    
23        public void setDataSource(DataSource dataSource)
24        {
25            this.dataSource = dataSource;
26        }
27    
28    
29        public final Object execute(String sql)
30        {
31            Connection conn = null;
32            Statement stmt = null;
33            try
34            {
35                conn = this.getDataSource().getConnection();
36                stmt = conn.createStatement();
37                Object retValue = this.executeWithStatement(stmt, sql);
38                return retValue;
39            }
40            catch (SQLException e)
41            {
42                throw new RuntimeException(e);
43            }
44            finally
45            {
46                closeStatement(stmt);
47                closeConnection(conn);
48            }
49        }
50         
51        protected abstract Object executeWithStatement(Statement stmt,String sql);
52         
53        private final DataAccessException translateSQLException(SQLException e)
54        {
55            DataAccessException dataAccessException = null;
56            //进行相应的转译
57            return dataAccessException;
58        }
59         
60        private final void closeStatement(Statement stmt)
61        {
62            //关闭Statement
63        }
64         
65        private final void closeConnection(Connection conn)
66        {
67            //关闭Connection
68        }
69    }

这样处理之后,JDBC代码的使用有了规范。但是,只使用模板方法还不足以提供方便的Helper类。顶着abstract的帽子,每次使用都要进行相应的子类化,这也太不靠谱了。所以,spring中的JdbcTemplate除了引入了模板方法之外,还引入了相应的Callback,避免了每次都子类化,比如,当引入了StatementCallback接口以后:
1    package com.google.spring.jdbc;
2    
3    import java.sql.Statement;
4    
5    public interface StatementCallback
6    {
7        public Object doWithStatement(Statement stmt) throws SQLException;
8    }

这样这个真正的Helper类就存在了,如下所示:
01    package com.google.spring.jdbc;
02    
03    import java.sql.Connection;
04    import java.sql.SQLException;
05    import java.sql.Statement;
06    
07    import javax.sql.DataSource;
08    
09    import org.springframework.dao.DataAccessException;
10    
11    
12    public  class JdbcTemplate
13    {
14        private DataSource dataSource;
15         
16         
17        public DataSource getDataSource()
18        {
19            return dataSource;
20        }
21    
22    
23        public void setDataSource(DataSource dataSource)
24        {
25            this.dataSource = dataSource;
26        }
27    
28    
29        public final Object execute(StatementCallback callback)
30        {
31            Connection conn = null;
32            Statement stmt = null;
33            try
34            {
35                conn = this.getDataSource().getConnection();
36                stmt = conn.createStatement();
37                Object retValue = callback.doWithStatement(stmt);
38                return retValue;
39            }
40            catch (SQLException e)
41            {
42                throw new RuntimeException(e);
43            }
44            finally
45            {
46                closeStatement(stmt);
47                closeConnection(conn);
48            }
49        }
50         
51         
52         
53        private final DataAccessException translateSQLException(SQLException e)
54        {
55            DataAccessException dataAccessException = null;
56            //进行相应的转译
57            return dataAccessException;
58        }
59         
60        private final void closeStatement(Statement stmt)
61        {
62            //关闭Statement
63        }
64         
65        private final void closeConnection(Connection conn)
66        {
67            //关闭Connection
68        }
69    }

要在相应的DAO中使用该JdbcTemplate,只需要根据情况提供参数和相应的callback就可以了,如下所示:
01    final String sql = "update";
02    JdbcTemplate jdbcTemplate = new JdbcTemplate();
03    BasicDataSource dataSource = new BasicDataSource();
04    jdbcTemplate.setDataSource(dataSource);
05    //对dataSource进行setter操作
06    StatementCallback callback = new StatementCallback()
07    {
08        public Object doWithStatement(Statement stmt) throws SQLException
09        {
10            return new Integer(stmt.executeUpdate(sql)) ;
11        }
12    };
13    
14    jdbcTemplate.execute(callback);

这样,开发人员只需要关注与数据访问逻辑相关的东西,JDBC底层的细节不需要再考虑了。
上述是spring中JdbcTemplate的中心思想,实际上,JdbcTemplate在实现上要考虑很多的东西,继承层次如下:

org.springframework.jdbc.core.JdbcOperations接口定义了JdbcTemplate可以使用的JDBC操作集合,该接口提供的操作声明,从查询到更新无所不有。
JdbcTemplate的直接父类是JdbcAccessor,这是一个抽象类,主要为子类提供一些公用的属性:

DataSource:javax.sql.DataSource是JDBC2.0之后引入的接口定义,用来替代java.sql.DriverManager的数据库连接方式,它的角色可以看做是JDBC的连接工厂,所以,基本上现在它应该作为获取数据库资源的统一接口。

SQLExceptionTranslator:JdbcTemplate委托此类进行异常的转译。

JdbcTemplate中的模板方法可分为如下的四组:
面向Connection的模板方法:
通过ConnectionCallback接口所公开的Connection进行数据访问
1    import java.sql.Connection;
2    import java.sql.SQLException;
3    
4    import org.springframework.dao.DataAccessException;
5    
6    public interface ConnectionCallback
7    {
8        Object doInConnection(Connection con) throws SQLException, DataAccessException;
9    }

01    public Object execute(ConnectionCallback action) throws DataAccessException {
02        Assert.notNull(action, "Callback object must not be null");
03    
04        Connection con = DataSourceUtils.getConnection(getDataSource());
05        try {
06            Connection conToUse = con;
07            if (this.nativeJdbcExtractor != null) {
08                // Extract native JDBC Connection, castable to OracleConnection or the like.
09                conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
10            }
11            else {
12                // Create close-suppressing Connection proxy, also preparing returned Statements.
13                conToUse = createConnectionProxy(con);
14            }
15            return action.doInConnection(conToUse);         }
16        catch (SQLException ex) {
17            // Release Connection early, to avoid potential connection pool deadlock
18            // in the case when the exception translator hasn't been initialized yet.
19            DataSourceUtils.releaseConnection(con, getDataSource());
20            con = null;
21            throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
22        }
23        finally {
24            DataSourceUtils.releaseConnection(con, getDataSource());
25        }
26    }

可以随意操作Connection。
面向Statement的模板方法:
该模板方法主要处理基于SQL的数据访问请求。该组模板方法通过org.springframework.jdbc.core.StatementCallback回调接口,对外公开java.sql.Statement的操作句柄。该方式缩小了回调接口内的权限范围,但是提高了API使用上的安全性和便捷性。
面向PreparedStatement的模板方法:
对于使用包含查询参数的SQL请求来说,使用PreparedStatement可以让我们免于SQL注入的攻击,而在使用PreparedStatement之前,需要根据传入的包含参数的SQL对其进行创建,所以,面向PreparedStatement的模板方式会通过org.springframework.jdbc.core.PreparedStatementCreator的回调接口公开Connection以允许PreparedStatement的创建。PreparedStatement创建之后,会公开org.springframework.jdbc.core.PreparedStatementCallback回调接口,以支持其使用PreparedStatement进行数据访问。
面向CallableStatement的模板方法:
JDBC支持使用CallableStatement进行数据库存储过程的访问,面向CallableStatement的的模板方法会通过org.springframework.jdbc.core.CallableStatementCreator公开的Connection用于创建调用存储过程的CallableStatement。之后,再通过org.springframework.jdbc.core.CallableStatementCallback公开的CallableStatement的操作句柄,实现基于存储过程的数据访问。
每一组的模板方法都 有一个核心的方法实现,其它的属于同一组的重载的模板方法,会调用这个核心的方法来完成最终的工作。以面向Statement的模板方法为例,使用StatementCallback回调接口作为方法参数的execute方法是这组的核心代码:
01    public Object execute(StatementCallback action) throws DataAccessException {
02        Assert.notNull(action, "Callback object must not be null");
03    
04        Connection con = DataSourceUtils.getConnection(getDataSource());
05        Statement stmt = null;
06        try {
07            Connection conToUse = con;
08            if (this.nativeJdbcExtractor != null &&
09                    this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
10                conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
11            }
12            stmt = conToUse.createStatement();
13            applyStatementSettings(stmt);
14            Statement stmtToUse = stmt;
15            if (this.nativeJdbcExtractor != null) {
16                stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
17            }
18            Object result = action.doInStatement(stmtToUse);
19            handleWarnings(stmt);
20            return result;
21        }
22        catch (SQLException ex) {
23            // Release Connection early, to avoid potential connection pool deadlock
24            // in the case when the exception translator hasn't been initialized yet.
25            JdbcUtils.closeStatement(stmt);
26            stmt = null;
27            DataSourceUtils.releaseConnection(con, getDataSource());
28            con = null;
29            throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
30        }
31        finally {
32            JdbcUtils.closeStatement(stmt);
33            DataSourceUtils.releaseConnection(con, getDataSource());
34        }
35    }

其它模板方法会根据自身的签名,构建相应的StatementCallback实例以调用回调接口中公开的方法,例如:
01    public void execute(final String sql) throws DataAccessException {
02        if (logger.isDebugEnabled()) {
03            logger.debug("Executing SQL statement [" + sql + "]");
04        }
05    
06        class ExecuteStatementCallback implements StatementCallback, SqlProvider {
07            public Object doInStatement(Statement stmt) throws SQLException {
08                stmt.execute(sql);
09                return null;
10            }
11            public String getSql() {
12                return sql;
13            }
14        }
15        execute(new ExecuteStatementCallback());
16    }

同一组内的模板方法,可以根据使用的方便性进行增加,只要在实现的时候,将相应的条件加以对应,改组的回调接口进行封装,最终调用当前组的核心模板方法即可。
下面来逐一看下这些方法:
public void execute(final String sql):
01    public void execute(final String sql) throws DataAccessException {
02        if (logger.isDebugEnabled()) {
03            logger.debug("Executing SQL statement [" + sql + "]");
04        }
05    
06        class ExecuteStatementCallback implements StatementCallback, SqlProvider {
07            public Object doInStatement(Statement stmt) throws SQLException {
08                stmt.execute(sql);
09                return null;
10            }
11            public String getSql() {
12                return sql;
13            }
14        }
15        execute(new ExecuteStatementCallback());
16    }

根据传入的静态SQL语句进行更新,无返回值,使用的是Statement

 public Object execute(String callString, CallableStatementCallback action) throws DataAccessException:
1    public Object execute(String callString, CallableStatementCallback action) throws DataAccessException {
2        return execute(new SimpleCallableStatementCreator(callString), action);
3    }

内部调的是
01    public Object execute(CallableStatementCreator csc, CallableStatementCallback action)
02            throws DataAccessException {
03    
04        Assert.notNull(csc, "CallableStatementCreator must not be null");
05        Assert.notNull(action, "Callback object must not be null");
06        if (logger.isDebugEnabled()) {
07            String sql = getSql(csc);
08            logger.debug("Calling stored procedure" + (sql != null ? " [" + sql  + "]" : ""));
09        }
10    
11        Connection con = DataSourceUtils.getConnection(getDataSource());
12        CallableStatement cs = null;
13        try {
14            Connection conToUse = con;
15            if (this.nativeJdbcExtractor != null) {
16                conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
17            }
18            cs = csc.createCallableStatement(conToUse);
19            applyStatementSettings(cs);
20            CallableStatement csToUse = cs;
21            if (this.nativeJdbcExtractor != null) {
22                csToUse = this.nativeJdbcExtractor.getNativeCallableStatement(cs);
23            }
24            Object result = action.doInCallableStatement(csToUse);
25            handleWarnings(cs);
26            return result;
27        }
28        catch (SQLException ex) {
29            // Release Connection early, to avoid potential connection pool deadlock
30            // in the case when the exception translator hasn't been initialized yet.
31            if (csc instanceof ParameterDisposer) {
32                ((ParameterDisposer) csc).cleanupParameters();
33            }
34            String sql = getSql(csc);
35            csc = null;
36            JdbcUtils.closeStatement(cs);
37            cs = null;
38            DataSourceUtils.releaseConnection(con, getDataSource());
39            con = null;
40            throw getExceptionTranslator().translate("CallableStatementCallback", sql, ex);
41        }
42        finally {
43            if (csc instanceof ParameterDisposer) {
44                ((ParameterDisposer) csc).cleanupParameters();
45            }
46            JdbcUtils.closeStatement(cs);
47            DataSourceUtils.releaseConnection(con, getDataSource());
48        }
49    }

JdbcTemplate在此提供了一个内部类SimpleCallableStatementCreator:
01    private static class SimpleCallableStatementCreator implements CallableStatementCreator, SqlProvider {
02    
03        private final String callString;
04    
05        public SimpleCallableStatementCreator(String callString) {
06            Assert.notNull(callString, "Call string must not be null");
07            this.callString = callString;
08        }
09    
10        public CallableStatement createCallableStatement(Connection con) throws SQLException {
11            return con.prepareCall(this.callString);
12        }
13    
14        public String getSql() {
15            return this.callString;
16        }
17    }

根据传入的sql语句创建一CallableStatement

public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException
1    public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {
2        return execute(new SimplePreparedStatementCreator(sql), action);
3    }

可知其内部调用了
execute(new SimplePreparedStatementCreator(sql), action)方法。
01    public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)
02            throws DataAccessException {
03    
04        Assert.notNull(psc, "PreparedStatementCreator must not be null");
05        Assert.notNull(action, "Callback object must not be null");
06        if (logger.isDebugEnabled()) {
07            String sql = getSql(psc);
08            logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
09        }
10    
11        Connection con = DataSourceUtils.getConnection(getDataSource());
12        PreparedStatement ps = null;
13        try {
14            Connection conToUse = con;
15            if (this.nativeJdbcExtractor != null &&
16                    this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
17                conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
18            }
19            ps = psc.createPreparedStatement(conToUse);
20            applyStatementSettings(ps);
21            PreparedStatement psToUse = ps;
22            if (this.nativeJdbcExtractor != null) {
23                psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
24            }
25            Object result = action.doInPreparedStatement(psToUse);
26            handleWarnings(ps);
27            return result;
28        }
29        catch (SQLException ex) {
30            // Release Connection early, to avoid potential connection pool deadlock
31            // in the case when the exception translator hasn't been initialized yet.
32            if (psc instanceof ParameterDisposer) {
33                ((ParameterDisposer) psc).cleanupParameters();
34            }
35            String sql = getSql(psc);
36            psc = null;
37            JdbcUtils.closeStatement(ps);
38            ps = null;
39            DataSourceUtils.releaseConnection(con, getDataSource());
40            con = null;
41            throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
42        }
43        finally {
44            if (psc instanceof ParameterDisposer) {
45                ((ParameterDisposer) psc).cleanupParameters();
46            }
47            JdbcUtils.closeStatement(ps);
48            DataSourceUtils.releaseConnection(con, getDataSource());
49        }
50    }

在创建PreparedStatementCreator实现类的时候,JdbcTemplate为其默认提供了一个SimplePreparedStatementCreator内部静态类,可根据传入的SQL语句创建一个PreparedStatement  代码如下:
view source
print?
01    private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {
02    
03        private final String sql;
04    
05        public SimplePreparedStatementCreator(String sql) {
06            Assert.notNull(sql, "SQL must not be null");
07            this.sql = sql;
08        }
09    
10        public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
11            return con.prepareStatement(this.sql);
12        }
13    
14        public String getSql() {
15            return this.sql;
16        }
17    }
其它的模仿方法与此类似,可以触类旁通!
原创粉丝点击