我的服务(使用main dataSource的jobExecutor)在从spring MVC控制器调用时工作正常,但是当从调度方法调用时,所有时间都抛出“TransactionRequiredException:没有事务正在进行中”。原因是看起来从scheduledThreadPool绑定到线程的jdbcTransaction将NOT_ACTIVE绑定为localStatus。该事务用于主数据源,默认情况下从DataSourceTransactionManager开始。
我正在使用spring-boot,spring-data和hibernate,以下是那些版本
spring-boot:1.2.7.RELEASE
hibernate-core:4.3.11.Final
hibernate-entitymanager:4.3.11.Final
还使用java配置
ServerConfig.java
@Configuration
@EnableAutoConfiguration
@EnableScheduling
@EnableAsync
@EnableAspectJAutoProxy
@ComponentScan("com.my.client")
@EnableTransactionManagement
@EntityScan(basePackages = {"com.my.database.model"})
@EnableJpaRepositories(
transactionManagerRef = "transactionManager",
basePackages = {"com.my.database.repository"})
public class ServerConfig extends SpringBootServletInitializer implements SchedulingConfigurer, AsyncConfigurer {
static Logger log = Logger.getLogger(ServerModeConfig.class.getName());
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
/**
* get executor for scheduling job
* @return scheduled executor
*/
@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(20);
}
/**
* get executor for async job
* @return executor for asynchronous job but no time limit
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(200);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyClientAsyncExceptionHandler();
}
}
MyScheduler.java
@Component
public class MyScheduler {
@Autowired
protected JobExecutor jobExecutor;
@Override
@Scheduled(cron = "0 */1 * * * *") // every minute
public void run() {
log.info("Trigger job");
jobExecutor.execute();
}
}
服务层
JobExecutorImpl.java
@Service("jobExecutor")
@Transactional("transactionManager")
public class JobExecutorImpl implements JobExecutor {
static Logger log = Logger.getLogger(JobExecutorImpl.class.getName());
@Override
public ClientJobBehaviour execute() {
log.info("transaction exists? ".concat( String.valueOf(TransactionSynchronizationManager.isActualTransactionActive()) )); // true
log.info("transaction sync? ".concat( String.valueOf(TransactionSynchronizationManager.isSynchronizationActive()) )); // true
ClientJobBehaviour job = new ClientJobBehaviour();
JobInstance jobInstance = new JobInstance();
jobInstance.setStatus(JobStatus.STARTED.toString());
jobInstance = jobInstanceRepository.save(jobInstance);
jobInstanceRepository.flush(); // throws TransactionRequiredException
job.setInstanceId(jobInstance.getId());
return job;
}
}
内部dataSource的Spring Data JPA存储库
JobInstanceRepository.java
@Repository
public interface JobInstanceRepository extends JpaRepository<JobInstance, Long>{
}
外部数据源的配置。 这使用JpaTransactionManager并命名为adapterTransactionManager。 外部数据源的存储库看起来工作正常
ExternalRepositoryConfig.java
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "adapterEntityManagerFactory",
transactionManagerRef = "adapterTransactionManager",
basePackages = {"com.my.adapter.database.repository"})
public class ExternalRepositoryConfig {
@Autowired
JpaVendorAdapter jpaVendorAdapter;
@Value("${adapter.datasource.url}")
private String databaseUrl;
@Value("${adapter.datasource.username}")
private String username;
@Value("${adapter.datasource.password}")
private String password;
@Value("${adapter.datasource.hibernate.dialect}")
private String dialect;
public DataSource dataSource() {
return new DriverManagerDataSource(databaseUrl, username, password);
}
@Bean(name = "adapterEntityManager")
public EntityManager entityManager() {
return entityManagerFactory().createEntityManager();
}
@Bean(name = "adapterEntityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", dialect);
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource());
emf.setJpaVendorAdapter(jpaVendorAdapter);
emf.setPackagesToScan("com.my.client.another.database.model");
emf.setPersistenceUnitName("adapterPersistenceUnit");
emf.setJpaProperties(properties);
emf.afterPropertiesSet();
return emf.getObject();
}
@Bean(name = "adapterTransactionManager")
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory());
}
}
当从调度方法
调用服务时,下面是stackTrace org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:410)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy95.flush(Unknown Source)
at com.textura.client.job.executor.JobExecutorHelperImpl.preProcess(JobExecutorHelperImpl.java:62)
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:317)
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$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
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:207)
at com.sun.proxy.$Proxy99.preProcess(Unknown Source)
at com.textura.client.job.executor.JobExecutorImpl.execute(JobExecutorImpl.java:30)
at com.textura.client.scheduler.ExportInvoicesScheduler.lambda$0(ExportInvoicesScheduler.java:49)
at com.textura.client.scheduler.ExportInvoicesScheduler$$Lambda$76/1738859546.accept(Unknown Source)
at java.util.Optional.ifPresent(Optional.java:159)
at com.textura.client.scheduler.ExportInvoicesScheduler.run(ExportInvoicesScheduler.java:43)
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.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
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)
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1171)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1332)
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.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:344)
at com.sun.proxy.$Proxy86.flush(Unknown Source)
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:291)
at com.sun.proxy.$Proxy86.flush(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.flush(SimpleJpaRepository.java:480)
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.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:436)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:421)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:393)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$DefaultMethodInvokingMethodInterceptor.invoke(RepositoryFactorySupport.java:506)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 40 common frames omitted
当我调试时,我可以看到jdbcTransaction的localStatus是NOT_ACTIVE而且hibernate下面的方法抛出异常
AbstractEntityManagerImpl.java
private void checkTransactionNeeded() {
if ( !isTransactionInProgress() ) {
throw new TransactionRequiredException(
"no transaction is in progress"
);
}
}
我们有两个dataSource,一个供内部使用,另一个供外部使用
application.properties
# database configuration
spring.datasource.url=jdbc:h2:file:~/internal-db;AUTO_SERVER=TRUE;LOCK_TIMEOUT=10000
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.schema=schema.sql
spring.datasource.data=data.sql
spring.datasource.initialize=false
#spring.datasource.initialize=true only for first time to create table, after that switch to false
# JPA. Hibernate
spring.jpa.database=H2
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=validate
#spring.jpa.hibernate.ddl-auto=choose one of [create-drop, create, update, validate, none]
spring.jpa.hibernate.naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.show-sql=false
spring.data.jpa.repositories.enabled=true
adapter.datasource.url=jdbc:sqlserver://some.host.com:1433;databaseName=MyDBname
adapter.datasource.username=sa
adapter.datasource.password=
adapter.datasource.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
答案 0 :(得分:5)
我已通过更改内部dataSource的transactionManager配置解决了此问题。看起来@EnableTransactionManagement
配置的默认事务管理器是 DataSourceTransactionManager ,并且hibernate上的某种方式begin()方法如果作业来自任何调度程序,则不调用AbstractTransactionImpl。所以我将内部dataSource的 DataSourceTransactionManager 更改为 JpaTransactionManager ,如下所示。现在,无论作业来自调度程序还是UI,两个dataSource的所有事务都会成功。所以我认为我在这篇文章中解决的问题已得到修复
@Configuration
@EnableAutoConfiguration
@EnableScheduling
@EnableAsync
@EnableAspectJAutoProxy
@ComponentScan("com.my.client")
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
basePackages = {"com.my.database.repository"})
public class ServerConfig extends SpringBootServletInitializer implements SchedulingConfigurer, AsyncConfigurer {
static Logger log = Logger.getLogger(ServerModeConfig.class.getName());
@Autowired
JpaVendorAdapter jpaVendorAdapter;
@Autowired
DataSource dataSource;
@Bean(name = "entityManager")
public EntityManager entityManager() {
return entityManagerFactory().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactory")
EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setJpaVendorAdapter(jpaVendorAdapter);
emf.setPackagesToScan("com.my.client.database.model");
emf.setPersistenceUnitName("default");
emf.afterPropertiesSet();
return emf.getObject();
}
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(entityManagerFactory());
return tm;
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
/**
* get executor for scheduling job
* @return scheduled executor
*/
@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(20);
}
/**
* get executor for async job
* @return executor for asynchronous job but no time limit
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(200);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyClientAsyncExceptionHandler();
}
}
答案 1 :(得分:0)
我建议尝试从您的项目中创建您的,repo事务或禁用您的事务属性并尝试它。当您尝试从事务上下文访问非事务上下文时,可能会发生这种情况。
@Repository
@Transactional
public interface JobInstanceRepository extends JpaRepository<JobInstance, Long>{
}
答案 2 :(得分:0)
尝试从JobExecutorImpl中删除@Service注释,并使用@Bean将其添加到ServerConfig类中。
另外,我会删除@EnableAspectJAutoProxy临时,以查看是否与方面扫描有任何冲突。找到根本问题时至少有一点不用担心。
答案 3 :(得分:0)
我对@Transactional @Scheduled方法有相同的探查。公开方法解决了这个问题。我不知道为什么!
答案 4 :(得分:0)
仅将@Transactional
添加到方法或类对我不起作用,我不得不使用@Transactional(value="entityTransactionManager")
添加事务管理器的bean名称。
在此之后一切正常。