zoukankan      html  css  js  c++  java
  • Spring 事务模板方法设计模式

    接上一篇文章

    上一篇讲到了doGetTransaction方法

    一、模板方法设计模式

    这里涉及到了一个经典的设计模式:模板方法

    如下图:

    AbstractPlatformTransactionManager实现了PlatformTranscationManager接口

    DatasourceTransactionManager,HibernateTransactionManager等继承了AbstractPlatformTransactionManager类

    AbstractPlatformTransactionManager类中定义了事务处理的若干方法。 其中获得事务,提交事务,回滚事务由具体的子类实现。

    二、进入doGetTransaction的其中一个实现类Hibernate3中的HibernateTransactionManager 类

    进入doGetTransaction方法

    	@Override
    	protected Object doGetTransaction() {
    		HibernateTransactionObject txObject = new HibernateTransactionObject();
    		txObject.setSavepointAllowed(isNestedTransactionAllowed());
    
    		SessionHolder sessionHolder =
    				(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
    		if (sessionHolder != null) {
    			if (logger.isDebugEnabled()) {
    				logger.debug("Found thread-bound Session [" +
    						SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");
    			}
    			txObject.setSessionHolder(sessionHolder);
    		}
    		else if (this.hibernateManagedSession) {
    			try {
    				Session session = getSessionFactory().getCurrentSession();
    				if (logger.isDebugEnabled()) {
    					logger.debug("Found Hibernate-managed Session [" +
    							SessionFactoryUtils.toString(session) + "] for Spring-managed transaction");
    				}
    				txObject.setExistingSession(session);
    			}
    			catch (HibernateException ex) {
    				throw new DataAccessResourceFailureException(
    						"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
    			}
    		}
    
    		if (getDataSource() != null) {
    			ConnectionHolder conHolder = (ConnectionHolder)
    					TransactionSynchronizationManager.getResource(getDataSource());
    			txObject.setConnectionHolder(conHolder);
    		}
    
    		return txObject;
    	}
    

      

    1、创建HibernateTransactionObject对象

    HibernateTransactionObject txObject = new HibernateTransactionObject();

    HibernateTransactionObject 是一个Hibernate事务对象,代表一个SessionHolder。被HibernateTransactionManger用作事务对象。

    2、进入SessionHolder

    SessionHolder: session持有者,包装了一个Hibernate Session和一个Hibernate事务

    三 、进入doGetTransaction的其中一个实现类JDBC中的DataSourceTransactionManager 类

    进入doGetTransaction方法

    	@Override
    	protected Object doGetTransaction() {
    		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    		txObject.setSavepointAllowed(isNestedTransactionAllowed());
    		ConnectionHolder conHolder =
    		    (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
    		txObject.setConnectionHolder(conHolder, false);
    		return txObject;
    	}
    

      

    1、创建DataSourceTransactionObject实例
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    进入DataSourceTransactionObject

    DataSourceTransactionObject也是继承自JdbcTransactionObjectSupport

    三、回到AbstractPlatformTransactionManager类的getTransaction方法
    进入如下代码

    如果已经存在事务,则检查事务的传播行为

    2、进入handleExistingTransaction方法

    /**
    	 * Create a TransactionStatus for an existing transaction.
    	 */
    	private TransactionStatus handleExistingTransaction(
    			TransactionDefinition definition, Object transaction, boolean debugEnabled)
    			throws TransactionException {
    
    		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
    			throw new IllegalTransactionStateException(
    					"Existing transaction found for transaction marked with propagation 'never'");
    		}
    
    		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
    			if (debugEnabled) {
    				logger.debug("Suspending current transaction");
    			}
    			Object suspendedResources = suspend(transaction);
    			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
    			return prepareTransactionStatus(
    					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
    		}
    
    		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
    			if (debugEnabled) {
    				logger.debug("Suspending current transaction, creating new transaction with name [" +
    						definition.getName() + "]");
    			}
    			SuspendedResourcesHolder suspendedResources = suspend(transaction);
    			try {
    				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    				DefaultTransactionStatus status = newTransactionStatus(
    						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
    				doBegin(transaction, definition);
    				prepareSynchronization(status, definition);
    				return status;
    			}
    			catch (RuntimeException beginEx) {
    				resumeAfterBeginException(transaction, suspendedResources, beginEx);
    				throw beginEx;
    			}
    			catch (Error beginErr) {
    				resumeAfterBeginException(transaction, suspendedResources, beginErr);
    				throw beginErr;
    			}
    		}
    
    		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    			if (!isNestedTransactionAllowed()) {
    				throw new NestedTransactionNotSupportedException(
    						"Transaction manager does not allow nested transactions by default - " +
    						"specify 'nestedTransactionAllowed' property with value 'true'");
    			}
    			if (debugEnabled) {
    				logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
    			}
    			if (useSavepointForNestedTransaction()) {
    				// Create savepoint within existing Spring-managed transaction,
    				// through the SavepointManager API implemented by TransactionStatus.
    				// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
    				DefaultTransactionStatus status =
    						prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
    				status.createAndHoldSavepoint();
    				return status;
    			}
    			else {
    				// Nested transaction through nested begin and commit/rollback calls.
    				// Usually only for JTA: Spring synchronization might get activated here
    				// in case of a pre-existing JTA transaction.
    				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    				DefaultTransactionStatus status = newTransactionStatus(
    						definition, transaction, true, newSynchronization, debugEnabled, null);
    				doBegin(transaction, definition);
    				prepareSynchronization(status, definition);
    				return status;
    			}
    		}
    
    		// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
    		if (debugEnabled) {
    			logger.debug("Participating in existing transaction");
    		}
    		if (isValidateExistingTransaction()) {
    			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
    				Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
    				if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
    					Constants isoConstants = DefaultTransactionDefinition.constants;
    					throw new IllegalTransactionStateException("Participating transaction with definition [" +
    							definition + "] specifies isolation level which is incompatible with existing transaction: " +
    							(currentIsolationLevel != null ?
    									isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
    									"(unknown)"));
    				}
    			}
    			if (!definition.isReadOnly()) {
    				if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
    					throw new IllegalTransactionStateException("Participating transaction with definition [" +
    							definition + "] is not marked as read-only but existing transaction is");
    				}
    			}
    		}
    		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    		return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
    	}
    

      其中的doBegin(transaction, definition);方法,是开启事务

    进入最后一行的prepareTransactionStatus方法

    3、返回AbstractPlatformTransactionManager类的getTransaction方法,进入如下代码

    4、进入Hibernate的doBegin开启事务方法实现
    protected void doBegin(Object transaction, TransactionDefinition definition) {
    		HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
    
    		if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
    			throw new IllegalTransactionStateException(
    					"Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
    					"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
    					"It is recommended to use a single HibernateTransactionManager for all transactions " +
    					"on a single DataSource, no matter whether Hibernate or JDBC access.");
    		}
    
    		Session session = null;
    
    		try {
    			if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
    				Interceptor entityInterceptor = getEntityInterceptor();
    				Session newSession = (entityInterceptor != null ?
    						getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());
    				if (logger.isDebugEnabled()) {
    					logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) +
    							"] for Hibernate transaction");
    				}
    				txObject.setSession(newSession);
    			}
    
    			session = txObject.getSessionHolder().getSession();
    
    			if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
    				// We're allowed to change the transaction settings of the JDBC Connection.
    				if (logger.isDebugEnabled()) {
    					logger.debug(
    							"Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
    				}
    				Connection con = session.connection();
    				Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
    				txObject.setPreviousIsolationLevel(previousIsolationLevel);
    			}
    			else {
    				// Not allowed to change the transaction settings of the JDBC Connection.
    				if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
    					// We should set a specific isolation level but are not allowed to...
    					throw new InvalidIsolationLevelException(
    							"HibernateTransactionManager is not allowed to support custom isolation levels: " +
    							"make sure that its 'prepareConnection' flag is on (the default) and that the " +
    							"Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default). " +
    							"Make sure that your LocalSessionFactoryBean actually uses SpringTransactionFactory: Your " +
    							"Hibernate properties should *not* include a 'hibernate.transaction.factory_class' property!");
    				}
    				if (logger.isDebugEnabled()) {
    					logger.debug(
    							"Not preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
    				}
    			}
    
    			if (definition.isReadOnly() && txObject.isNewSession()) {
    				// Just set to NEVER in case of a new Session for this transaction.
    				session.setFlushMode(FlushMode.MANUAL);
    			}
    
    			if (!definition.isReadOnly() && !txObject.isNewSession()) {
    				// We need AUTO or COMMIT for a non-read-only transaction.
    				FlushMode flushMode = session.getFlushMode();
    				if (flushMode.lessThan(FlushMode.COMMIT)) {
    					session.setFlushMode(FlushMode.AUTO);
    					txObject.getSessionHolder().setPreviousFlushMode(flushMode);
    				}
    			}
    
    			Transaction hibTx;
    
    			// Register transaction timeout.
    			int timeout = determineTimeout(definition);
    			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
    				// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
    				// Applies to all statements, also to inserts, updates and deletes!
    				hibTx = session.getTransaction();
    				hibTx.setTimeout(timeout);
    				hibTx.begin();
    			}
    			else {
    				// Open a plain Hibernate transaction without specified timeout.
    				hibTx = session.beginTransaction();
    			}
    
    			// Add the Hibernate transaction to the session holder.
    			txObject.getSessionHolder().setTransaction(hibTx);
    
    			// Register the Hibernate Session's JDBC Connection for the DataSource, if set.
    			if (getDataSource() != null) {
    				Connection con = session.connection();
    				ConnectionHolder conHolder = new ConnectionHolder(con);
    				if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
    					conHolder.setTimeoutInSeconds(timeout);
    				}
    				if (logger.isDebugEnabled()) {
    					logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");
    				}
    				TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
    				txObject.setConnectionHolder(conHolder);
    			}
    
    			// Bind the session holder to the thread.
    			if (txObject.isNewSessionHolder()) {
    				TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
    			}
    			txObject.getSessionHolder().setSynchronizedWithTransaction(true);
    		}
    
    		catch (Exception ex) {
    			if (txObject.isNewSession()) {
    				try {
    					if (session.getTransaction().isActive()) {
    						session.getTransaction().rollback();
    					}
    				}
    				catch (Throwable ex2) {
    					logger.debug("Could not rollback Session after failed transaction begin", ex);
    				}
    				finally {
    					SessionFactoryUtils.closeSession(session);
    				}
    			}
    			throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
    		}
    	}
    

      关键代码


    5、进入JDBC的doBegin方法实现方式(DataSourceTransactionManager类中)
    @Override
    	protected void doBegin(Object transaction, TransactionDefinition definition) {
    		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    		Connection con = null;
    
    		try {
    			if (txObject.getConnectionHolder() == null ||
    					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
    				Connection newCon = this.dataSource.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).
    			if (con.getAutoCommit()) {
    				txObject.setMustRestoreAutoCommit(true);
    				if (logger.isDebugEnabled()) {
    					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
    				}
    				con.setAutoCommit(false);
    			}
    			txObject.getConnectionHolder().setTransactionActive(true);
    
    			int timeout = determineTimeout(definition);
    			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
    				txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
    			}
    
    			// Bind the session holder to the thread.
    			if (txObject.isNewConnectionHolder()) {
    				TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
    			}
    		}
    
    		catch (Exception ex) {
    			DataSourceUtils.releaseConnection(con, this.dataSource);
    			throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
    		}
    	}
    

      

    开启事务: con.setAutoCommit(false);,关闭自动提交,换句话说就是手工提交,开启了事务。

    6、返回TransactionAspectSupport类createTransactionIfNecessary方法

    进入prepareTransactionInfo方法

    7、定位到txInfo.bindToThread();

    进入bindToThread方法

    公开当前的TransactionStatus,当前事务完成之后,恢复之前的事务。 我们可以理解成,如果存在嵌套事务,将外层事务保存在oldTransactionInfo里面,里面的事务执行完成后再获取外层的。实际上就是将外层的事务挂起。

    8、回到TransactionInterceptor类的invoke方法

    commitTransactionAfterReturning 最终提交事务。

    进入commitTransactionAfterReturning 方法

     进入txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); 方法的实现

    	/**
    	 * This implementation of commit handles participating in existing
    	 * transactions and programmatic rollback requests.
    	 * Delegates to <code>isRollbackOnly</code>, <code>doCommit</code>
    	 * and <code>rollback</code>.
    	 * @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
    	 * @see #doCommit
    	 * @see #rollback
    	 */
    	public final void commit(TransactionStatus status) throws TransactionException {
    		if (status.isCompleted()) {
    			throw new IllegalTransactionStateException(
    					"Transaction is already completed - do not call commit or rollback more than once per transaction");
    		}
    
    		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    		if (defStatus.isLocalRollbackOnly()) {
    			if (defStatus.isDebug()) {
    				logger.debug("Transactional code has requested rollback");
    			}
    			processRollback(defStatus);
    			return;
    		}
    		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
    			if (defStatus.isDebug()) {
    				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
    			}
    			processRollback(defStatus);
    			// Throw UnexpectedRollbackException only at outermost transaction boundary
    			// or if explicitly asked to.
    			if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
    				throw new UnexpectedRollbackException(
    						"Transaction rolled back because it has been marked as rollback-only");
    			}
    			return;
    		}
    
    		processCommit(defStatus);
    	}
    

      

    进入最后一行processCommit(defStatus);

    	/**
    	 * Process an actual commit.
    	 * Rollback-only flags have already been checked and applied.
    	 * @param status object representing the transaction
    	 * @throws TransactionException in case of commit failure
    	 */
    	private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    		try {
    			boolean beforeCompletionInvoked = false;
    			try {
    				prepareForCommit(status);
    				triggerBeforeCommit(status);
    				triggerBeforeCompletion(status);
    				beforeCompletionInvoked = true;
    				boolean globalRollbackOnly = false;
    				if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
    					globalRollbackOnly = status.isGlobalRollbackOnly();
    				}
    				if (status.hasSavepoint()) {
    					if (status.isDebug()) {
    						logger.debug("Releasing transaction savepoint");
    					}
    					status.releaseHeldSavepoint();
    				}
    				else if (status.isNewTransaction()) {
    					if (status.isDebug()) {
    						logger.debug("Initiating transaction commit");
    					}
    					doCommit(status);
    				}
    				// Throw UnexpectedRollbackException if we have a global rollback-only
    				// marker but still didn't get a corresponding exception from commit.
    				if (globalRollbackOnly) {
    					throw new UnexpectedRollbackException(
    							"Transaction silently rolled back because it has been marked as rollback-only");
    				}
    			}
    			catch (UnexpectedRollbackException ex) {
    				// can only be caused by doCommit
    				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    				throw ex;
    			}
    			catch (TransactionException ex) {
    				// can only be caused by doCommit
    				if (isRollbackOnCommitFailure()) {
    					doRollbackOnCommitException(status, ex);
    				}
    				else {
    					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
    				}
    				throw ex;
    			}
    			catch (RuntimeException ex) {
    				if (!beforeCompletionInvoked) {
    					triggerBeforeCompletion(status);
    				}
    				doRollbackOnCommitException(status, ex);
    				throw ex;
    			}
    			catch (Error err) {
    				if (!beforeCompletionInvoked) {
    					triggerBeforeCompletion(status);
    				}
    				doRollbackOnCommitException(status, err);
    				throw err;
    			}
    
    			// Trigger afterCommit callbacks, with an exception thrown there
    			// propagated to callers but the transaction still considered as committed.
    			try {
    				triggerAfterCommit(status);
    			}
    			finally {
    				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
    			}
    
    		}
    		finally {
    			cleanupAfterCompletion(status);
    		}
    	}
    

      真正提交事务的方法

       doCommit是一个抽象类,由具体的子类实现

    9、进入Hibernate的doCommit实现

    10.进入JDBC实现的doCommit实现

    11、回到TransactionInterceptor类的invoke方法,定位到抛出异常代码completeTransactionAfterThrowing(txInfo, ex);

    进入completeTransactionAfterThrowing(txInfo, ex);方法

    /**
    	 * Handle a throwable, completing the transaction.
    	 * We may commit or roll back, depending on the configuration.
    	 * @param txInfo information about the current transaction
    	 * @param ex throwable encountered
    	 */
    	protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
    		if (txInfo != null && txInfo.hasTransaction()) {
    			if (logger.isTraceEnabled()) {
    				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
    						"] after exception: " + ex);
    			}
    			if (txInfo.transactionAttribute.rollbackOn(ex)) {
    				try {
    					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
    				}
    				catch (TransactionSystemException ex2) {
    					logger.error("Application exception overridden by rollback exception", ex);
    					ex2.initApplicationException(ex);
    					throw ex2;
    				}
    				catch (RuntimeException ex2) {
    					logger.error("Application exception overridden by rollback exception", ex);
    					throw ex2;
    				}
    				catch (Error err) {
    					logger.error("Application exception overridden by rollback error", ex);
    					throw err;
    				}
    			}
    			else {
    				// We don't roll back on this exception.
    				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
    				try {
    					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    				}
    				catch (TransactionSystemException ex2) {
    					logger.error("Application exception overridden by commit exception", ex);
    					ex2.initApplicationException(ex);
    					throw ex2;
    				}
    				catch (RuntimeException ex2) {
    					logger.error("Application exception overridden by commit exception", ex);
    					throw ex2;
    				}
    				catch (Error err) {
    					logger.error("Application exception overridden by commit error", ex);
    					throw err;
    				}
    			}
    		}
    	}
    

      根据当前事务的配置,来确定是提交还是回滚。

    12、回到TransactionInterceptor类的invoke方法,定位到cleanupTransactionInfo(txInfo);方法

    进入cleanupTransactionInfo方法

    重置TransactionInfo ThreadLocal

    	/**
    	 * Reset the TransactionInfo ThreadLocal.
    	 * <p>Call this in all cases: exception or normal return!
    	 * @param txInfo information about the current transaction (may be <code>null</code>)
    	 */
    	protected void cleanupTransactionInfo(TransactionInfo txInfo) {
    		if (txInfo != null) {
    			txInfo.restoreThreadLocalStatus();
    		}
    	}
    

      

    进入restoreThreadLocalStatus方法

    相当于里层的事务执行完了,恢复外层的事务

    transactionInfoHolder是一个ThreadLocal类型的变量。

    总结,Spring事务用到的几个类

    TransactionDefinition 事务的定义,下面有一个重要的子接口TransactionAttribute,主要描述事务的配置信息

    TransactionInfo 是一个内部类,里面包含了TransactionStatus和自身TransactionInfo (用于嵌套事务,供恢复使用)

    TransactionStatus: 当前事务运行期的一些状态。

    PlatformTransactionManager: 平台事务管理器

      AbstractPlatformTransactionManager 抽象平台事务管理器的实现类,里面用到了大量的模板方法(模板方法设计模式)

      AbstractPlatformTransactionManager  抽象平台事务管理器下面,就是各个平台相关的事务管理器。如下图:

      

  • 相关阅读:
    flex 连接mysql
    正确配置调试world wind on vs2008
    FLex调用servlet连接数据库
    c# 连接mysql并webservice数据
    ADF连接SOM
    转载加收藏关于OPENGL配置VS2008
    flex不能显示本地发布的地图
    Symbian专区
    asp.net控件开发基础學習
    控制网页大小
  • 原文地址:https://www.cnblogs.com/linlf03/p/11221498.html
Copyright © 2011-2022 走看看