君子博学而日参省乎己 则知明而行无过矣

博客园 首页 新随笔 联系 订阅 管理

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实现任务后进行开发,他或许开发了如下所示的代码:

01package com.google.spring.jdbc;
02 
03import java.sql.Connection;
04import java.sql.SQLException;
05import java.sql.Statement;
06 
07import javax.sql.DataSource;
08 
09import org.apache.commons.logging.Log;
10import org.apache.commons.logging.LogFactory;
11 
12public 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、踩油门启动车辆运行
此时,我们可以声明一个模板方法类,将确定的行为以模板的形式定义,而将不同的行为留给相应的子类来实现:

01package com.google.spring.jdbc;
02 
03public 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,表示该类是不能被子类重写的,车辆的自动挡和手动挡是不同的,所以留给了子类去实现: 
01package com.google.spring.jdbc;
02 
03public class VehicleAT extends Vehicle
04{
05 
06    @Override
07    protected void putIntoGear()
08    {
09        //挂前进档位
10 
11    }
12 
13}

01package com.google.spring.jdbc;
02 
03public 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的雏形:

01package com.google.spring.jdbc;
02 
03import java.sql.Connection;
04import java.sql.SQLException;
05import java.sql.Statement;
06 
07import javax.sql.DataSource;
08 
09import org.springframework.dao.DataAccessException;
10 
11 
12public 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接口以后: 
1package com.google.spring.jdbc;
2 
3import java.sql.Statement;
4 
5public interface StatementCallback
6{
7    public Object doWithStatement(Statement stmt) throws SQLException;
8}

这样这个真正的Helper类就存在了,如下所示: 
01package com.google.spring.jdbc;
02 
03import java.sql.Connection;
04import java.sql.SQLException;
05import java.sql.Statement;
06 
07import javax.sql.DataSource;
08 
09import org.springframework.dao.DataAccessException;
10 
11 
12public  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就可以了,如下所示: 
01final String sql = "update";
02JdbcTemplate jdbcTemplate = new JdbcTemplate();
03BasicDataSource dataSource = new BasicDataSource();
04jdbcTemplate.setDataSource(dataSource);
05//对dataSource进行setter操作
06StatementCallback callback = new StatementCallback()
07{
08    public Object doWithStatement(Statement stmt) throws SQLException
09    {
10        return new Integer(stmt.executeUpdate(sql)) ;
11    }
12};
13 
14jdbcTemplate.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进行数据访问

1import java.sql.Connection;
2import java.sql.SQLException;
3 
4import org.springframework.dao.DataAccessException;
5 
6public interface ConnectionCallback
7{
8    Object doInConnection(Connection con) throws SQLException, DataAccessException;
9}

01public 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方法是这组的核心代码: 
01public 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实例以调用回调接口中公开的方法,例如: 
01public 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): 
01public 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:

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

内部调的是 
01public 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: 
01private static class SimpleCallableStatementCreator implementsCallableStatementCreator, 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) throwsSQLException {
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

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

可知其内部调用了 
execute(new SimplePreparedStatementCreator(sql), action)方法。 
01public 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  代码如下: 
01private static class SimplePreparedStatementCreator implementsPreparedStatementCreator, 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) throwsSQLException {
11        return con.prepareStatement(this.sql);
12    }
13 
14    public String getSql() {
15        return this.sql;
16    }
17}
其它的模仿方法与此类似,可以触类旁通!
posted on 2012-08-06 23:33  刺猬的温驯  阅读(444)  评论(0)    收藏  举报