zoukankan      html  css  js  c++  java
  • Spring 数据库连接(Connection)绑定线程(Thread)的实现

    最近在看spring事务的时候在想一个问题:spring中的很多bean都是单例的,是非状态的,而数据库连接是一种有状态的对象,所以spring一定在创建出connection之后在threadlocal中保存了它。今天正好有空,就看了一下源码:

        /**
         * Bind the given resource for the given key to the current thread.
         * @param key the key to bind the value to (usually the resource factory)
         * @param value the value to bind (usually the active resource object)
         * @throws IllegalStateException if there is already a value bound to the thread
         * @see ResourceTransactionManager#getResourceFactory()
         */
        public static void bindResource(Object key, Object value) throws IllegalStateException {
            Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
            Assert.notNull(value, "Value must not be null");
            Map<Object, Object> map = resources.get();
            // set ThreadLocal Map if none found
            if (map == null) {
                map = new HashMap<>();
                resources.set(map);
            }
            Object oldValue = map.put(actualKey, value);
            // Transparently suppress a ResourceHolder that was marked as void...
            if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
                oldValue = null;
            }
            if (oldValue != null) {
                throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
                        actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +
                        Thread.currentThread().getName() + "]");
            }
        }

    代码很简单,以dataSource为key,ConnectionHolder为value存进了一个map里,而这个叫做resources的map是一个Threadlocal变量,存在于当前线程的ThreadlocalMap里。

    bindResource是在DataSourceTransactionManager.doBegin()方法中被调用的,来看看这个方法

    /**
         * This implementation sets the isolation level but ignores the timeout.
         */
        @Override
        protected void doBegin(Object transaction, TransactionDefinition definition) {
            DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
            Connection con = null;
    
            try {
                if (!txObject.hasConnectionHolder() ||
                        txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                    Connection newCon = obtainDataSource().getConnection();
                    if (logger.isDebugEnabled()) {
                        logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
                    }
                    txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
                }
    
                txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
                con = txObject.getConnectionHolder().getConnection();
            //这里设置隔离级别
                Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
                txObject.setPreviousIsolationLevel(previousIsolationLevel);
    
                // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
                // so we don't want to do it unnecessarily (for example if we've explicitly
                // configured the connection pool to set it already).
           //这里设置自动提交由spring控制
    if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); } prepareTransactionalConnection(con, definition);
    //设置该连接现在已经有事务了 txObject.getConnectionHolder().setTransactionActive(
    true); int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); }        // Bind the connection holder to the thread.
           //这里把新连接绑定到当前线程
    if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }

    该方法的主要作用都写在注释里了,看一下就好。

  • 相关阅读:
    Redis流程初始认知
    浅谈windows和linux进程和线程的区别
    linux下的信号量PV操作进阶之路
    linux下线程以及pthread库
    分清问题的主次
    linux下主线程return 0和pthread_exit(NULL)的区别
    进程和线程的区别
    注意搜索的陷阱
    模拟主线程等待子线程的过程
    【伯乐在线】编程面试的10大算法概念汇总
  • 原文地址:https://www.cnblogs.com/bethunebtj/p/7688129.html
Copyright © 2011-2022 走看看