spring 声明式事务原理解读
(上述内容参考《Spring In Action》第三版)。
下面是一个具体配置的例子:
<tx:advice id="txAdviceHibernate" transaction-manager="txManagerHibernate"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception" no-rollback-for="" /> <tx:method name="batch*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="execute*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="export*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="import*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="count*" propagation="NOT_SUPPORTED" read-only="true" /> <tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true" /> <tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true" /> <tx:method name="list*" propagation="NOT_SUPPORTED" read-only="true" /> <tx:method name="load*" propagation="NOT_SUPPORTED" read-only="true" /> <tx:method name="page*" propagation="NOT_SUPPORTED" read-only="true" /> <tx:method name="query*" propagation="NOT_SUPPORTED" read-only="true" /> </tx:attributes> </tx:advice> <!--此处应想办法实现动态加载--> <aop:config> <aop:advisor pointcut="execution(* com.cetc.datamc.app.collect.bs.*.*(..)) or execution(* com.cetc.datamc.app.mdms.bs.*.*(..))" advice-ref="txAdviceHibernate" /> </aop:config>
其中<tx:advice>是用来声明事务性策略的。对于<tx:advice>来说,事务属性定义在<tx:attributes>元素中,该元素包含了一个或多个的<tx:method>元素。<tx:method>元素为某个(或某些)name属性(使用通配符)指定的方法定义事务的参数。
当使用<tx:advice>来声明事务时,你还需要一个事务管理器,就像使用TransactionProxyFactoryBean那样。根据约定优于配置,<tx:advice>假定事务管理器被声明为一个id为transactionManager的Bean。如果碰巧为事务管理器配置了一个不同的id(如txManager),则需要在transaction-manager属性中明确指定事务管理器的id:
<tx:advice id="txAdviceHibernate" transaction-manager="txManager"> ... </tx:advice>
public interface IDataCollect { public String addDataItem(String data); } public class DataCollectService implements IDataCollect { private IMetadataService metaService; public void setMetaService(IMetadataService metaService) { this.metaService = metaService; } public String addDataItem(String data) { metaService.deleteMeta(data); return metaService.addMeta(data); } } public interface IMetadataService { public void deleteMeta(String data); public String addMeta(String data); public String queryMetaId(String data); } public class MetadataService implements IMetadataService { public void deleteMeta(String data) { System.out.println("delete metadata"); } public String addMeta(String data) { System.out.println("add metadata"); String id = queryMetaId(data); return data; } public String queryMetaId(String data) { return "id"; } }
这样,在DataCollectService中有一个IMetadataService类型的对象。那么在实际中该类型则是被Spring替换为动态代理对象,而不是原来的MetaService对象。所以调用了IMetadataService中的deleteMeta和addMeta方法,这两个方法都是调用的织入事务切面代码的函数。而不是原来的deleteMeta和addMeta方法。
另外在MetaService还可以看到addMeta中调用了queryMetaId方法。这个方法调用的就是没有织入切面代码的原来的函数。因为这个不是使用接口调用的,而是直接调用的对象本身的方法。所以就是该对象原来的queryMetaId方法。这就是为什么需要事务管理的类必须使用interface
事务管理的切面属于<aop:around>(AOP环绕通知)类型的切面。
org.springframework.transaction.interceptor.TransactionAspectSupport类
/** * General delegate for around-advice-based subclasses, delegating to several other template * methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager} * as well as regular {@link PlatformTransactionManager} implementations. * @param method the Method being invoked * @param targetClass the target class that we're invoking the method on * @param invocation the callback to use for proceeding with the target invocation * @return the return value of the method, if any * @throws Throwable propagated from the target invocation */ protected Object invokeWithinTransaction(Method method, Class targetClass, final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional. //这里返回的即是在tx:method中配置的属性值 //包括propagation, isolation, readonly, timeout, readonly等属性的配置值。 //如果一个方法没有在tx:method中配置,那么txAttr为null final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass); final PlatformTransactionManager tm = determineTransactionManager(txAttr); final String joinpointIdentification = methodIdentification(method, targetClass); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. //该方法主要就是根据配置来采取合适的事务策略。 //理解了该方法的调用原理,基本上spring的配置事务的各属性的涵义也就理解了 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } else { // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, new TransactionCallback<Object>() { public Object doInTransaction(TransactionStatus status) { TransactionInfo txInfo = prepareTransactionInfo( tm, txAttr, joinpointIdentification, status); try { return invocation.proceedWithInvocation(); } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { throw new ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. return new ThrowableHolder(ex); } } finally { cleanupTransactionInfo(txInfo); } } }); // Check result: It might indicate a Throwable to rethrow. if (result instanceof ThrowableHolder) { throw ((ThrowableHolder) result).getThrowable(); } else { return result; } } catch (ThrowableHolderException ex) { throw ex.getCause(); } } }
/** * Create a transaction if necessary based on the given TransactionAttribute. * <p>Allows callers to perform custom TransactionAttribute lookups through * the TransactionAttributeSource. * @param txAttr the TransactionAttribute (may be {@code null}) * @param joinpointIdentification the fully qualified method name * (used for monitoring and logging purposes) * @return a TransactionInfo object, whether or not a transaction was created. * The {@code hasTransaction()} method on TransactionInfo can be used to * tell if there was a transaction created. * @see #getTransactionAttributeSource() */ @SuppressWarnings("serial") protected TransactionInfo createTransactionIfNecessary( PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) { // If no name specified, apply method identification as transaction name. if (txAttr != null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { @Override public String getName() { return joinpointIdentification; } }; } TransactionStatus status = null; if (txAttr != null) { if (tm != null) { //重点看此函数 status = tm.getTransaction(txAttr); } else { if (logger.isDebugEnabled()) { logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured"); } } } //如果没有在tx:method中配置,那么txAttr=null, 这样返回的TransactionStatus也为null return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); }
org.springframework.transaction.support.AbstractPlatformTransactionManager类
/** * This implementation handles propagation behavior. Delegates to * {@code doGetTransaction}, {@code isExistingTransaction} * and {@code doBegin}. * @see #doGetTransaction * @see #isExistingTransaction * @see #doBegin */ public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { //doGetTransaction在各继承该类的子类中实现。 //这里假设使用的是Hibernate的事务,那么实现类是HibernateTransactionManager类 //这里的object即表示我们通常所说的Transaction,即事务的类的定义 //对于Hibernate Transaction的具体类是HibernateTransactionObject Object transaction = doGetTransaction(); // Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); if (definition == null) { // Use defaults if no transaction definition given. definition = new DefaultTransactionDefinition(); } //根据得到的transaction对象判断是否当前已经存在事务 //如果存在事务,那么调用handleExistingTransaction来根据配置的策略处理事务 if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } // Check definition settings for new transaction. if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } //如果当前不存在事务,那么根据不同的策略配置来产生具体的事务行为。 //这里即是各种不同配置策略行为的具体代码实现过程 // No existing transaction found -> check propagation behavior to find out how to proceed. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); } 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 ex) { resume(null, suspendedResources); throw ex; } catch (Error err) { resume(null, suspendedResources); throw err; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } }
下面我们暂时先抛开如何doGetTransaction以及如何判断isExistingTransaction不谈,先来看看如何doBegin一个新事务。
在org.springframework.orm.hibernate3.HibernateTransactionManager类中的doBegin实现如下:
@Override 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 { //如果当前txObject没有已有的Session,那么打开一个新的Session. //txObject也可能已经有了Session, //例如如果配置了OpenSessionInViewFilter,且当前的线程是一个request线程, //那么就会在每个request线程中就先openSession,并且绑定到当前线程中。 //这样在doGetTransaction就会获取到该Session。详见doGetTransaction函数和OpenSessionInViewFilter实现 if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) { Interceptor entityInterceptor = getEntityInterceptor(); //这里是openSession,而不是getCurrentSession Session newSession = (entityInterceptor != null ? getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession()); if (logger.isDebugEnabled()) { logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) + "] for Hibernate transaction"); } //设置newSessionHolder和newSession状态为true 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) + "]"); } } //如果是一个new Session并且事务是只读的,则设置该Session flushMode为FlushMode.MANUAL if (definition.isReadOnly() && txObject.isNewSession()) { // Just set to MANUAL in case of a new Session for this transaction. session.setFlushMode(FlushMode.MANUAL); } //如果事务不是只读的,且已有一个Session时,重新设置该Session的flushMode 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; //设置timeout 然后beginTransaction // 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 + "]"); } //将当前的conHolder绑定到线程上,可以用来判断当前线程是否已有事务 TransactionSynchronizationManager.bindResource(getDataSource(), conHolder); txObject.setConnectionHolder(conHolder); } // Bind the session holder to the thread. if (txObject.isNewSessionHolder()) { //将当前的SessionHolder绑定到当前线程上,可以用来判断当前线程是否已有事务 TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder()); } txObject.getSessionHolder().setSynchronizedWithTransaction(true); } catch (Throwable 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); txObject.setSessionHolder(null); } } throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex); } }
好了,知道了doBegin函数的实现原理,让我们回过头来看doGetTransaction和isExistingTransaction函数的实现。这样就很容易理解了。
@Override protected Object doGetTransaction() { HibernateTransactionObject txObject = new HibernateTransactionObject(); txObject.setSavepointAllowed(isNestedTransactionAllowed()); //获取当前线程中是否已经有已绑定的SessionHolder。如果当前线程已经存在事务,那么就会存在SessionHolder //具体可见doBegin函数的实现 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"); } //如果已经有了SessionHolder,那么直接就把当前已有的SessionHolder赋给新的transaction Object txObject.setSessionHolder(sessionHolder); } else if (this.hibernateManagedSession) { try { //当前线程中已经绑定了session,例如openSessionInViewFilter的情况 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); } } //如果当前线程已经绑定了ConnectionHolder,如果当前线程中已经开启了事务,就会存在ConnectionHolder //直接将已有的ConnectionHolder赋值给该transaction if (getDataSource() != null) { ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(getDataSource()); txObject.setConnectionHolder(conHolder); } return txObject; }
@Override protected boolean isExistingTransaction(Object transaction) { HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; //spring管理的事务或者是Hibernate自己管理的事务 return (txObject.hasSpringManagedTransaction() || (this.hibernateManagedSession && txObject.hasHibernateManagedTransaction())); }
isExistingTransaction函数里面的的hasSpringManagedTransaction的实现如下:
public boolean hasSpringManagedTransaction() { //如果当前的transaction Object的sessionHolder不为null,说明当前存在事务 return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null); }
这两个函数都很简单,结合doGetTransaction函数的实现原理,就很容易理解了。
好了,最后再来看一下AbstractPlatformTransactionManager类中的AbstractPlatformTransactionManager函数的实现原理。这个函数也是主要根据不同的配置策略产生不同的事务行为。理解了该函数,也能对事务不同策略的配置理解的更加透彻了。
/** * 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; } } //配置的策略为如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。 //嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在, //那么其行为与PROPAGATION_REQUIRED一样 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); }
获取更多学习资料,可以加群:473984645或扫描下方二维码
spring 声明式事务原理解读的更多相关文章
- spring声明式事务管理总结
事务配置 首先在/WEB-INF/applicationContext.xml添加以下内容: <!-- 配置事务管理器 --> <bean id="transactionM ...
- Spring声明式事务管理基于@Transactional注解
概述:我们已知道Spring声明式事务管理有两种常用的方式,一种是基于tx/aop命名空间的xml配置文件,另一种则是基于@Transactional 注解. 第一种方式我已在上文为大 ...
- Spring声明式事务配置管理方法
环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add ...
- Spring声明式事务配置管理方法(转)
项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add libra ...
- Spring声明式事务的配置~~~
/*2011年8月28日 10:03:30 by Rush */ 环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加 ...
- 深刻理解Spring声明式事务
问题引入 Spring中事务传播有哪几种,分别是怎样的? 理解注解事务的自动配置? SpringBoot启动类为什么不需要加@EnableTransactionManagement注解? 声明式事务的 ...
- spring 声明式事务管理
简单理解事务: 比如你去ATM机取5000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉5000元钱:然后ATM出5000元钱.这两个步骤必须是要么都执行要么都不执行.如果银行卡扣除了5000块但 ...
- Spring声明式事务管理基于tx/aop命名空间
目的:通过Spring AOP 实现Spring声明式事务管理; Spring支持编程式事务管理和声明式事务管理两种方式. 而声明式事务管理也有两种常用的方式,一种是基于tx/aop命名空间的xml配 ...
- 161117、使用spring声明式事务抛出 identifier of an instance of
今天项目组有成员使用spring声明式事务出现下面异常,这里跟大家分享学习下. 异常信息: org.springframework.orm.hibernate3.HibernateSystemExce ...
随机推荐
- Python Numpy 矩阵级基本操作(1)
NumPy的操作介绍 import numpy as np #导入numpy包,简写为np print "Generate 1*10 matrix" a=np.arange(1,1 ...
- Django框架(二十)—— Django rest_framework-认证组件
目录 Django rest_framework-认证组件 一.什么是认证 二.利用token记录认证过的用户 1.什么是token 2.token的原理 3.cookie.session.token ...
- linux中常用的命令大全
系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...
- Spring Boot 2.1.6 发布了!
Java技术栈 www.javastack.cn 优秀的Java技术公众号 最新消息: Spring Boot 2.1.6 昨天正式发布了,日常更新一些依赖和修复一些 BUG,没什么硬菜! 重点来了, ...
- Python面试题之“猴子补丁”(monkey patching)指的是什么?这种做法好吗?
“猴子补丁”就是指,在函数或对象已经定义之后,再去改变它们的行为. 举个例子: import datetime datetime.datetime.now = lambda: datetime.dat ...
- CTU OPEN 2017 Shooting Gallery /// 区间DP
题目大意: 给定n 给定n个数 选定一个区间留下其他消去 要求区间两端的两个数一样 若成功留下一个区间 则在选定区间的基础上 继续进行上述操作 直到无法再选出这样的区间 求最多操作数 按区间长度由短到 ...
- Spring学习笔记(1)——初识Spring
一.Spring是什么 通常说的Spring其实指的是Spring Framework,它是Spring下的一个子项目,Spring围绕Spring Framework这个核心项目开发了大 ...
- Chrome不支持css字体小于12px的解决办法
我们先来看个效果图(chrome下): 从上面的图可以很明显地看出Chrome下css设置字体大小为12px及以下时,显示的都是一样大小,都是默认12px: 那么网上有一个方法就是给当前样式添加Chr ...
- 出现异常: 非介入式客户端验证规则中的验证类型名称必须唯一。下列验证类型出现重复: required
在将web.config文件中的<add key="ClientValidationEnabled" value="false" /> 设为fals ...
- 在MyEclipse中更换或修改svn的用户名和密码
1.通过删除SVN客户端的账号配置文件 (1)找到我们使用的客户端配置文件,Windows XP中的位置是在系统盘的Documents and Settings\Administrator\A ...