forked from spring-projects/spring-batch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MySQLJdbcJobRepositoryIntegrationTests.java
197 lines (172 loc) · 7.29 KB
/
MySQLJdbcJobRepositoryIntegrationTests.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
* Copyright 2020-2022 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
*
* https://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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.test.repository;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.sql.DataSource;
import com.mysql.cj.jdbc.MysqlDataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.support.SimpleJobOperator;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.jdbc.support.JdbcTransactionManager;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.transaction.PlatformTransactionManager;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author Mahmoud Ben Hassine
*/
@Testcontainers
@SpringJUnitConfig
class MySQLJdbcJobRepositoryIntegrationTests {
// TODO find the best way to externalize and manage image versions
// when implementing https://github.com/spring-projects/spring-batch/issues/3092
private static final DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:8.0.31");
@Container
public static MySQLContainer<?> mysql = new MySQLContainer<>(MYSQL_IMAGE);
@Autowired
private DataSource dataSource;
@Autowired
private JobLauncher jobLauncher;
@Autowired
private JobOperator jobOperator;
@Autowired
private Job job;
@BeforeEach
void setUp() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.addScript(new ClassPathResource("/org/springframework/batch/core/schema-mysql.sql"));
databasePopulator.execute(this.dataSource);
}
/*
* This test is for issue https://github.com/spring-projects/spring-batch/issues/2202:
* A round trip from a `java.util.Date` JobParameter to the database and back again
* should preserve fractional seconds precision, otherwise a different job instance is
* created while the existing one should be used.
*
* This test ensures that round trip to the database with a `java.util.Date` parameter
* ends up with a single job instance (with two job executions) being created and not
* two distinct job instances (with a job execution for each one).
*
* Note the issue does not happen if the parameter is of type Long (when using
* addLong("date", date.getTime()) for instance).
*/
@Test
void testDateMillisecondPrecision() throws Exception {
// given
Date date = new Date();
JobParameters jobParameters = new JobParametersBuilder().addDate("date", date).toJobParameters();
// when
JobExecution jobExecution = this.jobLauncher.run(this.job, jobParameters);
this.jobOperator.restart(jobExecution.getId()); // should load the date parameter
// with fractional seconds
// precision here
// then
List<Long> jobInstances = this.jobOperator.getJobInstances("job", 0, 100);
assertEquals(1, jobInstances.size());
List<Long> jobExecutions = this.jobOperator.getExecutions(jobInstances.get(0));
assertEquals(2, jobExecutions.size());
}
@Configuration
@EnableBatchProcessing
static class TestConfiguration {
@Bean
public DataSource dataSource() throws Exception {
MysqlDataSource datasource = new MysqlDataSource();
datasource.setURL(mysql.getJdbcUrl());
datasource.setUser(mysql.getUsername());
datasource.setPassword(mysql.getPassword());
datasource.setUseSSL(false);
return datasource;
}
@Bean
public JdbcTransactionManager transactionManager(DataSource dataSource) {
return new JdbcTransactionManager(dataSource);
}
@Bean
public Job job(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new JobBuilder("job", jobRepository)
.start(new StepBuilder("step", jobRepository).tasklet((contribution, chunkContext) -> {
throw new Exception("expected failure");
}, transactionManager).build()).build();
}
@Bean
public JobOperator jobOperator(JobLauncher jobLauncher, JobRegistry jobRegistry, JobExplorer jobExplorer,
JobRepository jobRepository) {
SimpleJobOperator jobOperator = new SimpleJobOperator();
jobOperator.setJobExplorer(jobExplorer);
jobOperator.setJobLauncher(jobLauncher);
jobOperator.setJobRegistry(jobRegistry);
jobOperator.setJobRepository(jobRepository);
return jobOperator;
}
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
return jobRegistryBeanPostProcessor;
}
@Bean
public ConfigurableConversionService conversionService() {
DefaultConversionService conversionService = new DefaultConversionService();
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
conversionService.addConverter(String.class, Date.class, new Converter<String, Date>() {
@Override
public Date convert(String source) {
try {
return dateFormat.parse(source);
}
catch (ParseException e) {
throw new RuntimeException(e);
}
}
});
conversionService.addConverter(Date.class, String.class, new Converter<Date, String>() {
@Override
public String convert(Date source) {
return dateFormat.format(source);
}
});
return conversionService;
}
}
}