使用 Spring 4,如何在主线程之外创建 @Transactional?
With Spring 4, how do I create @Transactional outside of my main thread?
我正在使用 Spring 4.3.8.RELEASE 和 Hibernate 5.1。我在上下文中设置了一个线程池
<bean id="myprojectThreadFactory" class="org.springframework.scheduling.concurrent.CustomizableThreadFactory">
<constructor-arg value="myproject-"/>
</bean>
<bean id="myprojectTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="threadFactory" ref="myprojectThreadFactory"/>
<property name="corePoolSize" value="${myproject.core.thread.pool.size}" />
<property name="maxPoolSize" value="${myproject.max.thread.pool.size}" />
</bean>
但是我如何在我的主线程之外的任务线程之一中创建事务?我正在尝试这个
// Cue up threads to execute
for (final Organization org : allOrgs)
{
m_threadExecutor.execute(new Thread(new Runnable(){
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void run()
{
System.out.println("started.");
processData(org.getId());
System.out.println("finished.");
}
}));
但这会导致以下异常
Exception in thread "myproject-1" Exception in thread "myproject-2" org.hibernate.SessionException: Session is closed!
at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:132)
at org.hibernate.internal.SessionImpl.setCacheMode(SessionImpl.java:1511)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1109)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1033)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
at com.sun.proxy.$Proxy68.find(Unknown Source)
at org.mainco.subco.core.repo.GenericDao.find(GenericDao.java:123)
at org.mainco.subco.organization.repo.OrganizationDaoImpl.find(OrganizationDaoImpl.java:61)
at org.mainco.subco.organization.service.OrganizationServiceImpl.getDescendantOrganizations(OrganizationServiceImpl.java:283)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy75.getDescendantOrganizations(Unknown Source)
at org.mainco.subco.myproject.repo.myprojectClassDaoImpl.findBymyprojectOrg(myprojectClassDaoImpl.java:89)
at org.mainco.subco.myproject.service.myprojectClassServiceImpl.find(myprojectClassServiceImpl.java:300)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy91.find(Unknown Source)
at org.mainco.subco.myproject.quartz.ImportClassesWorker.preProcessData(ImportClassesWorker.java:56)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker.processData(AbstractImportDataWorker.java:126)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker.access[=13=](AbstractImportDataWorker.java:123)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker.run(AbstractImportDataWorker.java:110)
at java.lang.Thread.run(Thread.java:745)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Spring 对您的 Runnable 实例一无所知。因此,在 run
方法上添加注释没有任何意义。相反,您想要做的是将 @Transactional
放在 processData
方法上。但是注意应该是一个Springbean的方法!
更新:
刚刚尝试用 @Transactional
和 @Async
标记一个业务方法,它对我有用。我使用 spring-boot 版本 1.5.4.RELEASE 随 spring 4.3.9.RELEASE 和 hibernate 5.0.12.Final 一起提供。对于我的 @Async
配置,我只使用了 @EnableAsync
。这是我得到的:
@Test
@Transactional
public void test() throws InterruptedException {
beanA.testMethodA();
//Sleeping, since after test is completed application is down which leaves @Async transaction not completed.
Thread.sleep(3000);
System.out.println("Thread in test method: " + Thread.currentThread().getName());
System.out.println("Transaction in test method: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
这里是BeanA的一个方法。它是事务性的,具有默认的传播级别,这使其绑定到由测试启动的事务:
@Override
@Transactional
public void testMethodA() {
beanB.testMethodB();
System.out.println("Thread in method A: " + Thread.currentThread().getName());
System.out.println("Transaction in method A: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
最后一部分 - BeanB 的方法,标记为 @Async
和 @Transactional
。它在单独的线程和单独的事务中执行 运行:
@Async
@Transactional
@Override
public void testMethodB() {
System.out.println("Thread in method B: " + Thread.currentThread().getName());
System.out.println("Transaction in method B: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
在我 运行 测试之后,我得到以下输出:
Thread in test method: main
Transaction in test method: com.example.Whosebug.TransactionTest.test
Thread in method A: main
Transaction in method A: com.example.Whosebug.TransactionTest.test
Thread in method B: SimpleAsyncTaskExecutor-1
Transaction in method B: com.example.Whosebug.BeanBImpl.testMethodB
注意最后两个陈述。标记为 @Async
和 @Transactional
的方法 运行 在它自己的事务和它自己的线程中。
逻辑使用TransactionTemplate。像这样
final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
taskExecutor.execute(new Runnable() {
@Override
public void run() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
System.out.println("started.");
processData(org.getId());
System.out.println("finished.");
}
});
}
});
我正在使用 Spring 4.3.8.RELEASE 和 Hibernate 5.1。我在上下文中设置了一个线程池
<bean id="myprojectThreadFactory" class="org.springframework.scheduling.concurrent.CustomizableThreadFactory">
<constructor-arg value="myproject-"/>
</bean>
<bean id="myprojectTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="threadFactory" ref="myprojectThreadFactory"/>
<property name="corePoolSize" value="${myproject.core.thread.pool.size}" />
<property name="maxPoolSize" value="${myproject.max.thread.pool.size}" />
</bean>
但是我如何在我的主线程之外的任务线程之一中创建事务?我正在尝试这个
// Cue up threads to execute
for (final Organization org : allOrgs)
{
m_threadExecutor.execute(new Thread(new Runnable(){
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void run()
{
System.out.println("started.");
processData(org.getId());
System.out.println("finished.");
}
}));
但这会导致以下异常
Exception in thread "myproject-1" Exception in thread "myproject-2" org.hibernate.SessionException: Session is closed!
at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:132)
at org.hibernate.internal.SessionImpl.setCacheMode(SessionImpl.java:1511)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1109)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1033)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
at com.sun.proxy.$Proxy68.find(Unknown Source)
at org.mainco.subco.core.repo.GenericDao.find(GenericDao.java:123)
at org.mainco.subco.organization.repo.OrganizationDaoImpl.find(OrganizationDaoImpl.java:61)
at org.mainco.subco.organization.service.OrganizationServiceImpl.getDescendantOrganizations(OrganizationServiceImpl.java:283)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy75.getDescendantOrganizations(Unknown Source)
at org.mainco.subco.myproject.repo.myprojectClassDaoImpl.findBymyprojectOrg(myprojectClassDaoImpl.java:89)
at org.mainco.subco.myproject.service.myprojectClassServiceImpl.find(myprojectClassServiceImpl.java:300)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy91.find(Unknown Source)
at org.mainco.subco.myproject.quartz.ImportClassesWorker.preProcessData(ImportClassesWorker.java:56)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker.processData(AbstractImportDataWorker.java:126)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker.access[=13=](AbstractImportDataWorker.java:123)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker.run(AbstractImportDataWorker.java:110)
at java.lang.Thread.run(Thread.java:745)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Spring 对您的 Runnable 实例一无所知。因此,在 run
方法上添加注释没有任何意义。相反,您想要做的是将 @Transactional
放在 processData
方法上。但是注意应该是一个Springbean的方法!
更新:
刚刚尝试用 @Transactional
和 @Async
标记一个业务方法,它对我有用。我使用 spring-boot 版本 1.5.4.RELEASE 随 spring 4.3.9.RELEASE 和 hibernate 5.0.12.Final 一起提供。对于我的 @Async
配置,我只使用了 @EnableAsync
。这是我得到的:
@Test
@Transactional
public void test() throws InterruptedException {
beanA.testMethodA();
//Sleeping, since after test is completed application is down which leaves @Async transaction not completed.
Thread.sleep(3000);
System.out.println("Thread in test method: " + Thread.currentThread().getName());
System.out.println("Transaction in test method: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
这里是BeanA的一个方法。它是事务性的,具有默认的传播级别,这使其绑定到由测试启动的事务:
@Override
@Transactional
public void testMethodA() {
beanB.testMethodB();
System.out.println("Thread in method A: " + Thread.currentThread().getName());
System.out.println("Transaction in method A: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
最后一部分 - BeanB 的方法,标记为 @Async
和 @Transactional
。它在单独的线程和单独的事务中执行 运行:
@Async
@Transactional
@Override
public void testMethodB() {
System.out.println("Thread in method B: " + Thread.currentThread().getName());
System.out.println("Transaction in method B: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
在我 运行 测试之后,我得到以下输出:
Thread in test method: main
Transaction in test method: com.example.Whosebug.TransactionTest.test
Thread in method A: main
Transaction in method A: com.example.Whosebug.TransactionTest.test
Thread in method B: SimpleAsyncTaskExecutor-1
Transaction in method B: com.example.Whosebug.BeanBImpl.testMethodB
注意最后两个陈述。标记为 @Async
和 @Transactional
的方法 运行 在它自己的事务和它自己的线程中。
逻辑使用TransactionTemplate。像这样
final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
taskExecutor.execute(new Runnable() {
@Override
public void run() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
System.out.println("started.");
processData(org.getId());
System.out.println("finished.");
}
});
}
});