关于JDBC中Connection的使用的几个details

本文探讨了JDBC中Connection的正确使用方式,包括为何应在每个方法调用时新建Connection及如何确保同一线程中所有数据库操作使用相同的Connection,提出了使用ThreadLocal或Spring事务管理两种方案。

最近一个小项目DAO层涉及对Connection的获取,优化的时候顺便了解了一下JDBC中Connection使用的几个细节问题,跟大家分享一下:

1. 通常情况下,应在每个方法调用时新建Connection。
一般要在每个方法调用时新建connection,因为connection实例并不是线程安全的,下面给出Mysql中调用Connection实现类的方法(com.mysql.jdbc.PreparedStatement中executeUpdate())实例:

    /**
     * com.mysql.jdbc.PreparedStatement中的executeUpdete()    
     * @throws SQLException
     */

    protected synchronized int executeUpdate(byte[][] batchedParameterStrings,
            InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
            int[] batchedStreamLengths, boolean[] batchedIsNull, boolean isReallyBatch)
            throws SQLException {

        checkClosed();

        //声明MySQLConnection
        MySQLConnection locallyScopedConn = this.connection;

        if (locallyScopedConn.isReadOnly()) {
            throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") //$NON-NLS-1$
                    + Messages.getString("PreparedStatement.35"), //$NON-NLS-1$
                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
        }

        if ((this.firstCharOfStmt == 'S')
                && isSelectQuery()) { //$NON-NLS-1$
            throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), //$NON-NLS-1$
                    "01S03", getExceptionInterceptor()); //$NON-NLS-1$
        }

        if (this.results != null) {
            if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
                this.results.realClose(false);
            }
        }

        ResultSetInternalMethods rs = null;

        // The checking and changing of catalogs
        // must happen in sequence, so synchronize
        // on the same mutex that _conn is using
        /*注意此处,sychronized(connection实例)
        catalogs的检查和更改必须按顺序进行,因此在_conn正在使用的synchronized 进行同步*/
        synchronized (locallyScopedConn) {
            Buffer sendPacket = fillSendPacket(batchedParameterStrings,
                    batchedParameterStreams, batchedIsStream,
                    batchedStreamLengths);

            String oldCatalog = null;

            if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
                oldCatalog = locallyScopedConn.getCatalog();
                locallyScopedConn.setCatalog(this.currentCatalog);
            }

            //
            // Only apply max_rows to selects
            //
            if (locallyScopedConn.useMaxRows()) {
                executeSimpleNonQuery(locallyScopedConn,
                        "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
            }

            boolean oldInfoMsgState = false;

            if (this.retrieveGeneratedKeys) {
                oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
                locallyScopedConn.setReadInfoMsgEnabled(true);
            }

            rs = executeInternal(-1, sendPacket, false, false, null, 
                    isReallyBatch);

            if (this.retrieveGeneratedKeys) {
                locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
                rs.setFirstCharOfQuery(this.firstCharOfStmt);
            }

            if (oldCatalog != null) {
                locallyScopedConn.setCatalog(oldCatalog);
            }
        }

一个connection对象只能被一个线程所使用,如果涉及到多个线程同时调用同一个connection将造成数据的错乱。因此,一般应在一个方法被调用时新建connection。因此多线程情况下,可以使用数据库连接池来提升性能,数据库连接池可以保证connection实例交替被多个线程使用。

2. 使用数据库连接池时,如何保证一个线程中的所有数据库操作使用的是同一个connection。
若想要同一个线程获取到的connection为同一个有两种方法:
其一,可以使用ThreadLocal保存connection。ThreadLocal内部是用Map来保存数据。其中Map键值对的减是当前线程而值即为当前线程对于的connection。这种方法适用于要求每次请求得到的connection都是唯一的,当线程消亡时才会将connection交给数据库连接池。
在网上看到有些文章交代要看数据库连接池本身是否使用了ThreadLocal然后看了下java.sql包和javax.sql下的Connection和数据库连接池都是interface,每个DatasourcePool具体可以自己更改实现类,DatasourcePool的具体实现需要的话可以采取ThreadLocal来存放connection。
其二,可以直接交给spring去管理,在srping中每个事务和一个connection绑定,在事务回滚时该connection才会被返回数据库连接池。

自己的浅薄理解,若有异议欢迎提出~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值