@Transactional supports qualifier value for choosing between multiple transaction managers

This commit is contained in:
Juergen Hoeller 2009-05-08 23:13:43 +00:00
parent dc83107d66
commit d34c4a2cf0
13 changed files with 208 additions and 75 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,12 +18,11 @@ package org.springframework.transaction.annotation;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import javax.ejb.ApplicationException;
import javax.ejb.TransactionAttributeType;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.support.DefaultTransactionDefinition;
/**
* Strategy implementation for parsing EJB3's {@link javax.ejb.TransactionAttribute}
@ -53,8 +52,7 @@ public class Ejb3TransactionAnnotationParser implements TransactionAnnotationPar
* EJB3-specific TransactionAttribute, implementing EJB3's rollback rules
* which are based on annotated exceptions.
*/
private static class Ejb3TransactionAttribute extends DefaultTransactionDefinition
implements TransactionAttribute {
private static class Ejb3TransactionAttribute extends DefaultTransactionAttribute {
public Ejb3TransactionAttribute(TransactionAttributeType type) {
setPropagationBehaviorName(PREFIX_PROPAGATION + type.name());
@ -62,7 +60,7 @@ public class Ejb3TransactionAnnotationParser implements TransactionAnnotationPar
public boolean rollbackOn(Throwable ex) {
ApplicationException ann = ex.getClass().getAnnotation(ApplicationException.class);
return (ann != null ? ann.rollback() : (ex instanceof RuntimeException || ex instanceof Error));
return (ann != null ? ann.rollback() : super.rollbackOn(ex));
}
}

View File

@ -17,8 +17,8 @@
package org.springframework.transaction.annotation;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
@ -58,6 +58,7 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
rbta.setIsolationLevel(ann.isolation().value());
rbta.setTimeout(ann.timeout());
rbta.setReadOnly(ann.readOnly());
rbta.setQualifier(ann.value());
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
Class[] rbf = ann.rollbackFor();
for (Class rbRule : rbf) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2006 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -49,6 +49,15 @@ import org.springframework.transaction.TransactionDefinition;
@Documented
public @interface Transactional {
/**
* A qualifier value for the specified transaction.
* <p>May be used to determine the target transaction manager,
* matching the qualifier value (or the bean name) of a specific
* {@link org.springframework.transaction.PlatformTransactionManager}
* bean definition.
*/
String value() default "";
/**
* The transaction propagation type.
* <p>Defaults to {@link Propagation#REQUIRED}.

View File

@ -95,8 +95,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
private static void registerTransactionManager(Element element, BeanDefinition def) {
String transactionManagerName = (element.hasAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE) ?
element.getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
def.getPropertyValues().addPropertyValue(
TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY, new RuntimeBeanReference(transactionManagerName));
def.getPropertyValues().addPropertyValue("transactionManagerBeanName", transactionManagerName);
}

View File

@ -27,6 +27,9 @@ import org.springframework.transaction.support.DefaultTransactionDefinition;
*/
public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {
private String qualifier;
/**
* Create a new DefaultTransactionAttribute, with default settings.
* Can be modified through bean property setters.
@ -66,10 +69,18 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
}
public void setQualifier(String qualifier) {
this.qualifier = qualifier;
}
public String getQualifier() {
return this.qualifier;
}
/**
* Default behavior is as with EJB: rollback on unchecked exception.
* The default behavior is as with EJB: rollback on unchecked exception.
* Additionally attempt to rollback on Error.
* Consistent with TransactionTemplate's behavior.
* <p>This is consistent with TransactionTemplate's default behavior.
*/
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);

View File

@ -64,6 +64,10 @@ public abstract class DelegatingTransactionAttribute implements TransactionAttri
return this.targetAttribute.getName();
}
public String getQualifier() {
return this.targetAttribute.getQualifier();
}
public boolean rollbackOn(Throwable ex) {
return this.targetAttribute.rollbackOn(ex);
}

View File

@ -17,18 +17,30 @@
package org.springframework.transaction.interceptor;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.core.NamedThreadLocal;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Base class for transactional aspects, such as the AOP Alliance
@ -61,7 +73,7 @@ import org.springframework.util.ClassUtils;
* @see #setTransactionAttributes
* @see #setTransactionAttributeSource
*/
public abstract class TransactionAspectSupport implements InitializingBean {
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
// NOTE: This class must not implement Serializable because it serves as base
// class for AspectJ aspects (which are not allowed to implement Serializable)!
@ -94,8 +106,9 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive()
*/
protected static TransactionInfo currentTransactionInfo() throws NoTransactionException {
return (TransactionInfo) transactionInfoHolder.get();
return transactionInfoHolder.get();
}
/**
* Return the transaction status of the current method invocation.
* Mainly intended for code that wants to set the current transaction
@ -114,23 +127,31 @@ public abstract class TransactionAspectSupport implements InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
/** Delegate used to create, commit and rollback transactions */
private String transactionManagerBeanName;
private PlatformTransactionManager transactionManager;
/** Helper used to find transaction attributes */
private TransactionAttributeSource transactionAttributeSource;
private BeanFactory beanFactory;
/**
* Set the transaction manager. This will perform actual
* transaction management: This class is just a way of invoking it.
* Specify the name of the default transaction manager bean.
*/
public void setTransactionManagerBeanName(String transactionManagerBeanName) {
this.transactionManagerBeanName = transactionManagerBeanName;
}
/**
* Specify the target transaction manager.
*/
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Return the transaction manager.
* Return the transaction manager, if specified.
*/
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
@ -160,7 +181,6 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @see CompositeTransactionAttributeSource
* @see MethodMapTransactionAttributeSource
* @see NameMatchTransactionAttributeSource
* @see AttributesTransactionAttributeSource
* @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
*/
public void setTransactionAttributeSources(TransactionAttributeSource[] transactionAttributeSources) {
@ -174,7 +194,6 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @see TransactionAttributeSourceEditor
* @see MethodMapTransactionAttributeSource
* @see NameMatchTransactionAttributeSource
* @see AttributesTransactionAttributeSource
* @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
*/
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
@ -188,22 +207,79 @@ public abstract class TransactionAspectSupport implements InitializingBean {
return this.transactionAttributeSource;
}
/**
* Set the BeanFactory to use for retrieving PlatformTransactionManager beans.
*/
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Check that required properties were set.
*/
public void afterPropertiesSet() {
if (getTransactionManager() == null) {
throw new IllegalArgumentException("Property 'transactionManager' is required");
if (this.transactionManager == null && this.beanFactory == null) {
throw new IllegalStateException(
"Setting the property 'transactionManager' or running in a ListableBeanFactory is required");
}
if (getTransactionAttributeSource() == null) {
throw new IllegalArgumentException(
if (this.transactionAttributeSource == null) {
throw new IllegalStateException(
"Either 'transactionAttributeSource' or 'transactionAttributes' is required: " +
"If there are no transactional methods, then don't use a transaction aspect.");
}
}
/**
* Determine the specific transaction manager to use for the given transaction.
*/
protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {
if (this.transactionManager != null) {
return this.transactionManager;
}
PlatformTransactionManager chosen = null;
String qualifier = txAttr.getQualifier();
if (StringUtils.hasLength(qualifier)) {
if (!(this.beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalStateException("BeanFactory required to be a ConfigurableListableBeanFactory " +
"for resolution of qualifier '" + qualifier + "': " + this.beanFactory.getClass());
}
ConfigurableListableBeanFactory bf = (ConfigurableListableBeanFactory) this.beanFactory;
Map<String, PlatformTransactionManager> tms =
BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, PlatformTransactionManager.class);
for (String beanName : tms.keySet()) {
if (bf.containsBeanDefinition(beanName)) {
BeanDefinition bd = bf.getBeanDefinition(beanName);
if (bd instanceof AbstractBeanDefinition) {
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
if (chosen != null) {
throw new IllegalStateException("No unique PlatformTransactionManager bean found " +
"for qualifier '" + qualifier + "'");
}
chosen = tms.get(beanName);
}
}
}
}
}
if (chosen != null) {
return chosen;
}
else if (this.transactionManagerBeanName != null) {
return this.beanFactory.getBean(this.transactionManagerBeanName, PlatformTransactionManager.class);
}
else if (this.beanFactory instanceof ListableBeanFactory) {
return BeanFactoryUtils.beanOfTypeIncludingAncestors(((ListableBeanFactory) this.beanFactory), PlatformTransactionManager.class);
}
else {
throw new IllegalStateException(
"Cannot retrieve PlatformTransactionManager beans from non-listable BeanFactory: " + this.beanFactory);
}
}
/**
* Create a transaction if necessary, based on the given method and class.
* <p>Performs a default TransactionAttribute lookup for the given method.
@ -217,7 +293,8 @@ public abstract class TransactionAspectSupport implements InitializingBean {
protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
return createTransactionIfNecessary(txAttr, methodIdentification(method));
PlatformTransactionManager tm = determineTransactionManager(txAttr);
return createTransactionIfNecessary(tm, txAttr, methodIdentification(method));
}
/**
@ -245,7 +322,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @see #getTransactionAttributeSource()
*/
protected TransactionInfo createTransactionIfNecessary(
TransactionAttribute txAttr, final String joinpointIdentification) {
PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
@ -259,7 +336,6 @@ public abstract class TransactionAspectSupport implements InitializingBean {
TransactionStatus status = null;
if (txAttr != null) {
PlatformTransactionManager tm = getTransactionManager();
if (tm != null) {
status = tm.getTransaction(txAttr);
}
@ -270,7 +346,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
}
}
}
return prepareTransactionInfo(txAttr, joinpointIdentification, status);
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
/**
@ -281,10 +357,10 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @param status the TransactionStatus for the current transaction
* @return the prepared TransactionInfo object
*/
protected TransactionInfo prepareTransactionInfo(
protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm,
TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification);
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// We need a transaction for this method
if (logger.isTraceEnabled()) {
@ -319,7 +395,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
getTransactionManager().commit(txInfo.getTransactionStatus());
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
@ -337,7 +413,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
}
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
getTransactionManager().rollback(txInfo.getTransactionStatus());
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
@ -357,7 +433,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
getTransactionManager().commit(txInfo.getTransactionStatus());
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
@ -392,7 +468,9 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* Opaque object used to hold Transaction information. Subclasses
* must pass it back to methods on this class, but not see its internals.
*/
protected class TransactionInfo {
protected final class TransactionInfo {
private final PlatformTransactionManager transactionManager;
private final TransactionAttribute transactionAttribute;
@ -402,11 +480,17 @@ public abstract class TransactionAspectSupport implements InitializingBean {
private TransactionInfo oldTransactionInfo;
public TransactionInfo(TransactionAttribute transactionAttribute, String joinpointIdentification) {
public TransactionInfo(PlatformTransactionManager transactionManager,
TransactionAttribute transactionAttribute, String joinpointIdentification) {
this.transactionManager = transactionManager;
this.transactionAttribute = transactionAttribute;
this.joinpointIdentification = joinpointIdentification;
}
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
public TransactionAttribute getTransactionAttribute() {
return this.transactionAttribute;
}
@ -438,7 +522,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
this.oldTransactionInfo = (TransactionInfo) transactionInfoHolder.get();
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}

View File

@ -1,12 +1,12 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -19,7 +19,7 @@ package org.springframework.transaction.interceptor;
import org.springframework.transaction.TransactionDefinition;
/**
* This interface adds a <code>rollbackOn</code> specification to TransactionDefinition.
* This interface adds a <code>rollbackOn</code> specification to {@link TransactionDefinition}.
* As custom <code>rollbackOn</code> is only possible with AOP, this class resides
* in the AOP transaction package.
*
@ -29,7 +29,9 @@ import org.springframework.transaction.TransactionDefinition;
* @see RuleBasedTransactionAttribute
*/
public interface TransactionAttribute extends TransactionDefinition {
String getQualifier();
/**
* Should we roll back on the given exception?
* @param ex the exception to evaluate

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -94,11 +94,12 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(invocation.getMethod());
if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
@ -120,10 +121,10 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
new TransactionCallback() {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceed();
}
@ -167,6 +168,15 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
// Serialization support
//---------------------------------------------------------------------
private void writeObject(ObjectOutputStream oos) throws IOException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
oos.defaultWriteObject();
// Deserialize superclass fields.
oos.writeObject(getTransactionManager());
oos.writeObject(getTransactionAttributeSource());
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
ois.defaultReadObject();
@ -178,15 +188,6 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
setTransactionAttributeSource((TransactionAttributeSource) ois.readObject());
}
private void writeObject(ObjectOutputStream oos) throws IOException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
oos.defaultWriteObject();
// Deserialize superclass fields.
oos.writeObject(getTransactionManager());
oos.writeObject(getTransactionAttributeSource());
}
/**
* Internal holder class for a Throwable, used as a return value

View File

@ -23,7 +23,6 @@ import org.springframework.aop.framework.AbstractSingletonProxyFactoryBean;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.transaction.PlatformTransactionManager;
@ -171,12 +170,7 @@ public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBe
* @see org.springframework.transaction.PlatformTransactionManager
*/
public void setBeanFactory(BeanFactory beanFactory) {
if (this.transactionInterceptor.getTransactionManager() == null &&
beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) beanFactory;
PlatformTransactionManager ptm = BeanFactoryUtils.beanOfTypeIncludingAncestors(lbf, PlatformTransactionManager.class);
this.transactionInterceptor.setTransactionManager(ptm);
}
this.transactionInterceptor.setBeanFactory(beanFactory);
}

View File

@ -22,6 +22,7 @@ import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.CallCountingTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
@ -32,16 +33,34 @@ public class AnnotationDrivenTests extends TestCase {
public void testWithProxyTargetClass() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("annotationDrivenProxyTargetClassTests.xml", getClass());
TransactionalService service = (TransactionalService) context.getBean("service");
CallCountingTransactionManager tm1 = context.getBean("transactionManager1", CallCountingTransactionManager.class);
CallCountingTransactionManager tm2 = context.getBean("transactionManager2", CallCountingTransactionManager.class);
TransactionalService service = context.getBean("service", TransactionalService.class);
assertTrue(AopUtils.isCglibProxy(service));
service.setBeanName("someName");
service.setSomething("someName");
assertEquals(1, tm1.commits);
assertEquals(0, tm2.commits);
service.doSomething();
assertEquals(1, tm1.commits);
assertEquals(1, tm2.commits);
service.setSomething("someName");
assertEquals(2, tm1.commits);
assertEquals(1, tm2.commits);
service.doSomething();
assertEquals(2, tm1.commits);
assertEquals(2, tm2.commits);
}
public static class TransactionCheckingInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
if (methodInvocation.getMethod().getName().equals("setSomething")) {
assertTrue(TransactionSynchronizationManager.isSynchronizationActive());
}
else {
assertFalse(TransactionSynchronizationManager.isSynchronizationActive());
}
return methodInvocation.proceed();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,12 +21,16 @@ import org.springframework.transaction.annotation.Transactional;
/**
* @author Rob Harrop
* @author Juergen Hoeller
*/
@Transactional
public class TransactionalService implements BeanNameAware {
public class TransactionalService {
public void setBeanName(String name) {
// just for testing :)
@Transactional("synch")
public void setSomething(String name) {
}
@Transactional("noSynch")
public void doSomething() {
}
}

View File

@ -2,7 +2,7 @@
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<tx:annotation-driven proxy-target-class="true" order="0"/>
@ -12,8 +12,15 @@
<bean id="txCheckingInterceptor" class="org.springframework.transaction.config.AnnotationDrivenTests$TransactionCheckingInterceptor"/>
<bean id="transactionManager" class="org.springframework.transaction.CallCountingTransactionManager"/>
<bean id="transactionManager1" class="org.springframework.transaction.CallCountingTransactionManager">
<qualifier value="synch"/>
</bean>
<bean id="transactionManager2" class="org.springframework.transaction.CallCountingTransactionManager">
<property name="transactionSynchronizationName" value="SYNCHRONIZATION_NEVER"/>
<qualifier value="noSynch"/>
</bean>
<bean id="service" class="org.springframework.transaction.config.TransactionalService"/>
</beans>