`
huibin
  • 浏览: 755101 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
社区版块
存档分类
最新评论

Spring管理JDBC连接

阅读更多

在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数据库,在JdbcTemplate为用户程序提供了许多便利的数据库操作方法,比如查询,更新等,而且在Spring中,有许多类似 JdbcTemplate的模板,比如HibernateTemplate等等 - 看来这是Rod.Johnson的惯用手法,一般而言这种Template中都是通过回调函数CallBack类的使用来完成功能的,客户需要在回调接口中实现自己需要的定制行为,比如使用客户想要用的SQL语句等。不过往往Spring通过这种回调函数的实现已经为我们提供了许多现成的方法供客户使用。一般来说回调函数的用法采用匿名类的方式来实现,比如:

代码
JdbcTemplate = new JdbcTemplate(datasource);   
jdbcTemplate.execute(new CallBack(){   
            public CallbackInterfacedoInAction(){   
                ......   
               //用户定义的代码或者说Spring替我们实现的代码   
             }   
}   

在模板中嵌入的是需要客户化的代码,由Spring来作或者需要客户程序亲自动手完成。下面让我们具体看看在JdbcTemplate中的代码是怎样完成使命的,我们举JdbcTemplate.execute()为例,这个方法是在JdbcTemplate中被其他方法调用的基本方法之一,客户程序往往用这个方法来执行基本的SQL语句:

代码
public Object execute(ConnectionCallback action) throws DataAccessException {   
    //这里得到数据库联接   
     Connection con = DataSourceUtils.getConnection(getDataSource());   
    try {   
         Connection conToUse = con;   
        //有些特殊的数据库,需要我们使用特别的方法取得datasource   
        if (this.nativeJdbcExtractor != null) {   
            // Extract native JDBC Connection, castable to OracleConnection or the like.   
             conToUse = this.nativeJdbcExtractor.getNativeConnection(con);   
         }   
        else {   
            // Create close-suppressing Connection proxy, also preparing returned Statements.   
             conToUse = createConnectionProxy(con);   
         }   
    //这里调用的是传递进来的匿名类的方法,也就是用户程序需要实现CallBack接口的地方。   
        return action.doInConnection(conToUse);   
     }   
    catch (SQLException ex) {   
        //如果捕捉到数据库异常,把数据库联接释放,同时抛出一个经过Spring转换过的Spring数据库异常,   
        //我们知道,Spring做了一个有意义的工作是把这些数据库异常统一到自己的异常体系里了。   
         DataSourceUtils.releaseConnection(con, getDataSource());   
         con = null;   
        throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);   
     }   
    finally {   
        //最后不管怎样都会把数据库连接释放   
         DataSourceUtils.releaseConnection(con, getDataSource());   
     }   
}   

对于JdbcTemplate中给出的其他方法,比如query,update,execute等的实现,我们看看query():

代码
public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)   
        throws DataAccessException {   
     ..........   
    //这里调用了我们上面看到的execute()基本方法,然而这里的回调实现是Spring为我们完成的查询过程   
    return execute(psc, new PreparedStatementCallback() {   
        public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {   
            //准备查询结果集   
             ResultSet rs = null;   
            try {   
            //这里配置SQL参数   
                if (pss != null) {   
                     pss.setValues(ps);   
                 }   
          //这里执行的SQL查询   
                 rs = ps.executeQuery();   
                 ResultSet rsToUse = rs;   
                if (nativeJdbcExtractor != null) {   
                     rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);   
                 }   
         //返回需要的记录集合   
                return rse.extractData(rsToUse);   
             }   
            finally {   
        //最后关闭查询的纪录集,对数据库连接的释放在execute()中释放,就像我们在上面分析的看到那样。   
                 JdbcUtils.closeResultSet(rs);   
                if (pss instanceof ParameterDisposer) {   
                     ((ParameterDisposer) pss).cleanupParameters();   
                 }   
             }   
         }   
     });   
}   

辅助类DataSourceUtils来用来对数据库连接进行管理的主要工具,比如打开和关闭数据库连接等基本操作:

代码
public static Connection doGetConnection(DataSource dataSource) throws SQLException {   
   //把对数据库连接放到事务管理里面进行管理   
     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();   
     }   
    // 这里得到需要的数据库连接,在配置文件中定义好的。   
     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;   
}   

那我们实际的DataSource对象是怎样得到的?很清楚我们需要在上下文中进行配置:它作为JdbcTemplate父类JdbcAccessor的属性存在:

代码
public abstract class JdbcAccessor implements InitializingBean {   
  
    /** 这里是我们依赖注入数据库数据源的地方。 */  
    private DataSource dataSource;   
  
    /** Helper to translate SQL exceptions to DataAccessExceptions */  
    private SQLExceptionTranslator exceptionTranslator;   
  
    private boolean lazyInit = true;   
  
     ........   
}   

而对于DataSource的缓冲池实现,我们通过定义Apache Jakarta Commons DBCP或者C3P0提供的DataSource来完成,然后只要在上下文中配置好就可以使用了。从上面我们看到JdbcTemplate提供了许多简单查询和更新功能,但是如果需要更高层次的抽象,以及更面向对象的方法来访问数据库。Spring为我们提供了org.springframework.jdbc.object包,这里面包含了SqlQuery,SqlMappingQuery, SqlUpdate和StoredProcedure等类,这些类都是Spring JDBC应用程序可以使用的主要类,但我们要注意使用这些类的时候,用户需要为他们配置好一个JdbcTemplate作为其基本的操作的实现。
比如说我们使用MappingSqlQuery来将表数据直接映射到一个对象集合 - 具体可以参考书中的例子
1.我们需要建立DataSource和sql语句并建立持有这些对象的MappingSqlQuery对象
2.然后我们需要定义传递的SqlParameter,具体的实现我们在MappingSqlQuery的父类RdbmsOperation中可以找到:

代码
public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {   
   //如果声明已经被编译过,则该声明无效   
   if (isCompiled()) {   
       throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled");   
    }   
   //这里对参数值进行声明定义   
   this.declaredParameters.add(param);   
  

而这个declareParameters维护的是一个列表:

代码
/** List of SqlParameter objects */  
private List declaredParameters = new LinkedList();   

这个列表在以后compile的过程中会被使用。
3.然后用户程序需要实现MappingSqlQuery的mapRow接口,将具体的ResultSet数据生成我们需要的对象,这是我们迭代使用的方法。1,2,3步实际上为我们定义好了一个迭代的基本单元作为操作模板。
4.在应用程序,我们直接调用execute()方法得到我们需要的对象列表,列表中的每一个对象的数据来自于执行SQL语句得到记录集的每一条记录,事实上执行的execute在父类SqlQuery中起作用:

代码
public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException {   
     validateNamedParameters(paramMap);   
     Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap);   
     RowMapper rowMapper = newRowMapper(parameters, context);   
     String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap));   
    //我们又看到了JdbcTemplate,这里使用JdbcTemplate来完成对数据库的查询操作,所以我们说JdbcTemplate是基本的操作类。   
     return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);   
}   

在这里我们可以看到template模式的精彩应用和对JdbcTemplate的灵活使用。通过使用它,我们免去了手工迭代ResultSet并将其中的数据转化为对象列表的重复过程。在这里我们只需要定义SQL语句和SqlParameter - 如果需要的话,往往SQL语句就常常能够满足我们的要求了。这是灵活使用JdbcTemplate的一个很好的例子。
Spring还为其他数据库操作提供了许多服务,比如使用SqlUpdate插入和更新数据库,使用UpdatableSqlQuery更新ResultSet,生成主键,调用存储过程等。
书中还给出了对BLOB数据和CLOB数据进行数据库操作的例子:
对BLOB数据的操作通过LobHander来完成,通过调用JdbcTemplate和RDBMS都可以进行操作:
在JdbcTemplate中,具体的调用可以参考书中的例子 - 是通过以下调用起作用的:

代码
public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {   
    return execute(new SimplePreparedStatementCreator(sql), action);   
}   

然后通过对实现PreparedStatementCallback接口的AbstractLobCreatingPreparedStatementCallback的回调函数来完成:

代码
public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {   
     LobCreator lobCreator = this.lobHandler.getLobCreator();   
    try {   
        //这是一个模板方法,具体需要由客户程序实现   
         setValues(ps, lobCreator);   
        return new Integer(ps.executeUpdate());   
     }   
    finally {   
         lobCreator.close();   
     }   
}   
//定义的需要客户程序实现的虚函数   
protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)   
        throws SQLException, DataAccessException;   

而我们注意到setValues()是一个需要实现的抽象方法,应用程序通过实现setValues来定义自己的操作 - 在setValues中调用lobCreator.setBlobAsBinaryStrem()。让我们看看具体的BLOB操作在LobCreator是怎样完成的,我们一般使用DefaultLobCreator作为BLOB操作的驱动:

代码
public void setBlobAsBinaryStream(   
         PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength)   
        throws SQLException {   
    //通过JDBC来完成对BLOB数据的操作,对Oracle,Spring提供了OracleLobHandler来支持BLOB操作。   
     ps.setBinaryStream(paramIndex, binaryStream, contentLength);   
     ........   
}   

上面提到的是零零碎碎的Spring JDBC使用的例子,可以看到使用Spring JDBC可以帮助我们完成许多数据库的操作。Spring对数据库操作最基本的服务是通过JdbcTeamplate和他常用的回调函数来实现的,在此之上,又提供了许多RMDB的操作来帮助我们更便利的对数据库的数据进行操作 - 注意这里没有引入向Hibernate这样的O/R方案。对这些O/R方案的支持,Spring由其他包来完成服务。
书中还提到关于execute和update方法之间的区别,update方法返回的是受影响的记录数目的一个计数,并且如果传入参数的话,使用的是java.sql.PreparedStatement,而execute方法总是使用 java.sql.Statement,不接受参数,而且他不返回受影响记录的计数,更适合于创建和丢弃表的语句,而update方法更适合于插入,更新和删除操作,这也是我们在使用时需要注意的。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/teedry/archive/2009/10/20/4703049.aspx

分享到:
评论

相关推荐

    python入门-30.寻找列表中只出现一次的数字-寻找单身狗.py

    python入门-30.寻找列表中只出现一次的数字——寻找单身狗.py

    布尔教育linux优化笔记

    linux优化笔记,配套视频:https://www.bilibili.com/list/474327672?sid=4496133&spm_id_from=333.999.0.0&desc=1

    知识付费系统-直播+讲师入驻+课程售卖+商城系统-v2.1.9版本搭建以及资源分享下载

    知识付费系统-直播+讲师入驻+课程售卖+商城系统-v2.1.9版本搭建以及资源分享下载,CRMEB知识付费分销与直播营销系统是由西安众邦科技自主开发的一款在线教育平台,该系统不仅拥有独立的知识产权,还采用了先进的ThinkPhp5.0框架和Vue前端技术栈,集成了在线直播教学及课程分销等多种功能,旨在为用户提供全方位的学习体验,默认解压密码youyacaocom

    美妆神域-JAVA-基于springBoot美妆神域设计与实现

    美妆神域-JAVA-基于springBoot美妆神域设计与实现

    原生js制作Google粘土logo动画涂鸦代码.zip

    原生js制作Google粘土logo动画涂鸦代码.zip

    golin 扫描工具使用, 检查系统漏洞、web程序漏洞

    golin 扫描工具使用, 检查系统漏洞、web程序漏洞

    原生态纯js图片网格鼠标悬停放大显示特效代码下载.zip

    原生态纯js图片网格鼠标悬停放大显示特效代码下载.zip

    用AWLUM进行灰色编码2^2n-QAM调制的精确率Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    去水印web端独立版web

    去水印web端独立版web

    原生js制作左侧浮动可折叠在线客服代码.zip

    原生js制作左侧浮动可折叠在线客服代码.zip

    Chrome 谷歌浏览器下载

    Chrome 谷歌浏览器下载

    亲测全新完整版H5商城系统源码 附教程

    全新完整版H5商城系统源码 自己花钱买的,亲测可用,需要自行下载 H5商城系统设置是实现商城基本功能的核心部分,涵盖了从网站配置、短信和支付配置,到商品、工单、订单、分站和提现管理等多个模块的设置。以下是详细的设置指南,帮助您快速上手并高效管理商城系统。 测试环境:Nginx+PHP7.0+MySQL5.6 1. 网站配置 设置商城名称、LOGO、标题、联系方式和SEO关键词等,确保商城专业和易于搜索。 2. 短信配置 配置短信接口和模板,用于发送订单通知、验证码等,提升用户体验。 3. 支付接口配置 配置微信、支付宝等支付接口,填写API密钥和回调地址,确保支付流畅。 4. 商品分类管理 对商品进行分类和排序,设置分类名称和图标,便于用户查找商品。 5. 商品管理 添加和管理商品信息、规格、图片等,确保商品信息准确丰富。 6. 工单管理 查看和回复用户工单,记录售后问题,提升用户服务质量。 7. 订单管理 查看订单详情,更新订单状态,支持批量导出,方便订单跟踪。 8. 分站管理 创建不同区域分站,设置权限,统一管理各区域市场。 9. 提现管理

    短信3.141592672893982398674234

    apk安装包

    原生js选项卡插件自定义图片滑动选项卡切换.zip

    原生js选项卡插件自定义图片滑动选项卡切换.zip

    1-宗教信息佛教佛寺寺庙庵堂相关数据-社科数据.zip

    宗教信息佛教佛寺寺庙庵堂相关数据集提供了全国各个地区省市县各个佛教寺庙的详细信息。这些数据不仅包括寺庙的名称和负责人姓名,还涵盖了所属省份、地级市、区县、具体地址、建立日期以及支派类别等关键信息。该数据集整理了超过3万条样本,为研究中国佛教寺庙的分布、历史和文化提供了丰富的第一手资料。这些信息有助于了解佛教在中国的传播和发展,以及寺庙在社会和文化中的作用。数据的整理和提供,对于宗教学、社会学、历史学和文化研究等领域的学者来说,是一个宝贵的资源。

    线性电阻网络的等效电阻计算Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    简单的 Python 版本管理.zip

    简单的 Python 版本管理pyenvpyenv 可让您轻松在多个 Python 版本之间切换。它简单、不引人注目,并遵循 UNIX 传统,即使用单一用途的工具来做好一件事。该项目由rbenv和 ruby​​-build分叉而来,并针对 Python 进行了修改。pyenv 的作用是什么......允许您根据每个用户更改全局 Python 版本。为每个项目的 Python 版本提供支持。允许您使用环境变量覆盖 Python 版本。一次搜索多个 Python 版本的命令。这可能有助于使用tox跨 Python 版本进行测试。与 pythonbrew 和 pythonz 相比,pyenv没有……依赖于Python本身。pyenv由纯shell脚本制作。不存在Python的引导问题。需要加载到你的 shell 中。相反,pyenv 的 shim 方法通过向你的 中添加目录来工作PATH。管理虚拟环境。当然,你可以自己创建虚拟环境 ,或者使用pyenv-virtualenv 来自动化该过程。目录安装获取 PyenvLinux/UNIX自动安装程序基本

    Notepad-v2.20工具,是替代Notepad++的首选工具

    Notepad-v2.20工具,是替代Notepad++的首选工具

    原生js随机图片拖拽排序代码.zip

    原生js随机图片拖拽排序代码.zip

    更快、更好、更稳定的Redis桌面(GUI)管理客户端,兼容Windows、Mac、Linux,性能出众,轻松加载海量键值

    更快、更好、更稳定的Redis桌面(GUI)管理客户端,兼容Windows、Mac、Linux,性能出众,轻松加载海量键值

Global site tag (gtag.js) - Google Analytics