styles;
51 |
52 | @Data
53 | @XmlAccessorType(XmlAccessType.FIELD)
54 | public static class Master {
55 |
56 | @XmlValue
57 | Integer masterId;
58 |
59 | @XmlAttribute(name = "is_main_release")
60 | boolean isMaster;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/io/dsub/discogs/batch/util/FileUtil.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.util;
2 |
3 | import io.dsub.discogs.batch.exception.FileDeleteException;
4 | import io.dsub.discogs.batch.exception.FileException;
5 | import java.io.InputStream;
6 | import java.nio.file.Path;
7 | import org.apache.commons.lang3.SystemUtils;
8 |
9 | /**
10 | * Interface to support file creation, deletion operations only within application directory.
11 | * Currently, there is no need to utilize second level, hence flattening the method params by
12 | * String. There is NO GUARANTEE that this interface will not grow in the future.
13 | *
14 | * Methods and their behaviors will depends on its implementations.
15 | */
16 | public interface FileUtil {
17 |
18 | String DEFAULT_APP_DIR = "discogs-data-batch";
19 |
20 | void clearAll() throws FileException;
21 |
22 | Path getFilePath(String filename, boolean generate) throws FileException;
23 |
24 | Path getFilePath(String filename) throws FileException;
25 |
26 | Path getAppDirectory(boolean generate) throws FileException;
27 |
28 | void deleteFile(String filename) throws FileDeleteException;
29 |
30 | boolean isExisting(String filename);
31 |
32 | long getSize(String filename) throws FileException;
33 |
34 | void copy(InputStream inputStream, String filename) throws FileException;
35 |
36 | /**
37 | * A wrapper method to get home directory from the parent OS. The implementation of the method may
38 | * change over time, in case of bugs or requirements of additional logics.
39 | *
40 | * @return directory promised to be home directory (i.e. `~`.)
41 | */
42 | default Path getHomeDirectory() {
43 | return SystemUtils.getUserHome().toPath();
44 | }
45 |
46 | String getAppDirectory();
47 |
48 | boolean isTemporary();
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/io/dsub/discogs/batch/domain/master/MasterXML.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.domain.master;
2 |
3 | import io.dsub.discogs.batch.domain.BaseXML;
4 | import io.dsub.discogs.jooq.tables.records.MasterRecord;
5 | import java.time.Clock;
6 | import java.time.LocalDateTime;
7 | import java.util.List;
8 | import javax.xml.bind.annotation.XmlAccessType;
9 | import javax.xml.bind.annotation.XmlAccessorType;
10 | import javax.xml.bind.annotation.XmlAttribute;
11 | import javax.xml.bind.annotation.XmlElement;
12 | import javax.xml.bind.annotation.XmlElementWrapper;
13 | import javax.xml.bind.annotation.XmlRootElement;
14 | import lombok.Data;
15 | import lombok.EqualsAndHashCode;
16 |
17 | @Data
18 | @XmlRootElement(name = "master")
19 | @XmlAccessorType(XmlAccessType.FIELD)
20 | @EqualsAndHashCode(callSuper = false)
21 | public class MasterXML implements BaseXML {
22 |
23 | @XmlAttribute(name = "id")
24 | private Integer id;
25 |
26 | @XmlElement(name = "year")
27 | private Short year;
28 |
29 | @XmlElement(name = "title")
30 | private String title;
31 |
32 | @XmlElement(name = "main_release")
33 | private Integer mainReleaseId;
34 |
35 | @XmlElement(name = "data_quality")
36 | private String dataQuality;
37 |
38 | @XmlElementWrapper(name = "genres")
39 | @XmlElement(name = "genre")
40 | private List genres;
41 |
42 | @XmlElementWrapper(name = "styles")
43 | @XmlElement(name = "style")
44 | private List styles;
45 |
46 | @Override
47 | public MasterRecord buildRecord() {
48 | return new MasterRecord()
49 | .setId(id)
50 | .setTitle(title)
51 | .setYear(year)
52 | .setDataQuality(dataQuality)
53 | .setCreatedAt(LocalDateTime.now(Clock.systemUTC()))
54 | .setLastModifiedAt(LocalDateTime.now(Clock.systemUTC()));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/io/dsub/discogs/batch/JobLaunchingRunner.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch;
2 |
3 | import java.util.concurrent.CountDownLatch;
4 | import lombok.RequiredArgsConstructor;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.batch.core.Job;
7 | import org.springframework.batch.core.JobExecution;
8 | import org.springframework.batch.core.JobParameters;
9 | import org.springframework.batch.core.launch.JobLauncher;
10 | import org.springframework.boot.ApplicationArguments;
11 | import org.springframework.boot.ApplicationRunner;
12 | import org.springframework.boot.ExitCodeGenerator;
13 | import org.springframework.boot.SpringApplication;
14 | import org.springframework.context.ConfigurableApplicationContext;
15 | import org.springframework.context.annotation.Profile;
16 | import org.springframework.core.annotation.Order;
17 | import org.springframework.stereotype.Component;
18 |
19 | @Slf4j
20 | @Order(10)
21 | @Profile("!test")
22 | @Component
23 | @RequiredArgsConstructor
24 | public class JobLaunchingRunner implements ApplicationRunner {
25 |
26 | private final Job job;
27 | private final JobParameters discogsJobParameters;
28 | private final JobLauncher jobLauncher;
29 | private final ConfigurableApplicationContext ctx;
30 | private final CountDownLatch countDownLatch;
31 |
32 | @Override
33 | public void run(ApplicationArguments args) throws Exception {
34 | JobExecution jobExecution = jobLauncher.run(job, discogsJobParameters);
35 | log.info("main thread started job execution. awaiting for completion...");
36 | countDownLatch.await();
37 | log.info("job execution completed. exiting...");
38 | SpringApplication.exit(ctx, getExitCodeGenerator(jobExecution));
39 | }
40 |
41 | public ExitCodeGenerator getExitCodeGenerator(JobExecution jobExecution) {
42 | return () -> jobExecution.getFailureExceptions().size() > 0 ? 1 : 0;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/io/dsub/discogs/batch/argument/formatter/ArgumentNameFormatter.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.argument.formatter;
2 |
3 | import io.dsub.discogs.batch.argument.ArgType;
4 | import java.util.Arrays;
5 |
6 | /**
7 | * ArgumentFormatter that formats argument's name
8 | */
9 | public class ArgumentNameFormatter implements ArgumentFormatter {
10 |
11 | /**
12 | * Formats argument name so that it does not contains any plurals. Also, it will check the
13 | * argument name and assign proper name from {@link ArgType}.
14 | *
15 | * @param args argument to be evaluated.
16 | * @return argument with formatted name, or as-is if name is unrecognizable.
17 | */
18 | @Override
19 | public String[] format(String[] args) {
20 | return Arrays.stream(args)
21 | .map(this::doFormat)
22 | .toArray(String[]::new);
23 | }
24 |
25 | private String doFormat(String arg) {
26 | // make lower case
27 | String head = arg.split("=")[0].toLowerCase();
28 | // remove all plurals('[sS]$')
29 | if (head.matches(".*[sS]$") && !ArgType.contains(head)) {
30 | head = head.substring(0, head.length() - 1);
31 | }
32 | // fetch type of the argument name.
33 | ArgType type = ArgType.getTypeOf(head);
34 |
35 | String value;
36 |
37 | // meaning the last character is equals sign
38 | if (arg.indexOf('=') == arg.length() - 1 || arg.indexOf('=') == -1) {
39 | value = "";
40 | } else {
41 | // parse the value string from given argument.
42 | value = arg.substring(arg.indexOf("=") + 1);
43 | }
44 |
45 | // unknown type, hence return argument as-is.
46 | if (type == null) {
47 | return arg;
48 | }
49 |
50 | head = type.getGlobalName();
51 |
52 | // handle no-arg option
53 | if (!type.isValueRequired()) {
54 | return head;
55 | }
56 |
57 | return String.join("=", head, value);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/io/dsub/discogs/batch/job/tasklet/GenreStyleInsertionTasklet.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.job.tasklet;
2 |
3 | import io.dsub.discogs.batch.job.registry.DefaultEntityIdRegistry;
4 | import io.dsub.discogs.batch.job.registry.EntityIdRegistry;
5 | import io.dsub.discogs.jooq.tables.records.GenreRecord;
6 | import io.dsub.discogs.jooq.tables.records.StyleRecord;
7 | import java.util.stream.Collectors;
8 | import lombok.RequiredArgsConstructor;
9 | import org.jooq.UpdatableRecord;
10 | import org.springframework.batch.core.ExitStatus;
11 | import org.springframework.batch.core.StepContribution;
12 | import org.springframework.batch.core.scope.context.ChunkContext;
13 | import org.springframework.batch.core.step.tasklet.Tasklet;
14 | import org.springframework.batch.item.ItemWriter;
15 | import org.springframework.batch.repeat.RepeatStatus;
16 | import org.springframework.stereotype.Component;
17 |
18 | @Component
19 | @RequiredArgsConstructor
20 | public class GenreStyleInsertionTasklet implements Tasklet {
21 |
22 | private final EntityIdRegistry registry;
23 | private final ItemWriter> jooqItemWriter;
24 |
25 | @Override
26 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
27 | throws Exception {
28 | contribution.setExitStatus(ExitStatus.EXECUTING);
29 | jooqItemWriter.write(
30 | registry.getStringIdSetByType(DefaultEntityIdRegistry.Type.GENRE).stream()
31 | .map(genre -> new GenreRecord().setName(genre))
32 | .collect(Collectors.toList()));
33 | jooqItemWriter.write(
34 | registry.getStringIdSetByType(DefaultEntityIdRegistry.Type.STYLE).stream()
35 | .map(style -> new StyleRecord().setName(style))
36 | .collect(Collectors.toList()));
37 | contribution.setExitStatus(ExitStatus.COMPLETED);
38 | chunkContext.setComplete();
39 | return RepeatStatus.FINISHED;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/io/dsub/discogs/batch/domain/label/LabelSubItemsXML.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.domain.label;
2 |
3 | import io.dsub.discogs.batch.domain.SubItemXML;
4 | import io.dsub.discogs.jooq.tables.records.LabelSubLabelRecord;
5 | import java.time.Clock;
6 | import java.time.LocalDateTime;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 | import javax.xml.bind.annotation.XmlAccessType;
10 | import javax.xml.bind.annotation.XmlAccessorType;
11 | import javax.xml.bind.annotation.XmlAttribute;
12 | import javax.xml.bind.annotation.XmlElement;
13 | import javax.xml.bind.annotation.XmlElementWrapper;
14 | import javax.xml.bind.annotation.XmlRootElement;
15 | import javax.xml.bind.annotation.XmlValue;
16 | import lombok.Data;
17 | import lombok.EqualsAndHashCode;
18 |
19 | @Data
20 | @XmlRootElement(name = "label")
21 | @XmlAccessorType(XmlAccessType.FIELD)
22 | @EqualsAndHashCode(callSuper = false)
23 | public class LabelSubItemsXML {
24 |
25 | @XmlElement(name = "id")
26 | private Integer id;
27 |
28 | @XmlElementWrapper(name = "sublabels")
29 | @XmlElement(name = "label")
30 | private List labelSubLabels = new ArrayList<>();
31 |
32 | @XmlElementWrapper(name = "urls")
33 | @XmlElement(name = "url")
34 | private List urls = new ArrayList<>();
35 |
36 | @Data
37 | @XmlAccessorType(XmlAccessType.FIELD)
38 | public static class LabelSubLabelXML implements SubItemXML {
39 |
40 | @XmlValue
41 | private String name;
42 |
43 | @XmlAttribute(name = "id")
44 | private Integer subLabelId;
45 |
46 | @Override
47 | public LabelSubLabelRecord getRecord(int parentId) {
48 | return new LabelSubLabelRecord()
49 | .setParentLabelId(parentId)
50 | .setSubLabelId(subLabelId)
51 | .setCreatedAt(LocalDateTime.now(Clock.systemUTC()))
52 | .setLastModifiedAt(LocalDateTime.now(Clock.systemUTC()));
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/io/dsub/discogs/batch/argument/validator/TypeArgumentValidator.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.argument.validator;
2 |
3 | import java.util.List;
4 | import java.util.regex.Pattern;
5 | import java.util.stream.Collectors;
6 | import org.apache.logging.log4j.util.Strings;
7 | import org.springframework.boot.ApplicationArguments;
8 |
9 | public class TypeArgumentValidator implements ArgumentValidator {
10 |
11 | private static final Pattern TYPE_VALUE_PATTERN =
12 | Pattern.compile("^((ARTIST)|(RELEASE)|(MASTER)|(LABEL))$", Pattern.CASE_INSENSITIVE);
13 |
14 | private static final Pattern TYPE_PATTERN =
15 | Pattern.compile("^type[s]?$", Pattern.CASE_INSENSITIVE);
16 |
17 | @Override
18 | public ValidationResult validate(ApplicationArguments args) {
19 | // init
20 | ValidationResult result = new DefaultValidationResult();
21 |
22 | // collect possible duplicate type argument.
23 | List typeArgNames =
24 | args.getOptionNames().stream()
25 | .filter(name -> TYPE_PATTERN.matcher(name).matches())
26 | .collect(Collectors.toList());
27 |
28 | // empty means we do not need to validate anything.
29 | if (typeArgNames.isEmpty()) {
30 | return result;
31 | }
32 |
33 | // duplicated entries to be picked up from here.
34 | if (typeArgNames.size() > 1) {
35 | String msg = "duplicated type argument exists: " + Strings.join(typeArgNames, ',');
36 | return result.withIssues(msg);
37 | }
38 |
39 | // collect issues if any of argument does not match to the criteria.
40 | List issues =
41 | args.getOptionValues(typeArgNames.get(0)).stream()
42 | .filter(val -> !TYPE_VALUE_PATTERN.matcher(val).matches())
43 | .map(val -> "unknown type argument value: " + val)
44 | .collect(Collectors.toList());
45 |
46 | // will be empty if there was no issue.
47 | return result.withIssues(issues);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/test/java/io/dsub/discogs/batch/container/PostgreSQLContainerBaseTest.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.container;
2 |
3 | import javax.sql.DataSource;
4 | import org.springframework.boot.jdbc.DataSourceBuilder;
5 | import org.springframework.boot.test.util.TestPropertyValues;
6 | import org.springframework.context.ApplicationContextInitializer;
7 | import org.springframework.context.ConfigurableApplicationContext;
8 | import org.testcontainers.containers.PostgreSQLContainer;
9 |
10 | public abstract class PostgreSQLContainerBaseTest {
11 |
12 | protected static final PostgreSQLContainer CONTAINER;
13 | protected static final DataSource dataSource;
14 |
15 | static {
16 | CONTAINER = new PostgreSQLContainer("postgres:latest")
17 | .withDatabaseName("databaseName")
18 | .withPassword("password")
19 | .withUsername("username");
20 | CONTAINER.start();
21 | dataSource = DataSourceBuilder.create()
22 | .driverClassName(CONTAINER.getDriverClassName())
23 | .url(CONTAINER.getJdbcUrl())
24 | .username(CONTAINER.getUsername())
25 | .password(CONTAINER.getPassword())
26 | .build();
27 | }
28 |
29 | protected final String jdbcUrl = CONTAINER.getJdbcUrl();
30 | protected final String password = CONTAINER.getPassword();
31 | protected final String username = CONTAINER.getUsername();
32 |
33 | static class PostgreSQLPropertiesInitializer implements
34 | ApplicationContextInitializer {
35 |
36 | @Override
37 | public void initialize(ConfigurableApplicationContext applicationContext) {
38 | TestPropertyValues.of("spring.datasource.driver-class-name=" + CONTAINER.getDriverClassName(),
39 | "spring.datasource.username=" + CONTAINER.getUsername(),
40 | "spring.datasource.password=" + CONTAINER.getPassword(),
41 | "spring.datasource.url=" + CONTAINER.getJdbcUrl())
42 | .applyTo(applicationContext.getEnvironment());
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/test/java/io/dsub/discogs/batch/argument/handler/DefaultArgumentHandlerIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.argument.handler;
2 |
3 | import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
4 |
5 | import io.dsub.discogs.batch.container.PostgreSQLContainerBaseTest;
6 | import io.dsub.discogs.batch.exception.InvalidArgumentException;
7 | import org.junit.jupiter.api.Assertions;
8 | import org.junit.jupiter.api.Test;
9 | import org.junit.jupiter.params.ParameterizedTest;
10 | import org.junit.jupiter.params.provider.ValueSource;
11 |
12 | class DefaultArgumentHandlerIntegrationTest extends PostgreSQLContainerBaseTest {
13 |
14 | private final ArgumentHandler handler = new DefaultArgumentHandler();
15 |
16 | @Test
17 | void shouldHandleMalformedUrlArgumentFlag() throws InvalidArgumentException {
18 | String[] args = new String[]{"url=" + jdbcUrl, "user=" + username, "pass=" + password};
19 | Assertions.assertDoesNotThrow(() -> handler.resolve(args));
20 | String[] resolved = handler.resolve(args);
21 | for (String s : resolved) {
22 | assertThat(s.startsWith("--")).isTrue();
23 | }
24 | }
25 |
26 | @ParameterizedTest
27 | @ValueSource(strings = {"--m", "m", "--mount", "mount"})
28 | void whenOptionArgGiven__ShouldAddAsOption__RegardlessOfDashPresented(String arg)
29 | throws InvalidArgumentException {
30 | String[] args = {"url=" + jdbcUrl, "user=" + username, "pass=" + password, arg};
31 | args = handler.resolve(args);
32 | assertThat(args).contains("--mount");
33 | }
34 |
35 | @Test
36 | void shouldReplacePlurals() throws InvalidArgumentException {
37 | String[] args = {"urls=" + jdbcUrl, "user=" + username, "pass=" + password, "etags=hello"};
38 | args = handler.resolve(args);
39 | for (String arg : args) {
40 | String head = arg.split("=")[0];
41 | if (head.contains("pass")) {
42 | continue;
43 | }
44 | assertThat(arg.split("=")[0].matches(".*s$")).isFalse();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/java/io/dsub/discogs/batch/argument/formatter/JdbcUrlFormatterTest.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.argument.formatter;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import ch.qos.logback.classic.Level;
6 | import io.dsub.discogs.batch.testutil.LogSpy;
7 | import java.util.Arrays;
8 | import java.util.Optional;
9 | import org.junit.jupiter.api.Test;
10 | import org.junit.jupiter.api.extension.RegisterExtension;
11 |
12 | public class JdbcUrlFormatterTest {
13 |
14 | JdbcUrlFormatter formatter = new JdbcUrlFormatter();
15 |
16 | @RegisterExtension
17 | LogSpy logSpy = new LogSpy();
18 |
19 | @Test
20 | void whenSchemaOrDatabaseMissing__ShouldAddDefault() {
21 | String[] args = new String[]{"url=jdbc:postgresql://localhost:3306?serverTimeZone=UTC",
22 | "user=root", "pass=gozldwmf77", "m"};
23 |
24 | String[] formatted = formatter.format(args);
25 |
26 | Optional urlArg = Arrays.stream(formatted)
27 | .filter(arg -> arg.startsWith("url"))
28 | .map(arg -> arg.replace("url=", ""))
29 | .findFirst();
30 |
31 | assertThat(urlArg).isPresent();
32 |
33 | String arg = urlArg.get();
34 |
35 | assertThat(arg).matches(".*discogs.*");
36 |
37 | assertThat(logSpy.getLogsByExactLevelAsString(Level.INFO, true))
38 | .hasSize(1)
39 | .anyMatch(s -> s.matches("^default database or schema missing.*"));
40 | }
41 |
42 | @Test
43 | void whenArrayIsNull__ShouldReturnNull() {
44 |
45 | // when
46 | String[] formatted = formatter.format(null);
47 |
48 | // then
49 | assertThat(formatted).isNull();
50 | }
51 |
52 | @Test
53 | void whenArrayIsEmpty__ShouldReturnEmptyArray() {
54 | String[] args = new String[0];
55 |
56 | // when
57 | String[] formatted = formatter.format(args);
58 |
59 | // then
60 | assertThat(formatted)
61 | .isNotNull()
62 | .isEmpty();
63 | }
64 |
65 | @Test
66 | void whenUrlIsEmpty__ShouldReturnNull() {
67 |
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/test/java/io/dsub/discogs/batch/service/DefaultDatabaseConnectionValidatorUnitTest.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.service;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import io.dsub.discogs.batch.argument.validator.DefaultDatabaseConnectionValidator;
6 | import io.dsub.discogs.batch.argument.validator.ValidationResult;
7 | import io.dsub.discogs.batch.testutil.LogSpy;
8 | import org.junit.jupiter.api.Test;
9 | import org.junit.jupiter.api.extension.RegisterExtension;
10 | import org.springframework.boot.ApplicationArguments;
11 | import org.springframework.boot.DefaultApplicationArguments;
12 |
13 | class DefaultDatabaseConnectionValidatorUnitTest {
14 |
15 | final DefaultDatabaseConnectionValidator validator = new DefaultDatabaseConnectionValidator();
16 |
17 | @RegisterExtension
18 | LogSpy logSpy = new LogSpy();
19 |
20 | @Test
21 | void shouldPassIfValidDataSourceValueIsPresent() {
22 | ApplicationArguments args = getArgs("--url=jdbc:mysql://", "--password", "--username=sa");
23 |
24 | // when
25 | ValidationResult validationResult = validator.validate(args);
26 |
27 | // then
28 | assertThat(validationResult.isValid()).isFalse();
29 | }
30 |
31 | @Test
32 | void shouldReportIfInvalidValueHasBeenPassed() {
33 | // when
34 | ValidationResult result = validator
35 | .validate(getArgs("--url=hello", "--username=un", "--password=pw"));
36 |
37 | // then
38 | assertThat(result.getIssues()).contains("failed to allocate driver for url: hello");
39 | }
40 |
41 | @Test
42 | void whenArgumentExceptionIfUrlMissing__ShouldReturnValidationResult() {
43 |
44 | // when
45 | ValidationResult result = validator.validate(getArgs("--password=something", "--username=un"));
46 |
47 | // then
48 | assertThat(result.isValid()).isFalse();
49 | assertThat(result.getIssues()).contains("url cannot be null or blank");
50 | }
51 |
52 | private ApplicationArguments getArgs(String... args) {
53 | return new DefaultApplicationArguments(args);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/io/dsub/discogs/batch/job/writer/DefaultLJooqItemWriter.java:
--------------------------------------------------------------------------------
1 | package io.dsub.discogs.batch.job.writer;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 | import lombok.RequiredArgsConstructor;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.jooq.BatchBindStep;
8 | import org.jooq.DSLContext;
9 | import org.jooq.Field;
10 | import org.jooq.Query;
11 | import org.jooq.UpdatableRecord;
12 |
13 | @Slf4j
14 | @RequiredArgsConstructor
15 | public class DefaultLJooqItemWriter> extends AbstractJooqItemWriter {
16 |
17 | private final DSLContext context;
18 |
19 | @Override
20 | public void write(List extends T> items) {
21 | if (items.isEmpty()) {
22 | return;
23 | }
24 | Query q = this.getQuery(items.get(0));
25 | BatchBindStep batch = context.batch(q);
26 |
27 | items.forEach(record -> batch.bind(mapValues(record)));
28 | batch.execute();
29 | }
30 |
31 | /**
32 | * map values from record into a full array
33 | *
34 | * @param record to be parsed into array
35 | * @return values
36 | */
37 | private Object[] mapValues(T record) {
38 | List