开发者社区> jephon> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

SpringJDBC解析2-execute方法

简介: 大家都使用过JDBCTEMPLATE的execute方法,execute作为数据库操作的核心入口,将大多数数据库操作相同的步骤统一封装,而将个性化的操作使用参数PreparedStatementCallback回调。
+关注继续查看

大家都使用过JDBCTEMPLATE的execute方法,execute作为数据库操作的核心入口,将大多数数据库操作相同的步骤统一封装,而将个性化的操作使用参数PreparedStatementCallback回调。

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(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);  
        }  
        //调用回掉函数  
        T result = action.doInPreparedStatement(psToUse);  
        handleWarnings(ps);  
        return result;  
    }  
    catch (SQLException ex) {  //释放数据库连接避免当异常转换器没有被初始化的时候出现潜在的连接池死锁  
        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());  
    }  
} 

以上方法对常用操作进行了封装,包括如下几项内容:

  1. 获取数据库连接
  2. 应用用户设定的输入参数
  3. 调用回调函数
  4. 警告处理
  5. 资源释放

获取数据库连接

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();  
    }  
    logger.debug("Fetching JDBC Connection from DataSource");  
    Connection con = dataSource.getConnection();  
    //当前线程支持同步  
    if (TransactionSynchronizationManager.isSynchronizationActive()) {  
        logger.debug("Registering transaction synchronization for JDBC Connection");  //在事务中使用同一数据库连接  
        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主要考虑的是关于事务方面的处理。基于事务处理的特殊性,Spring需要保证线程中的数据库操作都是使用同一个事务连接

应用用户设定的输入参数

protected void applyStatementSettings(Statement stmt) throws SQLException {  
    int fetchSize = getFetchSize();  
    if (fetchSize > 0) {  
        stmt.setFetchSize(fetchSize);  
    }  
    int maxRows = getMaxRows();  
    if (maxRows > 0) {  
        stmt.setMaxRows(maxRows);  
    }  
    DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());  
}  

setFetchSize最主要是为了减少网络交互次数设计的。访问ResultSet时,如果它每次只从服务器读取一行数据,则会产生大量的开销。setFetchSize的意思是ResultSet会一次性从服务器上取得多少行数据回来,这样在下次rs.next时,它可以直接从内存中获取数据而不需要网络交互,提高了效率。这个设置可能会被某些JDBC驱动忽略,而且设置过大也会造成内存的上升。setMaxRows将此Statement对象生成的所有ResultSet对象可以包含的最大行数设置为给定数。

调用回调函数

处理一些通用方法外的个性化处理,也就是PreparedStatementCallback类型的参数的doInPreparedStatement方法的回调。

T result = action.doInPreparedStatement(psToUse);

警告处理

protected void handleWarnings(Statement stmt) throws SQLException {  
    //当设置为忽略警告时只尝试打印日志  
    if (isIgnoreWarnings()) {  
        if (logger.isDebugEnabled()) {  
            //如果日志开启的情况下下打印日志  
            SQLWarning warningToLog = stmt.getWarnings();  
            while (warningToLog != null) {  
                logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + "', error code '" +  
                        warningToLog.getErrorCode() + "', message [" + warningToLog.getMessage() + "]");  
                warningToLog = warningToLog.getNextWarning();  
            }  
        }  
    }  
    else {  
        handleWarnings(stmt.getWarnings());  
    }  
}  

这里用到了一个类SQLWarning,SQLWarning提供关于数据库访问警告信息的异常。这些警告直接链接到导致报告警告的方法所在的对象。警告可以从Connection、Statement和ResultSet对象中获得。试图在已经关闭的连接上获取警告将导致抛出异常。类似地,试图在已经关闭的语句上或已经关闭的结果集上获取警告也将导致抛出异常。注意,关闭语句时还会关闭它可能生成的结果集。

最常见的警告DataTruncation:DataTruncation直接继承SQLWarning,由于某种原因意外地截断数据值时会以DataTruncation 警告形式报告异常。对于警告的处理方式并不是直接抛出异常,出现警告很可能会出现数据错误,但是,并不一定会影响程序执行,所以用户可以自己设置处理警告的方式,如默认的是忽略警告,当出现警告时只打印警告日志,而另一种方式只直接抛出异常。

资源释放

数据库的连接并不是直接调用了Connection的API中的close()方法。考虑到存在事务的情况,如果当前线程存在事务,那么说明在当前线程中存在共用数据库的连接,这种情况下直接使用ConnectionHolder中的released方法进行连接数减一,而不是真正的释放连接。

public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {  
    if (con == null) {  
        return;  
    }  
    if (dataSource != null) {  
        //当前线程存在事务的情况下说明存在共用数据库连接  
        //直接使用ConnectionHolder中的released方法进行连接数减一而不是真正的释放链接  
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
        if (conHolder != null && connectionEquals(conHolder, con)) {  
            // It's the transactional Connection: Don't close it.  
            conHolder.released();  
            return;  
        }  
    }  
    logger.debug("Returning JDBC Connection to DataSource");  
    //在此方法中判断是否需要关闭连接,然后再进行关闭  
    doCloseConnection(con, dataSource);  
}  

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Spring JDBC中NamedParameterJdbcTemplate的使用,包括in的用法
项目中使用到了Spring JDBC, 一般jdbcTemplate基本可以满足我们的需求,我们可以通过?占位符来传参,方式sql注入。
0 0
Java日志的学习03--log4j 配置Spring JdbcTemplate已经MyBatis打印sql
今天接着说说如何在日志中配置SQL打印输出。
0 0
Java--SpringBoot-41-Mybatis-9-调用存储过程
存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集。经编译后存储在数据库中。现在要使用Mybatis调用数据库的存储过程。我们来看下如何实现。
0 0
【SpringBoot + Mybatis系列】Mapper接口与Sql绑定几种姿势
通常我们在使用Mybatis进行开发时,会选择xml文件来写对应的sql,然后将Mapper接口与sql的xml文件建立绑定关系,然后在项目中调用mapper接口就可以执行对应的sql 那么如何将Mapper接口与sql进行绑定呢?本文将介绍四种常见的姿势
0 0
SpringBoot整合MyBatis:反正我就是喜欢写SQL
mybatis-logo IDE:IntelliJ IDEA 新建SpringBoot项目 创建项目时添加依赖 所有依赖 :如果缺少依赖请自行补齐坐标 org.
1575 0
SpringJDBC解析4-query方法
重要步骤说明: 首先是从PersonServiceImpl方法进去,调用JdbcTemplate的query方法,然后执行一连串错中复杂的调用,而且里面有很多函数都是以回调形式处理, 1)JdbcTemplate接受到query请求,由于query没有带参数,所以选择不带sql参数的重载方法query执行。
630 0
SpringJDBC解析1-使用示例
JDBC(Java Data Base Connectivity,Java数据库连接)是一种用于执行SQL语句的JavaAPI,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。
784 0
SpringJDBC解析3-回调函数(update为例)
PreparedStatementCallback作为一个接口,其中只有一个函数doInPrepatedStatement,这个函数是用于调用通用方法execute的时候无法处理的一些个性化处理方法,在update中的函数实现: protected int update(final Prepare...
694 0
调用 jdbcTemplate.queryForList 时出现错误 spring-org.springframework.jdbc.IncorrectResultSetColumnCountException
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私募机构九鼎控股打造,九鼎投资是在全国股份转让系统挂牌的公众公司,股票代码为430719,为“中国PE第一股”,市值超1000亿元。
847 0
+关注
jephon
一直在路上..........
文章
问答
文章排行榜
最热
最新
相关电子书
更多
Java Spring Boot开发实战系列课程【第15讲】:Spring Boot 2.0 API与Spring REST Docs实战
立即下载
Java Spring Boot开发实战系列课程【第6讲】:Spring Boot 2.0实战MyBatis与优化(Java面试题)
立即下载
低代码开发师(初级)实战教程
立即下载