SpringJDBC解析2-execute方法

大家都使用过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);
}  
时间: 2024-08-27 22:39:22

SpringJDBC解析2-execute方法的相关文章

SpringJDBC解析4-query方法

重要步骤说明: 首先是从PersonServiceImpl方法进去,调用JdbcTemplate的query方法,然后执行一连串错中复杂的调用,而且里面有很多函数都是以回调形式处理, 1)JdbcTemplate接受到query请求,由于query没有带参数,所以选择不带sql参数的重载方法query执行. 2)query方法面会创建一个内部类(QueryStatementCallback),然后实例化,传给execute方法,等待execute回调. 3)上面是传了一个callback对象的实

12.ThreadPoolExecutor线程池原理及其execute方法

jdk1.7.0_79  对于线程池大部分人可能会用,也知道为什么用.无非就是任务需要异步执行,再者就是线程需要统一管理起来.对于从线程池中获取线程,大部分人可能只知道,我现在需要一个线程来执行一个任务,那我就把任务丢到线程池里,线程池里有空闲的线程就执行,没有空闲的线程就等待.实际上对于线程池的执行原理远远不止这么简单. 在Java并发包中提供了线程池类——ThreadPoolExecutor,实际上更多的我们可能用到的是Executors工厂类为我们提供的线程池:newFixedThread

execute方法有感

有一个method困扰了我一周之久:execute 我跟着<java web开发技术详解>这本书一步一步编程,结果卡在了this.execute(request,response); 编译器一直报错:cannot resolve method execute 于是我百度,谷歌,查java sdk. 就在准备砸电脑的时候,我发现这个execute方法是作者自己定义的,后面一页就有... 这件事告诉我们,相比于埋头拉车,抬头看路才是最重要的. bye~

vbs 解析 json jsonp 方法

昨天说了下用 htmlfile 来解析 html,今天依然用他来解析,htmlfile 是一个 COM 版的 BOM 和 DOM,所以解析 html, 执行 js 完全不在话下,今天就继续解析 json 和 jsonp 吧. 我收集了三种常见 json jsonp 的格式,因为很多人找我说如何用正则提取某个字符串,我看了下,要么是json,要么是jsonp,正则简直浪费,比如那种空间里说说的数据,上百条的信息,你正则个P,而且还有回复里的格式也差不多的,所以最好的方法还是解析. 先来看看常见的三

Struts1.x有两个execute方法,不要重写错哦HttpServletRequest才是对的(转)

Struts1.x 的 Action 有两个 execute 哦,小心搞错! by agate - Published: 2008-05-01 [9:42 下午] - Category: 程序编码 不知道各位使用 Struts1.x 的朋友们晓得不晓得,Struts1.x 的 org.apache.struts.action.Action 中有两个 execute 方法,在我们使用 eclipse 的自动完成 override 功能的时候要是不小心给弄错了你就等着迎接一个不报错的空白页面吧!让我

第五课 Struts的控制器【续】Action类的execute()方法

1.Action类的execute()方法: public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException : 2.Action类的execute()方法的参数和返回值: – execute()方法包含以下参数: –ActionMap

十七.jQuery源码解析之入口方法Sizzle(1)

函数Sizzle(selector,context,results,seed)用于查找与选择器表达式selector匹配的元素集合.该函数是选择器引擎的入口. 函数Sizzle执行的6个关键步骤如下: 1.解析选择器表达式,解析出块表达式和关系符. 2.如果存在位置伪类,则从左向右查找: a.查找第一个块表达式匹配的元素集合,得到第一个上下文元素集合. b.遍历剩余的块表达式和块间关系符,不断缩小上下文元素集合. 3.否则从右向左查找: a.查找最后一个块表达式匹配的元素集合,得到候选集,映射集

Java并发---- Executor并发框架--ThreadToolExecutor类详解(execute方法)

1.构造方法 请参考上篇文章:http://blog.csdn.net/ochangwen/article/details/53044733 2.源码详解 线程池内部有一些状态,先来了解下这些状态的机制.以下用代码注释的方式来解释其中的含义. /* 这个是用一个int来表示workerCount和runState的,其中runState占int的高3位, 其它29位为workerCount的值. workerCount:当前活动的线程数: runState:线程池的当前状态. 用AtomicIn

Struts2 调用非execute方法

调用非execute方法 1)如果你的Action类是继承自ActionSupport的话,确切的说是重写了execute方法,ActionSupport里的默认实现就是返回"success"视图.因此,你可以不实现execute方法,只要你的struts.xml里有"success"对应的result即可. 2)如果你的Action类没有继承ActionSupport,而你又没有在struts.xml中对应<action>标签中用method属性指定你