From 11a5422efc8a535bce6a51f496dfa8176fa97f29 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Fri, 10 Jan 2020 10:12:25 +0100 Subject: [PATCH] [jOOQ/jOOQ#8755] Add Loader[CSV|JSON|Rows]Step#fieldsFromSource() This new `fieldsFromSource()` method can be used when all or a subset of the input field names exactly match the target table column names. The load operation can then be executed without having to specify the fields and will create rows with all matching fields (other fields are ignored). Since the fields are mapped by name this requires the input to specify the field names. For CSV this means that there must be a header row, otherwise using `fieldsFromSource()` will result in a runtime exception. --- .../src/main/java/org/jooq/LoaderCSVStep.java | 13 +++++ .../main/java/org/jooq/LoaderJSONStep.java | 17 +++++- .../main/java/org/jooq/LoaderRowsStep.java | 12 +++++ .../main/java/org/jooq/impl/LoaderImpl.java | 53 +++++++++++++------ 4 files changed, 77 insertions(+), 18 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/LoaderCSVStep.java b/jOOQ/src/main/java/org/jooq/LoaderCSVStep.java index 039a5503b4..ba5666e3d0 100644 --- a/jOOQ/src/main/java/org/jooq/LoaderCSVStep.java +++ b/jOOQ/src/main/java/org/jooq/LoaderCSVStep.java @@ -40,6 +40,7 @@ package org.jooq; import java.util.Collection; import org.jooq.LoaderFieldMapper.LoaderFieldContext; +import org.jooq.exception.LoaderConfigurationException; /** * The Loader API is used for configuring data loads. @@ -102,4 +103,16 @@ public interface LoaderCSVStep { */ @Support LoaderCSVOptionsStep fields(LoaderFieldMapper mapper); + + /** + * Indicate that all input fields which have a corresponding field in the + * target table (with the same name) should be loaded. + *

+ * When {@link LoaderLoadStep#execute() executing the loader} input fields + * for which there is no match in the target table will be logged and if no + * field names can be derived for the input data a + * {@link LoaderConfigurationException} will be reported. + */ + @Support + LoaderCSVOptionsStep fieldsFromSource(); } diff --git a/jOOQ/src/main/java/org/jooq/LoaderJSONStep.java b/jOOQ/src/main/java/org/jooq/LoaderJSONStep.java index df1947014a..63792cd037 100644 --- a/jOOQ/src/main/java/org/jooq/LoaderJSONStep.java +++ b/jOOQ/src/main/java/org/jooq/LoaderJSONStep.java @@ -40,6 +40,7 @@ package org.jooq; import java.util.Collection; import org.jooq.LoaderFieldMapper.LoaderFieldContext; +import org.jooq.exception.LoaderConfigurationException; /** * The Loader API is used for configuring data loads. @@ -53,7 +54,7 @@ import org.jooq.LoaderFieldMapper.LoaderFieldContext; public interface LoaderJSONStep { /** - * Specify the the fields to be loaded into the table in the correct order. + * Specify the fields to be loaded into the table in the correct order. *

* The JSON column at index i is inserted into the table field * at index i. If fields[i] == null or @@ -64,7 +65,7 @@ public interface LoaderJSONStep { LoaderJSONOptionsStep fields(Field... fields); /** - * Specify the the fields to be loaded into the table in the correct order. + * Specify the fields to be loaded into the table in the correct order. *

* The JSON column at index i is inserted into the table field * at index i. If @@ -86,4 +87,16 @@ public interface LoaderJSONStep { */ @Support LoaderJSONOptionsStep fields(LoaderFieldMapper mapper); + + /** + * Indicate that all input fields which have a corresponding field in the + * target table (with the same name) should be loaded. + *

+ * When {@link LoaderLoadStep#execute() executing the loader} input fields + * for which there is no match in the target table will be logged and if no + * field names can be derived for the input data a + * {@link LoaderConfigurationException} will be reported. + */ + @Support + LoaderJSONOptionsStep fieldsFromSource(); } diff --git a/jOOQ/src/main/java/org/jooq/LoaderRowsStep.java b/jOOQ/src/main/java/org/jooq/LoaderRowsStep.java index dc2418cf01..29ab25f36d 100644 --- a/jOOQ/src/main/java/org/jooq/LoaderRowsStep.java +++ b/jOOQ/src/main/java/org/jooq/LoaderRowsStep.java @@ -40,6 +40,7 @@ package org.jooq; import java.util.Collection; import org.jooq.LoaderFieldMapper.LoaderFieldContext; +import org.jooq.exception.LoaderConfigurationException; /** * The Loader API is used for configuring data loads. @@ -103,4 +104,15 @@ public interface LoaderRowsStep { @Support LoaderListenerStep fields(LoaderFieldMapper mapper); + /** + * Indicate that all input fields which have a corresponding field in the + * target table (with the same name) should be loaded. + *

+ * When {@link LoaderLoadStep#execute() executing the loader} input fields + * for which there is no match in the target table will be logged and if no + * field names can be derived for the input data a + * {@link LoaderConfigurationException} will be reported. + */ + @Support + LoaderListenerStep fieldsFromSource(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java index aee661275f..688416e969 100644 --- a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java @@ -82,6 +82,7 @@ import org.jooq.Source; import org.jooq.Table; import org.jooq.exception.DataAccessException; import org.jooq.exception.LoaderConfigurationException; +import org.jooq.tools.JooqLogger; import org.jooq.tools.StringUtils; import org.jooq.tools.csv.CSVParser; import org.jooq.tools.csv.CSVReader; @@ -104,6 +105,8 @@ final class LoaderImpl implements LoaderJSONOptionsStep, Loader { + private static final JooqLogger log = JooqLogger.getLogger(LoaderImpl.class); + // Configuration constants // ----------------------- private static final int ON_DUPLICATE_KEY_ERROR = 0; @@ -156,6 +159,7 @@ final class LoaderImpl implements private Field[] source; private Field[] fields; private LoaderFieldMapper fieldMapper; + private boolean fieldsFromSource; private boolean[] primaryKey; // Result data @@ -547,29 +551,46 @@ final class LoaderImpl implements return this; } + @Override + public LoaderImpl fieldsFromSource() { + fieldsFromSource = true; + return this; + } + private final void fields0(Object[] row) { Field[] f = new Field[row.length]; // [#5145] When loading arrays, or when CSV headers are ignored, // the source is still null at this stage. if (source == null) - source = Tools.fields(row.length); + if (fieldsFromSource) + throw new LoaderConfigurationException("Using fieldsFromSource() requires field names to be available in source."); + else + source = Tools.fields(row.length); - for (int i = 0; i < row.length; i++) { - final int index = i; + if (fieldMapper != null) + for (int i = 0; i < row.length; i++) { + final int index = i; - f[i] = fieldMapper.map(new LoaderFieldContext() { - @Override - public int index() { - return index; - } + f[i] = fieldMapper.map(new LoaderFieldContext() { + @Override + public int index() { + return index; + } - @Override - public Field field() { - return source[index]; - } - }); - } + @Override + public Field field() { + return source[index]; + } + }); + } + + else if (fieldsFromSource) + for (int i = 0; i < row.length; i++) { + f[i] = table.field(source[i]); + if (f[i] == null) + log.info("No column in target table " + table + " found for input field " + source[i]); + } fields(f); } @@ -721,8 +742,8 @@ final class LoaderImpl implements if (row.getClass() != Object[].class) row = Arrays.copyOf(row, row.length, Object[].class); - // [#5145] Lazy initialisation of fields off the first row - // in case LoaderFieldMapper was used. + // [#5145][#8755] Lazy initialisation of fields from the first row + // in case fields(LoaderFieldMapper) or fieldsFromSource() was used if (fields == null) fields0(row);