Spring là một framework Java được sử dụng phổ biến nhất hiện nay, nó mang đến rất nhiều tính năng và nhiều phần bổ trợ cho các ứng dụng Java. Tuy nhiên, hầu hết mọi người đều có khuynh hướng sử dụng những tính năng này mà không thực sự hiểu cơ chế bên dưới của chúng.
1. Cách sử dụng và trường hợp sử dụng
@Transaction(value = "myTransactionManager", propagation = Propagation.REQUIRED) public void myMethod() { ... }
Thuộc tính value
của annotation @Transaction
không bắt buộc phải được khai báo. Nếu không khai báo thuộc tính này thì mặc định Spring sẽ tìm kiếm một bean
bất kì được khai báo bên trong context
có tên là "transactionManager" (đây là convention mặc định trong Spring).
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
2. Cấu hình sử dụng transaction trong Spring context
Muốn annotation @Transaction
có thể hoạt động được, ta sẽ phải khai báo thẻ <tx:annotation-driven>
(trong đó tx
là rút gọn của namespace "http://www.springframework.org/schema/tx" hoặc cũng có thể tạm hiểu đây là một alias cho namespace này).
3. Phân tích code
3.1. Khai báo spring bean
Ở phần này, chúng ta sẽ xem xét cách mà Spring context
xử lý khi khai báo sử dụng thẻ <tx:annotation-driven>
1, org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser
/**
* Parses the '<code></code>' tag. Will
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
* with the container as necessary.
*/
public BeanDefinition parse(Element element, ParserContext parserContext) {
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
registerTransactionAspect(element, parserContext);
} else {
// mode="proxy"
// DEFAULT MODE
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
Hầu hết, chúng ta sẽ đều đi vào block else
của block điều kiện if-else
trong đoạn code bên trên (mode="proxy"), vì vậy chúng ta sẽ gọi AopAutoProxyConfigurer.configureAutoProxyCreator()
private static class AopAutoProxyConfigurer { public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); if (!parserContext.getRegisty().containsBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME)) { Object eleSource = parserContext.extractSource(element); } // Create the TransactionAttributeSource definition. RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class); sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // The bean AnnotationTransactionAttributeSource is created and registed dynamically here String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); // Create the TransactionInterceptor definition. // Point A RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class); interceptorDef.setSource(eleSource); interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // Set the declared transaction manager in to the transactionInterceptor, when available registerTransactionManager(element, interceptorDef); interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName)); // The bean TransactionInterceptor is created and registed dynamically here String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef); // Create the TransactionAttributeSourceAdvisor definition. // This bean is an AOP definition RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // Inject the bean AnnotationTransactionAttributeSource into the AOP definition // Point B advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName)); // Definition of advice bean = TransactionInterceptor previously declared advisorDef.getPropertyValues().add("adviceBeanName", interceptorName); if (element.hasAttribute("order")) { advisorDef.getPropertyValues().add("order", element.getAttribute("order")); } // The bean BeanFactoryTransactionAttributeSourceAdvisor is created and registed dynamically here parserContext.getRegistry().registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef); CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource); compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName)); compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName)); compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, TRANSACTION_ADVISOR_BEAN_NAME)); parserContext.registerComponent(compositeDef); } // Retrieve the transactionManager attribute defined on when available // Example private static void registerTransactionManager(Element element, BeanDefinition def) { def.getPropertyValues().add("transactionManagerBeanName", TxNamespaceHandler.getTransactionManagerName(element)); } }
Tạo lớp transaction interceptor
:
// Point A RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
Khai báo transaction manager bằng thẻ <tx:annotation-driven>
, transaction manager
sẽ được tìm kiếm bên trong Spring context và kèm theo là transaction interceptor đã được định nghĩa trước đó:
// Set the declared transaction manager in to the transactionInterceptor, when available registerTransactionManager(element, interceptorDef);
Cuối cùng chúng ta sẽ đăng kí interceptorDef bean
với Spring context:
// The bean TransactionInterceptor is created and registed dynamically here String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
Khai báo bean TransactionAttributeSourceAdvisor
và đăng kí với Spring context
// This bean is an AOP definition RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // Inject the bean AnnotationTransactionAttributeSource into the AOP definition // Point B advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName)); // Definition of advice bean = TransactionInterceptor previously declared advisorDef.getPropertyValues().add("adviceBeanName", interceptorName); if (element.hasAttribute("order")) { advisorDef.getPropertyValues().add("order", element.getAttribute("order")); } // The bean BeanFactoryTransactionAttributeSourceAdvisor is created and registed dynamically here parserContext.getRegistry().registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef);
3.2. Phân tích @Transactional
1, org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor
Trong phần này, chúng ta sẽ xem cách mà annotaion @Transactional
được phân tích trong suốt quá trình runtime bởi Spring để nhận lấy các thuộc tính liên quan đến một giao dịch (transaction)
// Injected at Point B
private TransactionAttributeSource transactionAttributeSource;
// Define a pointcut here for AOP, injecting the transactionAttributeSource that was
// set in AopAutoProxyConfigurer
// see Point B
private final TransactionAttributeSourcePointcut pointcut =
new TransactionAttributeSourcePointcut() {
@Override
protected TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
};
Ở đây, Spring định nghĩa ra một pointcut advisor
được xử lý bởi lớp TransactionAttributeSourcePointcut
. transactionAttributeSource
được sử dụng thông qua anonymous class bên trong phương thức được overide getTransactionAttributeSource()
.
2, Lớp trừu tượng org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut
abstract class org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut
// Determine whether a call on a particular method matches the poincut
// If it matches then the advice bean will be called
// The advice bean that has been registered for this pointcut is the
// TransactionInterceptor class (see Point A)
public boolean matches(Method method, Class targetClass) {
TransactionAttributeSource tas = getTransactionAttributeSource();
// Call getTransactionAttribute of the injected transactionAttributeSoirce
// (see Point C)
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
Lớp trừu tượng này chỉ định nghĩa hành vi mặc định của phương thức matches()
để kiểm tra xem các join point
có phù hợp với point cut
không.
3, org.springframework.transaction.annotation.AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
// Should be false in a context of J2SE private static final boolean ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", AnnotationTransactionAttributeSource.class.getClassLoader()); // Default constructor public AnnotationTransactionAttributeSource() { this(true); } // publicMethodsOnly = true because this bean has been registered dynamically // by AopAutoProxyConfigurer with no argument so the default constructor above applies // // ejb3Present = false public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) { this.publicMethodsOnly = publicMethodsOnly; this.annotationParsers = new LinkedHashSet(2); this.annotationParsers.add(new SpringTransactionAnnotationParser()); if (ejb3Present) { this.annotationParsers.add(new Ejb3TransactionAnnotationParser()); } }Hàm khởi tạo thứ hai của lớp
AnnotationTransactionAttributeSource
đăng kí SpringTransactionAnnotationParser
là trình phân tích cú pháp mặc định cho annotation @Transaction
4, org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource
Point C & Point D
// Point C public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) { // First, see if we have a cached value. Object cacheKey = getCacheKey(method, targetClass); Object cached = this.attributeCache.get(cacheKey); if (cached != null) { .... .... // Not interesting code } else { // We need to work it out. // (see Point D below) TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass); // Put it in the cache. if (txAtt == null) { this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); } else { if (logger.isDebugEnabled()) { logger.debug("Adding transactional method '" + method.getName() + "' with attribute: " + txAtt); } this.attributeCache.put(cacheKey, txAtt); } return txAtt; } } // Point D private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) { // Don't allow no-public methods as required. // Here allowPublicMethodsOnly() will return true because we set the attribute // publicMethodOnly = true in the constructor of AnnotationTransactionAttribute if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // Ignore CGLIB subclasses - introspect the actual user class. Class userClass = ClassUtils.getUserClass(targetClass); // The method may be on an interface, but we need attributes from the target class. // If the target class is null, the method will be unchanged. Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass); // If we are dealing with method with generic parameters, find the original method. specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); // Find the @Transactional attributes of the method in the target class. // (see Point E) TransactionAttribute txAtt = findTransactionAttribute(specificMethod); if (txAtt != null) { return txAtt; } ... // Not interesting code }
Phương thức getTransactionAttribute()
của lớp trừu tượng này sẽ ủy nhiệm công việc cho phương thức computeTransactionAttribute()
.
Đầu tiên, nó sẽ xác định lớp mục tiêu (trong trường hợp annotation này được đặt bên trên một phương thức của interface) sau đó sẽ gọi phương thức findTransactionAttribute()
.
5, org.springframework.transaction.annotation.AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
Point E
// Point E protected TransactionAttribute findTransactionAttribute(Method method) { // See below return determineTransactionAttribute(method); } protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) { for (TransactionAnnotationParser annotationParser : this.annotationParsers) { // (see Point F) TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae); if (attr != null) { return attr; } } return null; }
Thêm một lần nữa, công việc thực sự vẫn sẽ chưa được thực hiện ở đây, nhưng nó sẽ được ủy quyền cho lớp phân tích annotation là SpringTransactionAnnotationParser
đã được đăng kí từ trước đó bên trong hàm khởi tạo của lớp AnnotationTransactionAttributeSource
.
6, org.springframework.transaction.annotation.SpringTransactionAnnotationParser
Point F
// Point F public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) { Transactional ann = ae.getAnnotation(Transactional.class); if (ann == null) { for (Annotation metaAnn : ae.getAnnotations()) { // @Transactional annotation ann = metaAnn.annotationType().getAnnotation(Transactional.class); if (ann != null) { break; } } } if (ann != null) { //See below return parseTransactionAnnotation(ann); } else { return null; } } public TransactionAttribute parseTransactionAnnotation(Transactional ann) { RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); rbta.setPropagationBehavior(ann.propagation().value()); rbta.setIsolationLevel(ann.isolation().value()); rbta.setTimeout(ann.timeout()); rbta.setReadOnly(ann.readOnly()); /* Set qualifier name in the case multiple transaction managers are used * bean id="myTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" * property name="entityManagerFactory" ref="myEntityManagerFactory" / * /bean * * @Transactional(value = "myTransactionManager") */ rbta.setQualifier(ann.value()); ArrayList rollBackRules = new ArrayList(); ... // Not interesting code return rbta; }
Trình phân tích cú pháp sẽ lấy toàn bộ thuộc tính của annotation @Transaction
, gồm:
- propagation behavior
- isolation level
- giá trị timeout của transaction
- readOnly flag
- và thuộc tính quan trọng nhất là:
value
, thuộc tính này sẽ xác định tên của beantransactionManager
đã được khai báo bên trong Spring context và chịu trách nhiệm đối với transaction hiện tại.
value
, thì giá trị sẽ mặc định là "transactionManager"
. Khi phải làm việc cùng với nhiều database hoặc chia ra nhiều datasource, có nhiều hơn một transactionManager
được khai báo trong Spring context thì thuộc tính value
sẽ rất quan trọng để giúp Spring có thể chọn đúng transactionManager
cần thiết.
3.3. Transactional interceptor
1, org.springframework.transaction.interceptor.TransactionInterceptor extends TransactionAspectSupport
// The real job is done here
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be null
.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// If the transaction attribute is null, the method is non-transactional.
// Similar to Point C
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
// (see Point G)
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(invocation.getMethod(), targetClass);
// The txAttr is not null but the transactionManager is NOT an instance
// of CallbackPreferringPlatformTransactionManager so we still enter the if block
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction() and commit/rollback calls.
// (see Point H)
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.proceed();
} catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// Commit after the method call returns
// (see Point O)
commitTransactionAfterReturning(txInfo);
return retVal;
}
- Đầu tiên, Spring lấy ra các thuộc tính của transaction
//Similar to Point C final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
- Sau đó, lấy ra
transactionManager
từ Spring context và các thuộc tính của transaction
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
- Một transaction được tạo ra bởi
entityManager
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
- Gọi phương thức mục tiêu
retVal = invocation.proceed();
- Sau khi phương thức được gọi trả về kết quả, transaction sẽ được commit
commitTransactionAfterReturning(txInfo);
2, public abstract class org.springframework.transaction.interceptor.TransactionAspectSupport
Point G & Point H
//Point G protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) { if (this.transactionManager != null || this.beanFactory == null || txAttr == null) { return this.transactionManager; } String qualifier = txAttr.getQualifier(); // Case when the transaction manager has been declared directly in the @Transactional annotation // Example @Transactional(value = "myTransactionManager") if (StringUtils.hasLength(qualifier)) { return TransactionAspectUtils.getTransactionManager(this.beanFactory, qualifier); } // Case when the transaction manager has been declared in the tx:annotation-driven tag // Example tx:annotation driven transaction-manager="myTransactionManager" else if (this.transactionManagerBeanName != null) { return this.beanFactory.getBean(this.transactionManagerBeanName, PlatformTransactionManager.class); } ... // Not interesting code } // Point H protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) { // If no name specified, apply method identification as transaction name. // This is the default case 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) { // Call to the AbstractPlatFormTransactionManager to start a transaction // (see Point I) status = tm.getTransaction(txAttr); } else { if (logger.isDebugEnabled()) { logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured"); } } } return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); }
Lớp này sẽ thực hiện hai nhiệm vụ chính:
- Xác định
transactionManager
để quản lí transaction hiện tại, bằng cách sử dụng thuộc tínhvalue
thuộc annotation@Transaction
hoặc sử dụng thuộc tínhtransaction-manager
của thẻ<tx:annotation-driven>
. - ủy quyền tạo transaction cho lớp
AbstractPlatFormTransactionManager
.
3, abstract class org.springframework.transaction.support.AbstractPlatformTransactionManager
Point I
//Point I public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { // Retrieve the transaction from JpaTransactionManager.doGetTransaction() // (see Point J) bject 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(); } if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } ... // 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'"); } // Our case 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); /* * Return a new DefaultTransactionStatus(transaction, * newTransaction = true, * newSynchronization = true, * definition.isReadOnly(),debugEnabled, * suspendedResources) * for a new transaction */ DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); // Real job here, delegates call to JpaTransactionManager.doBegin() // (see Point K) doBegin(transaction, definition); // Set some synchronization flags to the TransactionSynchronizationManager thread local prepareSynchronization(status, definition); return status; } catch (RuntimeException ex) { resume(null, suspendedResources); throw ex; } catch (Error err) { resume(null, suspendedResources); throw err; } } else { // TransactionDefinition = PROPAGATION_SUPPORTS or PROPAGATION_NOT_SUPPORTED // or PROPAGATION_NEVER // Create "empty" transaction: no actual transaction, but potentially synchronization. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } }
Phương thức getTransaction()
tự ủy nhiệm việc tạo và bắt đầu transaction cho JpaTransactionManager
Chúng ta có thể thấy cách mà Spring quản lý các kiểu Propagation behavior
khác nhau.
4, org.springframework.orm.jpa.JpaTransactionManager
Point J & Point K
// Point J protected Object doGetTransaction() { JpaTransactionObject txObject = new JpaTransactionObject(); txObject.setSavepointAllowed(isNestedTransactionAllowed()); // Try to retrieve an EntityManagerHolder from the thread local map of // TransactionSynchronizationManager using the EntityManagerFactory as search key // The EntityManagerFactory was injected in the JpaTransactionManager in the XML config file // // bean id="myTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" // property name="entityManagerFactory" ref="myEntityManagerFactory" // bean // // // this EntityManagerHolder might be null when called the first time EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(getEntityManagerFactory()); if (emHolder != null) { if (logger.isDebugEnabled()) { logger.debug("Found thread-bound EntityManager [" + emHolder.getEntityManager() + "] for JPA transaction"); } // attach the EntityManagerHolder to the JpaTransactionObject // the flag false is set to the property newEntityManagerHolder txObject.setEntityManagerHolder(emHolder, false); } // The datasource is injected directly into the JpaTransactionManager // after bean initialization (afterPropertySet()) // by inspecting the injected EntityManagerFactory // // bean id="myEntityManagerFactory" // class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" // property name="dataSource" ref="myDataSource" // property name="persistenceUnitName" value="myPersistenceUnit" // bean // // // this test always evaluates to true if (getDataSource() != null) { ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(getDataSource()); // attach a connectionHolder to the JpaTransactionObject (to start JDBC transaction probably) txObject.setConnectionHolder(conHolder); } return txObject; } //Point K protected void doBegin(Object transaction, TransactionDefinition definition) { JpaTransactionObject txObject = (JpaTransactionObject) transaction; ... try { // The EntityManagerHolder can be null if not registered already in the // thread local map of TransactionSynchronizationManager if (txObject.getEntityManagerHolder() == null || txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) { // Create a new EntityManager from the EntityManagerFactory EntityManager newEm = createEntityManagerForTransaction(); if (logger.isDebugEnabled()) { logger.debug("Opened new EntityManager [" + newEm + "] for JPA transaction"); } // attach the EntityManagerHolder to the JpaTransactionObject // newEntityManagerHolder = true // because the EntityManager has just been created from scratch txObject.setEntityManagerHolder(new EntityManagerHolder(newEm), true); } EntityManager em = txObject.getEntityManagerHolder().getEntityManager(); final int timeoutToUse = determineTimeout(definition); /* Delegate to JpaDialect for actual transaction begin, passing the EntityManager * * META-INF|persistence.xml * * persistence-unit name="myPersistenceUnit" transaction-type="RESOURCE_LOCAL" * provider * org.hibernate.ejb.HibernatePersistence * provider * properties * property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" * ... */ // (see Point L for HibernateJpaDialect) Object transactionData = getJpaDialect().beginTransaction(em, new DelegatingTransactionDefinition(definition) { @Override public int getTimeout() { return timeoutToUse; } }); // Set transaction data to the JpaTransactionObject txObject.setTransactionData(transactionData); // Register transaction timeout. if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getEntityManagerHolder().setTimeoutInSeconds(timeoutToUse); } // Register the JPA EntityManager's JDBC Connection for the DataSource, if set. if (getDataSource() != null) { // Retrieve the underlying JDBC connection by calling the JPA Dialect class ConnectionHandle conHandle = getJpaDialect().getJdbcConnection(em, definition.isReadOnly()); if (conHandle != null) { ConnectionHolder conHolder = new ConnectionHolder(conHandle); if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) { conHolder.setTimeoutInSeconds(timeoutToUse); } if (logger.isDebugEnabled()) { logger.debug("Exposing JPA transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]"); } // Set the JDBC connection to the current Threadlocal resources map, the // datasource being the key TransactionSynchronizationManager.bindResource(getDataSource(), conHolder); // Set JDBC connection holder to the JpaTransactionObject txObject.setConnectionHolder(conHolder); } else { if (logger.isDebugEnabled()) { logger.debug("Not exposing JPA transaction [" + em + "] as JDBC transaction because JpaDialect [" + getJpaDialect() + "] does not support JDBC Connection retrieval"); } } } // If the EntityManager has been created from scratch (see Point L) if (txObject.isNewEntityManagerHolder()) { // register the EntityManagerHolder to the current Threadlocal resources map, the EntityManagerFactory being the key TransactionSynchronizationManager.bindResource( getEntityManagerFactory(), txObject.getEntityManagerHolder()); } txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true); } catch (TransactionException ex) { closeEntityManagerAfterFailedBegin(txObject); throw ex; } catch (Exception ex) { closeEntityManagerAfterFailedBegin(txObject); throw new CannotCreateTransactionException("Could not open JPA EntityManager for transaction", ex); } }
Công việc quan trọng nhất sẽ được hoàn thành bên trong lớp này.
Point J: doGetTransaction()
-
Đầu tiên, Spring sẽ thử tìm trong
map
ThreadLocal
TransactionSynchronizationManager
để tìm xem nếu có mộtentityManager
đang tồn tại thì sẽ sử dụngentityManager factory
như là khóa tìm kiếm.
Bài viết rất hay mang tính "sâu" và bổ ích :)
Trả lờiXóa