Hi I am new to Spring Batch and I referred this link to run the sample program.
I wanted to insert the large amount of CSV file data into MySQL database and through the example I am able to execute it fine with limited number of lines of CSV data.
But when I inserted more number of lines into CSV file and execute the batch program it was not inserting any data in other words it was not reading the file itself and not even showing any error/exception(s). I think I need to configure ItemReader to read for more than one time.
Following is the BatchConfiguration.java code
#Configuration
#EnableBatchProcessing
public class BatchConfiguration {
#Bean
public ItemReader<Student> reader() {
FlatFileItemReader<Student> reader = new FlatFileItemReader<Student>();
reader.setResource(new ClassPathResource("student-data.csv"));
reader.setLineMapper(new DefaultLineMapper<Student>() {{
setLineTokenizer(new DelimitedLineTokenizer() {{
setNames(new String[] {"stdId", "subMarkOne", "subMarkTwo" });
}});
setFieldSetMapper(new BeanWrapperFieldSetMapper<Student>() {{
setTargetType(Student.class);
}});
}});
return reader;
}
#Bean
public ItemWriter<Marksheet> writer(DataSource dataSource) {
JdbcBatchItemWriter<Marksheet> writer = new JdbcBatchItemWriter<Marksheet>();
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Marksheet>());
writer.setSql("INSERT INTO marksheet (studentId,totalMark) VALUES (:stdId,:totalSubMark)");
writer.setDataSource(dataSource);
return writer;
}
#Bean
public ItemProcessor<Student, Marksheet> processor() {
return new StudentItemProcessor();
}
#Bean
public Job createMarkSheet(JobBuilderFactory jobs, Step step) {
return jobs.get("createMarkSheet")
.flow(step)
.end()
.build();
}
#Bean
public Step step(StepBuilderFactory stepBuilderFactory, ItemReader<Student> reader,
ItemWriter<Marksheet> writer, ItemProcessor<Student, Marksheet> processor) {
return stepBuilderFactory.get("step")
.<Student, Marksheet> chunk(5)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Bean
public DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/concretepage");
dataSource.setUsername("root");
dataSource.setPassword("root123");
return dataSource;
}
main class to run
#ComponentScan
#EnableAutoConfiguration
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Main.class, args);
List<Marksheet> result = ctx.getBean(JdbcTemplate.class).query("select studentId,totalMark FROM marksheet",
new RowMapper<Marksheet>() {
#Override
public Marksheet mapRow(ResultSet rs, int row) throws SQLException {
return new Marksheet(rs.getString(1), Integer.parseInt(rs.getString(2)));
}
});
System.out.println("Number of Record:"+result.size());
}
}
process class
public class StudentItemProcessor implements ItemProcessor<Student, Marksheet> {
#Override
public Marksheet process(final Student student) throws Exception {
int totalMark = student.getSubMarkOne()+student.getSubMarkTwo();
System.out.println("student id:"+student.getStdId() +" and Total mark:"+ totalMark);
Marksheet marksheet = new Marksheet(student.getStdId(), totalMark);
return marksheet;
}
}
Any help will be appreciated. thanks.
Related
I tried to implement an example of Spring Boot Batch Processing from db to csv.
The issue which I cannot solve is based on not sorting all values by id in csv file as well as showing column titles.
Here is the output in csv file. (First value is related with id)
3,9,2013-04-15,Japan,jheino3#mayoclinic.com,Jard,MALE,Heino,87cdda81-45d0-451a-a62f-f8450eae1b64
2,23,1999-01-25,Panama,nloynes2#woothemes.com,Natala,FEMALE,Loynes,24be24e6-525f-42de-855d-52d4fef21608
6,9,2013-02-16,China,rcossans4#harvard.edu,Roseline,FEMALE,Cossans,06f70b0d-2c98-4f46-b933-528499ab91b3
4,8,2014-05-09,Indonesia,jcarlaw1#t.co,Jilleen,FEMALE,Carlaw,c722e6d5-9024-49c5-80e0-c2555f1eb9cc
1,22,2000-08-15,China,gspearing0#flickr.com,Ginnie,FEMALE,Spearing,fa26fa96-97d3-4e8e-856a-fdf07499e13e
5,22,2000-03-18,Indonesia,rgillino6#china.com.cn,Rainer,MALE,Gillino,5302a199-f313-4a24-9550-d643001d9faf
I want all values are sorted by id.
How can I do that?
Here is the batch configuration file shown below.
#Configuration // Informs Spring that this class contains configurations
#EnableBatchProcessing // Enables batch processing for the application
#RequiredArgsConstructor
public class BatchConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final UserRepository userRepository;
Date now = new Date(); // java.util.Date, NOT java.sql.Date or java.sql.Timestamp!
String format1 = new SimpleDateFormat("yyyy-MM-dd'-'HH-mm-ss-SSS",Locale.forLanguageTag("tr-TR")).format(now);
private Resource outputResource = new FileSystemResource("output/customers_" + format1 + ".csv");
#Bean
public RepositoryItemReader<User> reader(){
RepositoryItemReader<User> repositoryItemReader = new RepositoryItemReader<>();
repositoryItemReader.setRepository(userRepository);
repositoryItemReader.setMethodName("findAll");
final HashMap<String, Sort.Direction> sorts = new HashMap<>();
sorts.put("id", Sort.Direction.ASC);
repositoryItemReader.setSort(sorts);
return repositoryItemReader;
}
#Bean
public FlatFileItemWriter<User> writer() {
FlatFileItemWriter<User> writer = new FlatFileItemWriter<>();
writer.setResource(outputResource);
writer.setAppendAllowed(true);
writer.setLineAggregator(new DelimitedLineAggregator<User>() {
{
setDelimiter(",");
setFieldExtractor(new BeanWrapperFieldExtractor<User>() {
{
setNames(new String[]{"id", "age", "birthday", "country", "email", "firstName", "gender", "lastName", "personId"});
}
});
}
});
return writer;
}
#Bean
public UserProcessor processor() {
return new UserProcessor();
}
#Bean
public UserJobExecutionNotificationListener stepExecutionListener() {
return new UserJobExecutionNotificationListener(userRepository);
}
#Bean
public UserStepCompleteNotificationListener jobExecutionListener() {
return new UserStepCompleteNotificationListener();
}
#Bean
public Step step1() {
return stepBuilderFactory.get("csv-step").<User, User>chunk(10)
.reader(reader())
.processor(processor())
.writer(writer())
.listener(stepExecutionListener())
.taskExecutor(taskExecutor())
.build();
}
#Bean
public Job runJob() {
return jobBuilderFactory.get("importuserjob")
.listener(jobExecutionListener())
.flow(step1()).end().build();
}
#Bean
public TaskExecutor taskExecutor() {
SimpleAsyncTaskExecutor asyncTaskExecutor = new SimpleAsyncTaskExecutor();
asyncTaskExecutor.setConcurrencyLimit(10);
return asyncTaskExecutor;
}
}
Here is the link of example : Link
After I added FlatFileHeaderCallback into FlatFileItemWriter, I fixed the issue.
Here is the code snippets shown below.
writer.setHeaderCallback(new FlatFileHeaderCallback() {
#Override
public void writeHeader(Writer writer) throws IOException {
for(int i=0;i<headers.length;i++){
if(i!=headers.length-1)
writer.append(headers[i] + ",");
else
writer.append(headers[i]);
}
}
});
Im polling files from 2 different directories in 1 server using RotatingServerAdvice and that´s working fine, the problem is that I can´t stop polling once time I start the inboundtest.start (). The main idea is retrive all the files in those directories, and then send inboundtest.stop (), this is the code.
#Bean
public SessionFactory<LsEntry> sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(false);
factory.setHost(host);
factory.setPort(port);
factory.setUser(user);
factory.setPassword(password);
factory.setAllowUnknownKeys(true);
//factory.setTestSession(true);
return factory;
}
#Bean
public SftpInboundFileSynchronizer sftpInboundFileSynchronizer() {
SftpInboundFileSynchronizer fileSynchronizer = new SftpInboundFileSynchronizer(sftpSessionFactory());
fileSynchronizer.setDeleteRemoteFiles(true);
fileSynchronizer.setRemoteDirectory(sftpRemoteDirectory);
fileSynchronizer.setFilter(new SftpRegexPatternFileListFilter(".*?\\.(txt|TXT?)"));
return fileSynchronizer;
}
#Bean(name = "sftpMessageSource")
#EndpointId("inboundtest")
#InboundChannelAdapter(channel = "sftpChannel",poller = #Poller("fileReadingMessageSourcePollerMetadata"), autoStartup = "false")
public MessageSource<File> sftpMessageSource() {
SftpInboundFileSynchronizingMessageSource source =
new SftpInboundFileSynchronizingMessageSource(sftpInboundFileSynchronizer());
source.setLocalDirectory(new File(sftpLocalDirectoryDownloadUpload));
source.setAutoCreateLocalDirectory(true);
source.setLocalFilter(new AcceptOnceFileListFilter<File>());
return source;
}
#Bean
public DelegatingSessionFactory<LsEntry> sessionFactory() {
Map<Object, SessionFactory<LsEntry>> factories = new LinkedHashMap<>();
factories.put("one", sftpSessionFactory());
// use the first SF as the default
return new DelegatingSessionFactory<LsEntry>(factories, factories.values().iterator().next());
}
#Bean
public RotatingServerAdvice advice() {
List<RotationPolicy.KeyDirectory> keyDirectories = new ArrayList<>();
keyDirectories.add(new RotationPolicy.KeyDirectory("one", sftpRemoteDirectory));
keyDirectories.add(new RotationPolicy.KeyDirectory("one", sftpRemoteDirectoryNonUpload));
return new RotatingServerAdvice(sessionFactory(), keyDirectories, false);
}
#Bean
MessageChannel controlChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "controlChannel")
ExpressionControlBusFactoryBean controlBus() {
return new ExpressionControlBusFactoryBean();
}
#Bean
public PollerMetadata fileReadingMessageSourcePollerMetadata() {
PollerMetadata meta = new PollerMetadata();
meta.setTrigger(new PeriodicTrigger(1000));
meta.setAdviceChain(List.of(advice()));
meta.setMaxMessagesPerPoll(1);
meta.setErrorHandler(throwable -> new IOException());
return meta;
}
Allways is waiting for a new file in one of the 2 directories, but thats no the idea, the idea is stop polling when all the files be retrived
From another class I call inbound.start() trouhg the control chanel here the code:
#Autowired
private MessageChannel controlChannel;
public void startProcessingFiles() throws InterruptedException {
controlChannel.send(new GenericMessage<>("#inboundtest.start()"));
}
I was tryong stop with this class but doesn´t works
#Component
public class StopPollingAdvice implements ReceiveMessageAdvice {
#Autowired
private MessageChannel controlChannel;
#Override
public Message<?> afterReceive(Message<?> message, Object o) {
System.out.println("There is no more files, stopping connection" + message.getPayload());
if(message == null) {
System.out.println("There is no more files, stopping connection" + message.getPayload());
Message operation = MessageBuilder.withPayload("#inboundtest.stop()").build();
controlChannel.send(operation);
}
return message;
}
}
OK. Now I see your point. The RotatingServerAdvice does move to other server only when the first is exhausted (by default, see that fair option). So, when you stop it in the advice it cannot go to other dir for fetching any more. You need to think about some other stopping solution. Something what is not tied to the advice and this afterReceive(), somewhere downstream in your flow...
Or you can provide a custom RotationPolicy (extension of StandardRotationPolicy) and in its overridden afterReceive() check for all the dirs processed and then send stop command.
I'm new to spring batch and I'm trying to learn about it.
I implemented datasource and batch configuration but it doesn't seem to work, because
Exception in thread "main" org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is java.sql.SQLSyntaxErrorException: Table '$tableName.batch_job_instance' doesn't exist
DataSourceConfiguration class :
#Configuration
public class DataSourceConfiguration {
#Bean
public DataSource mysqlDataSource() throws SQLException {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriver(new com.mysql.cj.jdbc.Driver());
dataSource.setUrl("jdbc:mysql://localhost:3306/task4");
dataSource.setUsername("name");
dataSource.setPassword("password");
return dataSource;
}
}
And I have Batch configuration class, but I dont know #EnableBatchProcessing annotation works without springboot
#Configuration
#EnableBatchProcessing
#Import(DataSourceConfiguration.class)
public class BatchConfiguration {
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Autowired
private DataSource dataSource;
#Bean
public ItemReader<User> itemReader() {
return new JdbcCursorItemReaderBuilder<User>()
.dataSource(this.dataSource)
.name("creditReader")
.sql("select userLogin, userEmail, userPassword, accountBalance from userInfo")
.rowMapper(new UserRowMapper())
.build();
}
#Bean
public ItemWriter<User> simpleItemWriter(){
return new SimpleUserWriter();
}
#Bean
public Job sampleJob() {
return this.jobs.get("sampleJob")
.start(step1())
.build();
}
#Bean
public Step step1() {
return this.steps.get("step1")
.<User,User>chunk(10)
.reader(itemReader())
.writer(simpleItemWriter())
.build();
}
}
Main class contains those lines of code :
public class Demo {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new AnnotationConfigApplicationContext(BatchConfiguration.class);
context.getBean("jobRepository");
JobLauncher jobLauncher = context.getBean("jobLauncher",JobLauncher.class);
Job job = context.getBean("sampleJob", Job.class);
try {
JobExecution execution = jobLauncher.run(job, new JobParameters());
System.out.println("Job Exit Status : "+ execution.getStatus());
} catch (JobExecutionException e) {
System.out.println("Job ExamResult failed");
e.printStackTrace();
}
}
}
How can I solve this problem?Thanks for helping
The error seems to say that you are missing the tables needed by Spring batch.
Check https://docs.spring.io/spring-batch/docs/4.3.x/reference/html/schema-appendix.html
You can also find SQL files ready for the different DB engine under org/springframework/bacth/core/schema-<DB>.sql in the spring batch core jar file
I am using both Spring Boot and Batch in a maven multi-module project for parsing CSV files and storing data in a MySQL database.
When running the batch module using my BatchLauncher class (shared below) I get a BeanCurrentlyInCreationException caused by getDataBase() which I use for configuring my MySQL database. (click this link to see logs)
And when I remove this method Spring Boot choose automatically an embedded database of type H2 (link for logs)
BatchLauncher class :
#Slf4j
public class BatchLauncher {
public static void main(String[] args) {
try {
Launcher.launchWithConfig("My Batch", BatchConfig.class, false);
}catch (Exception ex) {
log.error(ex.getMessage());
}
}
}
Launcher class :
#Slf4j
public class Launcher {
private Launcher() {}
public static void launchWithConfig(String batchName, Class<?> configClass, boolean oncePerDayMax) throws JobExecutionException, BatchException {
try {
// Check the spring profiles used
log.info("Start batch \"" + batchName + "\" with profiles : " + System.getProperty("spring.profiles.active"));
// Load configuration
#SuppressWarnings("resource")
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configClass);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job = context.getBean(Job.class);
//Authorize only one execution of each job per day
JobParameters jobParameters = new JobParameters();
JobExecution execution = jobLauncher.run(job, jobParameters);
if(!BatchStatus.COMPLETED.equals(execution.getStatus())) {
throw new BatchException("Unknown error while executing batch : " + batchName);
}
}catch (Exception ex){
log.error("Exception",ex);
throw new BatchException(ex.getMessage());
}
}
}
BatchConfig class :
#Slf4j
#Configuration
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
#EnableBatchProcessing
#ComponentScan(basePackages = {
"fr.payet.flad.batch.tasklet",
"fr.payet.flad.batch.mapper"
})
#Import({CoreConfig.class})
public class BatchConfig {
private StepBuilderFactory steps;
private JobBuilderFactory jobBuilderFactory;
private ReadInputTasklet readInputTasklet;
public BatchConfig(StepBuilderFactory steps, JobBuilderFactory jobBuilderFactory, ReadInputTasklet readInputTasklet) {
this.steps = steps;
this.jobBuilderFactory = jobBuilderFactory;
this.readInputTasklet = readInputTasklet;
}
#Bean
public DataSource getDataBase(){
return DataSourceBuilder
.create()
.driverClassName("com.mysql.jdbc.Driver")
.url("jdbc:mysql://localhost:3306/myDb?useSSL=false")
.username("myuser")
.password("mypwd")
.build();
}
#Bean
public Step readInputStep() {
return steps.get("readInputStep")
.tasklet(readInputTasklet)
.build();
}
#Bean
public Job readCsvJob() {
return jobBuilderFactory.get("readCsvJob")
.incrementer(new RunIdIncrementer())
.flow(readInputStep())
.end()
.build();
}
}
The solution was to create a custom DataSourceConfiguration class annotated with #Configuration in which I set my own database like this :
#Bean
public DataSource getDataBase(){
return DataSourceBuilder
.create()
.driverClassName("com.mysql.jdbc.Driver")
.url("jdbc:mysql://localhost:3306/myDB?useSSL=false")
.username("myUser")
.password("myPwd")
.build();
}
First the stacktrace:
org.springframework.dao.InvalidDataAccessApiUsageException:
Exception Description: No transaction is currently active; nested exception is javax.persistence.TransactionRequiredException:
Exception Description: No transaction is currently active
org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:316)
org.springframework.orm.jpa.DefaultJpaDialect.translateExceptionIfPossible(DefaultJpaDialect.java:121)
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
com.sun.proxy.$Proxy299.saveAndFlush(Unknown Source)
I'm pretty sure it's a stupid error but I can't seem to find what I am doing wrong:
The #Service:
#Service
public class BookInfoTracker implements InfoTracker
{
#Autowired
#Qualifier("bookTrackerRepository")
private TrackerRepository bookTrackerRepository;
public BookInfoTracker() {}
public BookInfoTracker (TrackerRepository bookTrackerRepository)
{
this.bookTrackerRepository = bookTrackerRepository;
}
#Transactional
#Override
public void track (long idBooking, String idLeg, String bookingEmail, int sequence, String event)
{
BookInfo ci = new BookInfo(idBooking, idLeg, bookingEmail, new Date(), sequence, event);
bookTrackerRepository.saveAndFlush(ci);
}
#Override
public int getSequenceFrom (long idBooking, String idLeg)
{
BookInfo tracked = findLastTrackedBookFrom(bookTrackerRepository.getLastTrackedBookByIdBookingAndIdLeg(idBooking, idLeg));
return null == tracked? 0 : tracked.getSequence()+1;
}
private BookInfo findLastTrackedBookFrom (List<BookInfo> trackedBooks)
{
return trackedBooks.isEmpty() ? null : trackedBooks.get(0);
}
}
There is a base class for jpa configuration:
#EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
public abstract class JpaConfiguration
{
public JpaConfiguration ()
{
super();
}
public abstract LocalContainerEntityManagerFactoryBean buildEntityManagerFactory () throws SQLException;
#Bean(name = "transactionManager")
public PlatformTransactionManager buildTransactionManager () throws SQLException
{
JpaTransactionManager manager = new JpaTransactionManager();
LocalContainerEntityManagerFactoryBean factory = buildEntityManagerFactory();
manager.setEntityManagerFactory(factory.getObject());
return manager;
}
protected Database toDatabase (String databaseProductName)
{
for (Database database : Database.values())
if (databaseProductName.equalsIgnoreCase(database.toString())) return database;
return null;
}
protected LocalContainerEntityManagerFactoryBean initializeFactory (DataSource datasource, String persistenceUnitName, String... packagesToScan) throws SQLException
{
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(datasource);
factory.setPersistenceUnitName(persistenceUnitName);
factory.setPackagesToScan(packagesToScan);
EclipseLinkJpaVendorAdapter jpaAdaptor = new EclipseLinkJpaVendorAdapter();
jpaAdaptor.setDatabase(toDatabase(datasource.getConnection().getMetaData().getDatabaseProductName()));
jpaAdaptor.setShowSql(true);
factory.setJpaVendorAdapter(jpaAdaptor);
Properties jpaProperties = new Properties();
jpaProperties.put("eclipselink.ddl-generation", "none");
jpaProperties.put("eclipselink.ddl-generation.output-mode", "database");
jpaProperties.put("eclipselink.logging.level.sql", "FINE");
jpaProperties.put("eclipselink.logging.parameters", "true");
jpaProperties.put("eclipselink.cache.shared.default", "false");
jpaProperties.put("eclipselink.target-database", "MySQL");
jpaProperties.put("eclipselink.weaving","false");
factory.setJpaProperties(jpaProperties);
factory.afterPropertiesSet();
return factory;
}
}
and then the specific configuration, a subclass:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"com.book.tracking.repositories"}, entityManagerFactoryRef="bookManagerFactory")
#Profile({"integration","test","release"})
public class JpaBookConfiguration extends JpaConfiguration
{
#Autowired
#Qualifier("bookDs")
private DataSource datasource;
#Override
#Bean(name = "bookManagerFactory")
public LocalContainerEntityManagerFactoryBean buildEntityManagerFactory () throws SQLException
{
return initializeFactory(datasource, "book.jpa", "com.book.tracking.entity");
}
}
then the restservice:
#Controller
#ControllerAdvice
#RequestMapping("/bookservice")
public class BookRestService implements IBookService
{
private static final String LEG_VALID_PATTERN = "^(20)\\d\\d(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])_[A-Z]{3}-[A-Z]{3}$";
#Autowired
#Qualifier("bookBuilder")
private IBookBuilder bookBuilder;
#Autowired
#Qualifier("bookInfoTracker")
private InfoTracker bookInfoTracker;
#Autowired
#Qualifier("bookBookingEventsGenerator")
private BookingEventsGenerator bookBookingEventsGenerator;
public BookRestService (IBookBuilder bookBuilder,
InfoTracker infoTracker, BookingEventsGenerator bookBookingEventsGenerator)
{
this.bookBookingEventsGenerator = bookBookingEventsGenerator;
this.bookBuilder = bookBuilder;
this.bookInfoTracker = infoTracker;
}
public BookRestService ()
{
}
#Override
#RequestMapping(value="/generate/{idBooking}/{idLeg}")
public ResponseEntity<String> generateOneWayEvent (#PathVariable("idBooking") final long idBooking, #PathVariable("idLeg") final String idLeg)
{
return executeEventCommand(new EventCommand() {
#Override
public BookingEvent execute() {
BookingEvent event = bookBookingEventsGenerator.generateEventFrom(idBooking, idLeg);
return event;
}
}, idBooking, idLeg, "PUBLISH");
}
private ResponseEntity<String> executeEventCommand (EventCommand eventCommand, long idBooking, String idLeg, String operation)
{
try {
validate(idLeg);
logFor(idBooking, idLeg, "generateOneWayEvent");
HttpHeaders headers = modifyResponseAccordingTo(idLeg);
BookingEvent event = eventCommand.execute();
int sequence = bookInfoTracker.getSequenceFrom(idBooking, event.getBookingIdentifier());
String bookIcs = generateBookFrom(event, sequence);
logger.debug(bookInfoTracker.getClass());
//exception here
bookInfoTracker.track(idBooking, event.getBookingIdentifier(), bookBookingEventsGenerator.getContactEmail(), sequence, operation);
return new ResponseEntity<String>(bookIcs, headers, HttpStatus.OK );
} catch(LegNotFoundException lnfe){
logger.error(lnfe);
return new ResponseEntity<String>("Something went wrong", HttpStatus.NOT_FOUND);
}
}
/** other methods **/
}
It seems like it's not considering #Transactional annotation maybe? But I used #EnableTransactionManagement. Can you see something wrong in my configuration?
UPDATE:
#Configuration
#Profile({"integration", "release"})
public class IBookSpringConfiguration
{
#Autowired
#Qualifier("bookRestService")
private IBookService bookRestService;
#Autowired
#Qualifier("bookTrackerRepository")
private TrackerRepository bookTrackerRepository;
public ReloadableResourceBundleMessageSource getMessageBundle(){
ReloadableResourceBundleMessageSource bundle = new ReloadableResourceBundleMessageSource();
bundle.setBasename("book-message");
return bundle;
}
#Bean(name = "bookInfoTracker")
public InfoTracker getBookTrackingService(){
return new BookInfoTracker(bookTrackerRepository);
}
#Bean(name = "bookBuilder")
public IBookBuilder getBookBookingService(){
return new BookBuilder(getMessageBundle());
}
#Bean(name = "i18n")
public I18nUtilACL getI18n(){
return new I18nUtilBook(MultiSourceSiteCustomizer.getInstance());
}
#Bean(name = "bookBookingEventsGenerator")
public BookingEventsGenerator getBookBookingEventsGenerator(){
return new BookEventsGenerator();
}
#Bean(name = "bookBookingLegFactory")
public BookingLegEventsGenerator getBookBookingLegFactory(){
return new BookBookingLegEventsGenerator();
}
#Bean(name = "bookBookingFlightEventsGenerator")
public BookingEventsGenerator getBookBookingFlightEventsGenerator(){
return new BookEventsGenerator();
}
#Bean(name = "bookRestService")
#Scope("singleton")
public IBookService getBookRestService ()
{
return new BookRestService();
}
}
SOLVED BUT DON'T KNOW WHY:
I just merge parent and subclass of the JpaConfiguration in JpaBookConfiguration. Now it's working. Don't know why. Can you explain?