Skip to content
Snippets Groups Projects
Commit 9d69571f authored by Robert Goldmann's avatar Robert Goldmann
Browse files

#663 - migrator: migrate images

parent 80ce51e0
No related branches found
No related tags found
No related merge requests found
Showing
with 345 additions and 16 deletions
package de.deadlocker8.budgetmaster.databasemigrator; package de.deadlocker8.budgetmaster.databasemigrator;
import de.deadlocker8.budgetmaster.databasemigrator.destination.category.DestinationCategory; import de.deadlocker8.budgetmaster.databasemigrator.destination.category.DestinationCategory;
import de.deadlocker8.budgetmaster.databasemigrator.destination.image.DestinationImage;
import de.deadlocker8.budgetmaster.databasemigrator.listener.GenericChunkListener;
import de.deadlocker8.budgetmaster.databasemigrator.listener.GenericStepListener;
import de.deadlocker8.budgetmaster.databasemigrator.source.category.SourceCategory; import de.deadlocker8.budgetmaster.databasemigrator.source.category.SourceCategory;
import de.deadlocker8.budgetmaster.databasemigrator.steps.category.*; import de.deadlocker8.budgetmaster.databasemigrator.source.image.SourceImage;
import de.deadlocker8.budgetmaster.databasemigrator.steps.category.CategoryProcessor;
import de.deadlocker8.budgetmaster.databasemigrator.steps.category.CategoryReader;
import de.deadlocker8.budgetmaster.databasemigrator.steps.category.CategoryWriter;
import de.deadlocker8.budgetmaster.databasemigrator.steps.image.ImageProcessor;
import de.deadlocker8.budgetmaster.databasemigrator.steps.image.ImageReader;
import de.deadlocker8.budgetmaster.databasemigrator.steps.image.ImageWriter;
import org.springframework.batch.core.Job; import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step; import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
...@@ -21,15 +30,22 @@ public class BatchConfiguration ...@@ -21,15 +30,22 @@ public class BatchConfiguration
final StepBuilderFactory stepBuilderFactory; final StepBuilderFactory stepBuilderFactory;
final JobExplorer jobExplorer; final JobExplorer jobExplorer;
final ImageReader imageReader;
final ImageWriter imageWriter;
final ImageProcessor imageProcessor;
final CategoryReader categoryReader; final CategoryReader categoryReader;
final CategoryWriter categoryWriter; final CategoryWriter categoryWriter;
final CategoryProcessor categoryProcessor; final CategoryProcessor categoryProcessor;
public BatchConfiguration(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, JobExplorer jobExplorer, CategoryReader categoryReader, CategoryWriter categoryWriter, CategoryProcessor categoryProcessor) public BatchConfiguration(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, JobExplorer jobExplorer, ImageReader imageReader, ImageWriter imageWriter, ImageProcessor imageProcessor, CategoryReader categoryReader, CategoryWriter categoryWriter, CategoryProcessor categoryProcessor)
{ {
this.jobBuilderFactory = jobBuilderFactory; this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory; this.stepBuilderFactory = stepBuilderFactory;
this.jobExplorer = jobExplorer; this.jobExplorer = jobExplorer;
this.imageReader = imageReader;
this.imageWriter = imageWriter;
this.imageProcessor = imageProcessor;
this.categoryReader = categoryReader; this.categoryReader = categoryReader;
this.categoryWriter = categoryWriter; this.categoryWriter = categoryWriter;
this.categoryProcessor = categoryProcessor; this.categoryProcessor = categoryProcessor;
...@@ -40,8 +56,21 @@ public class BatchConfiguration ...@@ -40,8 +56,21 @@ public class BatchConfiguration
{ {
return jobBuilderFactory.get("Migrate from h2 to postgresql") return jobBuilderFactory.get("Migrate from h2 to postgresql")
.incrementer(new RunIdIncrementer()) .incrementer(new RunIdIncrementer())
.flow(createStepForCategoryMigration()) .start(createStepForImageMigration())
.end() .next(createStepForCategoryMigration())
.build();
}
@Bean
public Step createStepForImageMigration()
{
return stepBuilderFactory.get("Migrate images")
.<SourceImage, DestinationImage>chunk(1)
.reader(imageReader)
.processor(imageProcessor)
.writer(imageWriter)
.listener(new GenericChunkListener("image"))
.listener(new GenericStepListener("images"))
.build(); .build();
} }
...@@ -53,8 +82,8 @@ public class BatchConfiguration ...@@ -53,8 +82,8 @@ public class BatchConfiguration
.reader(categoryReader) .reader(categoryReader)
.processor(categoryProcessor) .processor(categoryProcessor)
.writer(categoryWriter) .writer(categoryWriter)
.listener(new CategoryChunkListener()) .listener(new GenericChunkListener("category"))
.listener(new CategoryStepListener()) .listener(new GenericStepListener("categories"))
.build(); .build();
} }
} }
...@@ -34,7 +34,7 @@ public class SchedulerConfig ...@@ -34,7 +34,7 @@ public class SchedulerConfig
this.job = job; this.job = job;
} }
@Scheduled(fixedDelay = Long.MAX_VALUE, initialDelay = 2000) @Scheduled(fixedDelay = Long.MAX_VALUE, initialDelay = 1000)
public void scheduleByFixedRate() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException public void scheduleByFixedRate() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException
{ {
LOGGER.info("Starting migration..."); LOGGER.info("Starting migration...");
......
package de.deadlocker8.budgetmaster.databasemigrator.destination.image;
import javax.persistence.*;
@Entity
@Table(name = "image")
public class DestinationImage
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer ID;
@Lob
private byte[] image;
@Column(name = "file_name")
private String fileName;
@Column(name = "file_extension")
private Integer fileExtension;
public DestinationImage(byte[] image, String fileName, Integer fileExtension)
{
this.image = image;
this.fileName = fileName;
this.fileExtension = fileExtension;
}
public DestinationImage()
{
}
public Integer getID()
{
return ID;
}
public void setID(Integer ID)
{
this.ID = ID;
}
public byte[] getImage()
{
return image;
}
public void setImage(byte[] image)
{
this.image = image;
}
public String getFileName()
{
return fileName;
}
public void setFileName(String fileName)
{
this.fileName = fileName;
}
public Integer getFileExtension()
{
return fileExtension;
}
public void setFileExtension(Integer fileExtension)
{
this.fileExtension = fileExtension;
}
@Override
public String toString()
{
return "SourceImage{" +
"ID=" + ID +
", image='" + image + '\'' +
", fileName='" + fileName + '\'' +
", fileExtension='" + fileExtension + '\'' +
'}';
}
}
\ No newline at end of file
package de.deadlocker8.budgetmaster.databasemigrator.destination.image;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DestinationImageRepository extends JpaRepository<DestinationImage, Integer>
{
}
package de.deadlocker8.budgetmaster.databasemigrator.steps.category; package de.deadlocker8.budgetmaster.databasemigrator.listener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ChunkListener; import org.springframework.batch.core.ChunkListener;
import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.scope.context.ChunkContext;
public class CategoryChunkListener implements ChunkListener public class GenericChunkListener implements ChunkListener
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(CategoryChunkListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(GenericChunkListener.class);
private final String itemName;
private int numberOfProcessedItems = 0; private int numberOfProcessedItems = 0;
public GenericChunkListener(String itemName)
{
this.itemName = itemName;
}
@Override @Override
public void beforeChunk(ChunkContext context) public void beforeChunk(ChunkContext context)
{ {
...@@ -24,7 +31,7 @@ public class CategoryChunkListener implements ChunkListener ...@@ -24,7 +31,7 @@ public class CategoryChunkListener implements ChunkListener
if(count > numberOfProcessedItems) if(count > numberOfProcessedItems)
{ {
numberOfProcessedItems++; numberOfProcessedItems++;
LOGGER.info("Migrating category {}", count); LOGGER.info("Migrating {} {}", itemName, count);
} }
} }
......
package de.deadlocker8.budgetmaster.databasemigrator.steps.category; package de.deadlocker8.budgetmaster.databasemigrator.listener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -6,22 +6,29 @@ import org.springframework.batch.core.ExitStatus; ...@@ -6,22 +6,29 @@ import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener; import org.springframework.batch.core.StepExecutionListener;
public class CategoryStepListener implements StepExecutionListener public class GenericStepListener implements StepExecutionListener
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(CategoryStepListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(GenericStepListener.class);
private final String itemName;
public GenericStepListener(String itemName)
{
this.itemName = itemName;
}
@Override @Override
public void beforeStep(StepExecution stepExecution) public void beforeStep(StepExecution stepExecution)
{ {
LOGGER.info("\n"); LOGGER.info("\n");
LOGGER.info(">>> Migrate categories..."); LOGGER.info(">>> Migrate {}...", itemName);
} }
@Override @Override
public ExitStatus afterStep(StepExecution stepExecution) public ExitStatus afterStep(StepExecution stepExecution)
{ {
final int count = stepExecution.getReadCount(); final int count = stepExecution.getReadCount();
LOGGER.info(">>> Successfully migrated {} categories\n", count); LOGGER.info(">>> Successfully migrated {} {}\n", count, itemName);
return null; return null;
} }
} }
package de.deadlocker8.budgetmaster.databasemigrator.source.image;
import javax.persistence.*;
@Entity
@Table(name = "image")
public class SourceImage
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer ID;
@Lob
private byte[] image;
private String fileName;
private Integer fileExtension;
public SourceImage(byte[] image, String fileName, Integer fileExtension)
{
this.image = image;
this.fileName = fileName;
this.fileExtension = fileExtension;
}
public SourceImage()
{
}
public Integer getID()
{
return ID;
}
public void setID(Integer ID)
{
this.ID = ID;
}
public byte[] getImage()
{
return image;
}
public void setImage(byte[] image)
{
this.image = image;
}
public String getFileName()
{
return fileName;
}
public void setFileName(String fileName)
{
this.fileName = fileName;
}
public Integer getFileExtension()
{
return fileExtension;
}
public void setFileExtension(Integer fileExtension)
{
this.fileExtension = fileExtension;
}
@Override
public String toString()
{
return "SourceImage{" +
"ID=" + ID +
", image='" + image + '\'' +
", fileName='" + fileName + '\'' +
", fileExtension='" + fileExtension + '\'' +
'}';
}
}
\ No newline at end of file
package de.deadlocker8.budgetmaster.databasemigrator.source.image;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SourceImageRepository extends JpaRepository<SourceImage, Integer>
{
}
package de.deadlocker8.budgetmaster.databasemigrator.steps.image;
import de.deadlocker8.budgetmaster.databasemigrator.destination.image.DestinationImage;
import de.deadlocker8.budgetmaster.databasemigrator.source.image.SourceImage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;
@Component
public class ImageProcessor implements ItemProcessor<SourceImage, DestinationImage>
{
private static final Logger LOGGER = LoggerFactory.getLogger(ImageProcessor.class);
@Override
public DestinationImage process(SourceImage image)
{
LOGGER.debug("ImageProcessor: Processing image: {}", image);
final DestinationImage destinationImage = new DestinationImage();
destinationImage.setFileExtension(image.getFileExtension());
destinationImage.setFileName(image.getFileName());
destinationImage.setImage(image.getImage());
return destinationImage;
}
}
package de.deadlocker8.budgetmaster.databasemigrator.steps.image;
import de.deadlocker8.budgetmaster.databasemigrator.source.image.SourceImage;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.MessageFormat;
@Component
public class ImageReader extends JdbcCursorItemReader<SourceImage> implements ItemReader<SourceImage>
{
private static final String TABLE_NAME = "image";
private static class DatabaseColumns
{
public static final String ID = "ID";
public static final String FILE_EXTENSION = "FILE_EXTENSION";
public static final String FILE_NAME = "FILE_NAME";
public static final String IMAGE = "IMAGE";
}
public ImageReader(@Autowired DataSource primaryDataSource)
{
setDataSource(primaryDataSource);
setSql(MessageFormat.format("SELECT * FROM {0}", TABLE_NAME));
setFetchSize(100);
setRowMapper(new ImageRowMapper());
}
public static class ImageRowMapper implements RowMapper<SourceImage>
{
@Override
public SourceImage mapRow(ResultSet rs, int rowNum) throws SQLException
{
final SourceImage image = new SourceImage();
image.setID(rs.getInt(DatabaseColumns.ID));
image.setFileExtension(rs.getInt(DatabaseColumns.FILE_EXTENSION));
image.setFileName(rs.getString(DatabaseColumns.FILE_NAME));
image.setImage(rs.getBytes(DatabaseColumns.IMAGE));
return image;
}
}
}
package de.deadlocker8.budgetmaster.databasemigrator.steps.image;
import de.deadlocker8.budgetmaster.databasemigrator.destination.image.DestinationImage;
import de.deadlocker8.budgetmaster.databasemigrator.destination.image.DestinationImageRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class ImageWriter implements ItemWriter<DestinationImage>
{
private static final Logger LOGGER = LoggerFactory.getLogger(ImageWriter.class);
final DestinationImageRepository destinationImageRepository;
public ImageWriter(DestinationImageRepository destinationImageRepository)
{
this.destinationImageRepository = destinationImageRepository;
}
@Override
public void write(List<? extends DestinationImage> list)
{
for(DestinationImage data : list)
{
LOGGER.debug("ImageWriter: Writing image: {}", data);
destinationImageRepository.save(data);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment