In my spring-boot application I have problem with rollback in transaction method. For test purpose I have this example:
#Transactional(rollbackFor = Exception.class)
public void insertPersonSaveLog() {
refService.insertPerson();
String a = null;
a.getBytes();
auditService.editRequest();
}
in log I can see that rollback should be perform but person is still saved in my db. Maybe I am missing some annotation. This is Main Class:
#SpringBootApplication
#EnableScheduling
public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
new SpringApplicationBuilder(MyApplication.class).run(args);
}
}
and stacktrace:
149254 DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit
149254 DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [..]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-java.lang.Exception
149254 DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating in existing transaction
149258 DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL query
149258 DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement
149258 DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
149258 DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Registering transaction synchronization for JDBC Connection
149260 DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement
149262 DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
149262 DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction rollback
java.lang.NullPointerException: null
at org.my.service.InserPersonService.insertPersonSaveLog(InserPersonService.java:77)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
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: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.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
DbConfig:
#Bean
#ConfigurationProperties(prefix = "datasource.ref")
#Primary
public DataSource refDataSource() {
return new DataSource();
}
#Bean
public JdbcTemplate refJdbcTemplate() {
return new JdbcTemplate(refDataSource());
}
#Bean
#ConfigurationProperties(prefix = "datasource.log")
public DataSource logDataSource() {
return new DataSource();
}
#Bean
public JdbcTemplate logJdbcTemplate() {
return new JdbcTemplate(logDataSource());
}
and application.properties:
datasource.ref.url=jdbc:oracle:thin:#something:1521:ora11g
datasource.ref.username=...
datasource.ref.password=...
datasource.ref.driver-class-name=oracle.jdbc.OracleDriver
datasource.log.url=jdbc:oracle:thin:#something:1521:ora11g
datasource.log.username=...
datasource.log.password=...
datasource.log.driver-class-name=oracle.jdbc.OracleDriver
Related
i'd like to use JpaItemWriter
in conditions of multi-datasource in spring batch, JPA.
below is my config (there are log, common, member db config)
common db config
#Configuration
#EnableJpaRepositories(
basePackages = "com.member.batch.dao.common",
entityManagerFactoryRef = "commonEntityManager",
transactionManagerRef = "commonTransactionManager"
)
public class CommonConfiguration {
#ConfigurationProperties(prefix = "spring.datasource-common.hikari")
#Primary
#Bean(name = "commonDataSource")
public DataSource commonDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
#ConfigurationProperties("spring.jpa.common-properties")
#Bean(name = "commonHibernateProperties")
public HashMap<String, Object> hibernateProperties() {
return new HashMap<>();
}
#Bean
public LocalContainerEntityManagerFactoryBean commonEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(commonDataSource());
em.setPackagesToScan("com.member.batch.entities.common");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
final HashMap<String, Object> properties = hibernateProperties();
em.setJpaPropertyMap(properties);
return em;
}
#Bean(name = "commonTransactionManager")
public PlatformTransactionManager commonTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(commonEntityManager().getObject());
return transactionManager;
}
}
log db config
#Configuration
#EnableJpaRepositories(
basePackages = "com.member.batch.dao.log",
entityManagerFactoryRef = "logEntityManager",
transactionManagerRef = "logTransactionManager"
)
public class LogConfiguration {
// same format with common
}
member db config
#Configuration
#EnableJpaRepositories(
basePackages = "com.member.batch.dao.member",
entityManagerFactoryRef = "memberEntityManager",
transactionManagerRef = "memberTransactionManager"
)
public class MemberConfiguration {
// same format with common
}
and i have to use configures in my job
#Slf4j
#RequiredArgsConstructor
#Configuration
public class WithdrawMembersJobConfiguration {
public static final String WITHDRAW_MEMBERS_JOB = "withdrawMembersJob";
private final MessageSource messageSource;
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final CustomJobListener customJobListener;
private final ScheduleJobService scheduleJobService;
private final EntityManagerFactory entityManagerFactory;
#Bean
#Primary
public Job withdrawMembersJob() {
return jobBuilderFactory.get(WITHDRAW_MEMBERS_JOB)
.start(withdrawStep())
.listener(customJobListener)
.build();
}
#JobScope
#Bean(name = WITHDRAW_MEMBERS_JOB + "_step")
public Step withdrawStep() {
SimpleStepBuilder step = stepBuilderFactory.get("deleteExpiredMembersStep")
.<WithdrawMember, WithdrawMember>chunk(1000)
.reader(withdrawItemReader(null, null))
.processor(withdrawItemProcessor())
.writer(withdrawItemWriter());
return step.build();
}
#StepScope
#Bean(name = WITHDRAW_MEMBERS_JOB + "_reader")
public JpaPagingItemReader<WithdrawMember> withdrawItemReader(#org.springframework.beans.factory.annotation.Value("#{jobParameters[requestDate]}") String requestDate,
#org.springframework.beans.factory.annotation.Value("#{jobParameters[status]}") WithdrawStatus status) {
LocalDateTime scheduledDate = LocalDateTime.now().with(LocalTime.MAX);
log.info("scheduled date is " + scheduledDate);
if (status == null) {
status = WithdrawStatus.WSC001;
}
Map<String, Object> parameters = new HashMap<>();
parameters.put("scheduledDate", scheduledDate);
parameters.put("status", status);
return new JpaPagingItemReaderBuilder<WithdrawMember>()
.name("withdrawReader")
.parameterValues(parameters)
.entityManagerFactory(entityManagerFactory)
.queryString("select m from WithdrawMember m where m.scheduleDate < :scheduledDate and m.status = :status")
.pageSize(1000)
.build();
}
#StepScope
#Bean(name = WITHDRAW_MEMBERS_JOB + "_processor")
public ItemProcessor<WithdrawMember, WithdrawMember> withdrawItemProcessor() {
return new ItemProcessor<WithdrawMember, WithdrawMember>() {
#Override
public WithdrawMember process(WithdrawMember item) throws Exception {
log.info("withdraw info , " + item.getReason());
return item;
}
};
}
#StepScope
#Bean(name = WITHDRAW_MEMBERS_JOB + "_writer")
public JpaItemWriter withdrawItemWriter() {
return new JpaItemWriterBuilder<WithdrawMember>()
.entityManagerFactory(entityManagerFactory)
.build();
}
}
but now i am having getting error ,below is my console
2022-09-15 16:18:01.521 DEBUG 1552 --- [eduler_Worker-4] o.s.j.d.DataSourceTransactionManager : Creating new transaction with name [org.springframework.batch.core.repository.support.SimpleJobRepository.update]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2022-09-15 16:18:01.521 DEBUG 1552 --- [eduler_Worker-4] o.s.j.d.DataSourceTransactionManager : Acquired Connection [HikariProxyConnection#238016139 wrapping com.mysql.cj.jdbc.ConnectionImpl#3023ef72] for JDBC transaction
2022-09-15 16:18:01.521 DEBUG 1552 --- [eduler_Worker-4] o.s.j.d.DataSourceTransactionManager : Switching JDBC Connection [HikariProxyConnection#238016139 wrapping com.mysql.cj.jdbc.ConnectionImpl#3023ef72] to manual commit
2022-09-15 16:18:01.541 DEBUG 1552 --- [eduler_Worker-4] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query
2022-09-15 16:18:01.541 DEBUG 1552 --- [eduler_Worker-4] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT VERSION FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID=?]
2022-09-15 16:18:01.577 DEBUG 1552 --- [eduler_Worker-4] o.s.b.c.r.dao.JdbcJobExecutionDao : Truncating long message before update of JobExecution: JobExecution: id=1956, version=1, startTime=Thu Sep 15 16:18:00 KST 2022, endTime=Thu Sep 15 16:18:01 KST 2022, lastUpdated=Thu Sep 15 16:18:01 KST 2022, status=FAILED, exitStatus=exitCode=FAILED;exitDescription=javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:422)
at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:3397)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1354)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1349)
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:422) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:3397) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1354) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1349) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:362) ~[spring-orm-5.3.9.jar:5.3.9]
at com.sun.proxy.$Proxy119.flush(Unknown Source) ~[na:na]
at org.springframework.batch.item.database.JpaItemWriter.write(JpaItemWriter.java:94) ~[spring-batch-infrastructure-4.3.3.jar:4.3.3]
at org.springframework.batch.item.database.JpaItemWriter$$FastClassBySpringCGLIB$$29c4242e.invoke(<generated>) ~[spring-batch-infrastructure-4.3.3.jar:4.3.3]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.9.jar:5.3.9]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) ~[spring-aop-5.3.9.jar:5.3.9]
at
i don't know why i get this error.
i could get result of itemReader.
None of the classes you shared is annotated with #EnableBatchProcessing, so it is not clear which transaction manager is set on the StepBuilderFactory you use to create steps. The default transaction manager configured by #EnableBatchProcessing is a DataSourceTransactionManager, which knows nothing about the JPA context. Since you use JPA, so you need to configure a JpaTransactionManager in your step:
#JobScope
#Bean(name = WITHDRAW_MEMBERS_JOB + "_step")
public Step withdrawStep(JpaTransactionManager transactionManager) {
SimpleStepBuilder step = stepBuilderFactory.get("deleteExpiredMembersStep")
.<WithdrawMember, WithdrawMember>chunk(1000)
.transactionManager(transactionManager)
.reader(withdrawItemReader(null, null))
.processor(withdrawItemProcessor())
.writer(withdrawItemWriter());
return step.build();
}
If you use #EnableBatchProcessing, you need to provide a custom BatchConfigurer, something like:
#Bean
public BatchConfigurer batchConfigurer(DataSource dataSource, EntityManagerFactory entityManagerFactory) {
return new DefaultBatchConfigurer(dataSource) {
#Override
public PlatformTransactionManager getTransactionManager() {
return new JpaTransactionManager(entityManagerFactory);
}
};
}
I am trying to use transaction management in my code for the rollback scenario for two databases db2 and mysql if anyone fails I want transaction of both the databases should rollback, but it's failing after processing around 70 items in the loop, with HikariPool-1 - Connection is not available, request timed out after 30001ms.
Main Class :
public class Test {
#Autowired
#Qualifier("db2transactionmanager")
private DataSourceTransactionManager db2TransactionManager;
#Autowired
#Qualifier("mysqltransactionmanager")
private DataSourceTransactionManager mysqlTransactionManager;
#Autowired
private TransactionDefinition transactionDefinition;
public void mymethod() {
try {
TransactionStatus db2TransactionStatus = null;
TransactionStatus mysqlTransactionStatus = null;
// Some code
for (int i = 0; i <= 100; i++) {
if (//Valid Condition) {
//Getting error on below line
db2TransactionStatus = db2TransactionManager.getTransaction(transactionDefinition);
mysqlTransactionStatus = mysqlTransactionManager.getTransaction(transactionDefinition);
//Some code
db2TransactionStatus.createSavepoint();
// Updating the db2 table
db2TransactionStatus.createSavepoint();
mysqlTransactionStatus.createSavepoint();
// Updating the mysql table
if (//valid condition) {
if (db2TransactionStatus != null) {
db2TransactionManager.getTransaction(null);
db2TransactionManager.commit(db2TransactionStatus);
}
if (mysqlTransactionStatus != null) {
mysqlTransactionManager.getTransaction(null);
mysqlTransactionManager.commit(mysqlTransactionStatus);
}
}
}
}
}
application.properties:
//My sql
spring.mysql.datasource.url=${DB_URL}&serverTimezone=EST
spring.mysql.datasource.username=${DB_USER}
spring.mysql.datasource.password=${DB_PASSWORD}
spring.mysql.datasource.hikari.maximum-pool-size=3
spring.mysql.datasource.hikari.minimum-idle=1
spring.mysql.datasource.hikari.idle-timeout=10000
spring.mysql.datasource.hikari.show-sql=true
spring.mysql.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.mysql.datasource.autocommit=false
//DB2
spring.datasource.url=jdbc:db2://${DB2_SERVER}:${DB2_PORT}/${DB2_DSN}:retrieveMessagesFromServerOnGetMessage=true:allowNextOnExhaustedResultSet=1;
spring.datasource.username=${DB2_USER}
spring.datasource.password=${DB2_PASSWORD}
spring.datasource.driver-class-name=com.ibm.db2.jcc.DB2Driver
spring.datasource.autocommit=false
dbconfig class:
#Bean(name = "mysqltransactionmanager")
public DataSourceTransactionManager mysqlTranactionManager(#Qualifier("mysql") DataSource mysql)
{
LOGGER.info("Creating MYSQL JDBC Template with datasouce: {}", mysql);
return new DataSourceTransactionManager(mysql);
}
#Bean(name = "db2transactionmanager")
#Primary
public DataSourceTransactionManager db2TranactionManager(#Qualifier("db2") DataSource db2)
{
LOGGER.info("Creating db2 with datasouce: {}", db2);
return new DataSourceTransactionManager(db2);
}
#Bean(name = "transactionDefinition")
public TransactionDefinition transactionDefinitionNew() {
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
defaultTransactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
return defaultTransactionDefinition;
}
**Error** :
Could not open JDBC Connection for transaction; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30001ms.
org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30001ms.
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:305) ~[spring-jdbc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:430) ~[spring-tx-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:354) ~[spring-tx-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at com.process(Test.java:151) [classes/:?]
at com.run(MainTest.java:46) [classes/:?]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:813) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:797) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:324) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at com.main(MainTest.java:37) [classes/:?]
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30001ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:676) ~[HikariCP-3.2.0.jar:?]
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:190) ~[HikariCP-3.2.0.jar:?]
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:155) ~[HikariCP-3.2.0.jar:?]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128) ~[HikariCP-3.2.0.jar:?]
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:262) ~[spring-jdbc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
... 10 more
I´m creating an application with Spring boot + Quartz + Oracle and i would like to save the scheduling in the database (persistent, in case the server crashes). With RAMJobStore works fine, but when I try to use JobStoreTX it doesn't work, it always uses RAMJobStore, where colud be the problem?. I'm surely making a lot of mistakes, but it's my first application with spring boot + Quartz, can you give me an idea?
The events will be created dynamically, receiving the information in the controller.
application.yaml (In addition to Quartz, the application connects to the database to query tables, but the application and Quartz will use the same database)
hibernate:
globally_quoted_identifiers: true
show_sql: true
logging:
level:
org:
hibernate:
SQL: ${hibernate.logging}
pattern:
console: '%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n'
server:
port: ${port}
spring:
activemq:
broker-url: ${activemq-url}
password: ${activemq-password}
user: ${activemq-user}
datasource:
driver-class-name: ${driverClassName}
password: ${ddbb-password}
jdbcUrl: ${ddbb-url}
username: ${ddbb-user}
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.Oracle12cDialect
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: never
properties:
org:
quartz:
scheduler:
instanceId: AUTO
jobStore:
useProperties: true
isClustered: false
clusterCheckinInterval: 5000
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
dataSource: quartzDataSource
dataSource:
quartzDataSource:
driver: oracle.jdbc.driver.OracleDriver
URL: ${ddbb-url}
user: ${ddbb-user}
password: ${ddbb-password}
Class SchedulerConfiguration
#Configuration
public class SchedulerConfiguration {
#Bean
public SchedulerFactoryBean schedulerFactory(ApplicationContext applicationContext) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(new AutoWiringSpringBeanJobFactory());
return schedulerFactoryBean;
}
#Bean
public Scheduler scheduler(ApplicationContext applicationContext) throws SchedulerException {
Scheduler scheduler = schedulerFactory(applicationContext).getScheduler();
scheduler.start();
return scheduler;
}
#Bean
#QuartzDataSource
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource quartzDataSource() {
return DataSourceBuilder.create().build();
}
}
class AutoWiringSpringBeanJobFactor
public class AutoWiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware{
private transient AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
Job class
#Component
public class CampaignJob implements Job{
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("Hi, the job works");
}
}
The class where the scheduler it´s created
public class ManagerServiceImpl implements ManagerService {
#Autowired
private ProducerQueue producer;
#Autowired
private ManagementDatabase managementDatabase;
#Autowired
private Scheduler scheduler;
#Override
public String processCampaign(ScheduleCampaign scheduleCampaign) {
try {
ZonedDateTime dateTime = ZonedDateTime.of(scheduleCampaign.getDateTime(), scheduleCampaign.getTimeZone());
JobDetail jobDetail = buildJobDetail(scheduleCampaign);
Trigger trigger = buildJobTrigger(jobDetail, dateTime);
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
System.out.println("There was an error creating the scheduler: "+e);
}
return "Scheduler created";
}
private JobDetail buildJobDetail(ScheduleCampaign scheduleCampaign) {
JobDataMap jobDataMap = new JobDataMap();
System.out.println("Function: buildJobDetail - campaign value: "+scheduleCampaign.getCampaign());
jobDataMap.put("campaign", scheduleCampaign.getCampaign());
return JobBuilder.newJob(CampaignJob.class)
.withIdentity(UUID.randomUUID().toString(), "campaign-jobs")
.requestRecovery(true)
.storeDurably(true)
.withDescription("campaign job planned")
.usingJobData(jobDataMap)
.storeDurably()
.build();
}
private Trigger buildJobTrigger(JobDetail jobDetail, ZonedDateTime startAt) {
return TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity(jobDetail.getKey().getName(), "campaign-triggers")
.withDescription("campaign job Trigger")
.startAt(Date.from(startAt.toInstant()))
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withMisfireHandlingInstructionFireNow())
.build();
}
}
Logs
2020-12-28 16:16:08 INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
2020-12-28 16:16:09 INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
2020-12-28 16:16:09 INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2020-12-28 16:16:10 INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.4.23.Final
2020-12-28 16:16:10 INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor
2020-12-28 16:16:10 INFO o.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2020-12-28 16:16:10 INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.3.2 created.
2020-12-28 16:16:10 INFO org.quartz.simpl.RAMJobStore - RAMJobStore initialized.
2020-12-28 16:16:10 INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.3.2) 'schedulerFactory' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
Try to autowire your app default dataSource, if you are pointing the same DB for quartz jobs as well.
The below configuration worked for me with PostgreSQL
#Autowired
DataSource dataSource;
#Autowired
JobFactory jobFactory;
#Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(true);
factory.setDataSource(dataSource);
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
return factory;
}
#Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
My database has limited active connections which results in a HikariPool initialization exception as shown below for which I want to neglect the entire stack trace and catch the exception in my Main class.
Here's a stack trace of my exception log:
2020-11-18 16:27:16.619 INFO 9124 --- [ restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-11-18 16:27:18.344 ERROR 9124 --- [ restartedMain] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Exception during pool initialization.
java.sql.SQLSyntaxErrorException: User 6eX6BxR3TY already has more than 'max_user_connections' active connections
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:836)
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:456)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:246)
Here's the main class:
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class NseapiApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(NseapiApplication.class);
private static String splunkUrl;
#Value("${splunk.url}")
public void setSplunkUrl(String splunkUrl) {
NseapiApplication.splunkUrl = splunkUrl;
}
public static void main(String[] args) {
SpringApplication.run(NseapiApplication.class, args);
LOGGER.info("Forwarding logs to Splunk Cloud Instance : " + splunkUrl);
}
#Bean
public static BeanFactoryPostProcessor dependsOnPostProcessor() {
return bf -> {
String[] jpa = bf.getBeanNamesForType(EntityManagerFactory.class);
Stream.of(jpa).map(bf::getBeanDefinition).forEach(it -> it.setDependsOn("databaseStartupValidator"));
};
}
#Bean
public DatabaseStartupValidator databaseStartupValidator(DataSource dataSource) {
DatabaseStartupValidator dsv = new DatabaseStartupValidator();
dsv.setDataSource(dataSource);
dsv.setValidationQuery(DatabaseDriver.MYSQL.getValidationQuery());
return dsv;
}
}
Here's the Database Configuration class:
#Configuration
public class DatasourceConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(DatasourceConfig.class);
#Bean
public DataSource datasource() {
return DataSourceBuilder.create()
.driverClassName("com.mysql.cj.jdbc.Driver")
.url("jdbc:mysql://myDbUrl").username("myUserName").password("myPassword")
.build();
}
}
I use JHipster to make a simple app and write a service, code as follows:
#Service
#Transactional
public class OperateQueueActionService {
#Transactional(rollbackFor = Throwable.class)
public OperateQueueDTO apply(OperateQueueDTO operateQueueDTO, QueueEventType queueEventType, String deskNo) {
StateMachine<QueueStatus, QueueEventType> stateMachine = operateQueueActionMachineService.getStateMachine();
try { QueueEventDTO operateQueueEventDTO = operateQueueUtils.saveQueueEvent(operateQueueDTO, queueEventType, deskNo);
......in process will throw RuntimeException.
} finally {
stateMachine.stop();
}
}
}
I want the transaction to rollback and not save queueEvent, but the record is saved in the database. This is the transaction log:
[2018-07-24 12:04:51.861] [XNIO-2 task-6] WARN
o.s.s.l.CompositeStateMachineListener -Error during stateContext
java.lang.RuntimeException: 无效状态 at
com.higoee.queue.state.utils.OperateQueueStateMachineLogListener.stateContext(OperateQueueStateMachineLogListener.java:58)
at java.lang.Thread.run(Thread.java:745) [2018-07-24 12:04:51.871]
[XNIO-2 task-6] INFO c.h.q.s.u.OperateQueueStateMachineLogListener
-4d667539-3316-491d-a0db-240e29b0fcae状态机状态为:STATEMACHINE_STOP [2018-07-24 12:04:51.872] [XNIO-2 task-6] DEBUG
o.s.orm.jpa.JpaTransactionManager -Initiating transaction commit
[2018-07-24 12:04:51.874] [XNIO-2 task-6] DEBUG
o.s.orm.jpa.JpaTransactionManager -Committing JPA transaction on
EntityManager
JPA config have not problem,spring state machine had been catch the exception which was thrown in StateMachineListenerAdapter.