diff --git a/jOOQ/src/main/java/org/jooq/Mappable.java b/jOOQ/src/main/java/org/jooq/Mappable.java new file mode 100644 index 0000000000..fb1c1cf654 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Mappable.java @@ -0,0 +1,82 @@ +/* + * 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 + * + * http://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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import org.jooq.impl.DefaultRecordMapper; + +/** + * A type producing records that can be mapped. + * + * @author Lukas Eder + */ +public interface Mappable { + + /** + * Create a record mapper that extracts a value by field index. + */ + RecordMapper mapper(int fieldIndex); + + /** + * Create a record mapper that extracts a value by field name. + */ + RecordMapper mapper(String fieldName); + + /** + * Create a record mapper that extracts a value by field name. + */ + RecordMapper mapper(Name fieldName); + + /** + * Create a record mapper that extracts a value by field reference. + */ + RecordMapper mapper(Field field); + + /** + * Create a record mapper that maps records to a new + * {@link RecordQualifier#getRecordType()}. + */ + RecordMapper mapper(Table table); + + /** + * Create a record mapper that maps records to a {@link Class} using the + * configured {@link Configuration#recordMapperProvider()} (the + * {@link DefaultRecordMapper}, by default). + */ + RecordMapper mapper(Configuration configuration, Class type); + +} diff --git a/jOOQ/src/main/java/org/jooq/RecordType.java b/jOOQ/src/main/java/org/jooq/RecordType.java index 554ba10272..1088fa5ac4 100644 --- a/jOOQ/src/main/java/org/jooq/RecordType.java +++ b/jOOQ/src/main/java/org/jooq/RecordType.java @@ -37,6 +37,8 @@ */ package org.jooq; +import org.jooq.impl.DefaultRecordMapper; + /** * A record type for {@link Table}, {@link Cursor}, {@link Result} and other * objects. @@ -51,7 +53,7 @@ package org.jooq; * * @author Lukas Eder */ -public interface RecordType extends Fields { +public interface RecordType extends Fields, Mappable { /** * Get the degree of this record type. diff --git a/jOOQ/src/main/java/org/jooq/Records.java b/jOOQ/src/main/java/org/jooq/Records.java index c00aecc662..7bbb76c228 100644 --- a/jOOQ/src/main/java/org/jooq/Records.java +++ b/jOOQ/src/main/java/org/jooq/Records.java @@ -37,6 +37,19 @@ */ package org.jooq; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import org.jooq.exception.InvalidResultException; +import org.jooq.impl.Internal; + /** * Common utilities related to {@link Record} types and constructing * {@link RecordMapper}. @@ -58,6 +71,328 @@ package org.jooq; */ public final class Records { + /** + * Create a collector that can collect {@link Record1} resulting from a + * single column {@link ResultQuery} into a {@link List} of that column's + * type. + *

+ * For example: + *

+ *

+     * List<String> titles =
+     * ctx.select(BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toList());
+     * 
+ *

+ * This is the same as the following, but allows for omitting repeating the + * BOOK.TITLE column: + *

+ *

+     * List<String> titles =
+     * ctx.select(BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetch(BOOK.TITLE);
+     * 
+ */ + public static final > Collector> toList() { + return Collectors.mapping(Record1::value1, Collectors.toCollection(ArrayList::new)); + } + + /** + * Create a collector that can collect {@link Record} resulting from a + * {@link ResultQuery} into a {@link List} of a mapped type. + *

+ * For example: + *

+ *

+     * List<String> titles =
+     * ctx.select(BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toList(r -> r.get(BOOK.TITLE)));
+     * 
+ *

+ * This is the same as the following: + *

+ *

+     * List<String> titles =
+     * ctx.select(BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetch(BOOK.TITLE);
+     * 
+ */ + public static final Collector> toList(Function function) { + return Collectors.mapping(function, Collectors.toCollection(ArrayList::new)); + } + + /** + * Create a collector that can collect {@link Record1} resulting from a + * single column {@link ResultQuery} into a {@link Set} of that column's + * type. + *

+ * For example: + *

+ *

+     * Set<String> titles =
+     * ctx.select(BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toSet());
+     * 
+ *

+ * This is the same as the following, but allows for omitting repeating the + * BOOK.TITLE column: + *

+ *

+     * List<String> titles =
+     * ctx.select(BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetchSet(BOOK.TITLE);
+     * 
+ */ + public static final > Collector> toSet() { + return toSet(Record1::value1); + } + + /** + * Create a collector that can collect {@link Record} resulting from a + * {@link ResultQuery} into a {@link Set} of a mapped type. + *

+ * For example: + *

+ *

+     * Set<String> titles =
+     * ctx.select(BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toSet(r -> r.get(BOOK.TITLE)));
+     * 
+ *

+ * This is the same as the following: + *

+ *

+     * List<String> titles =
+     * ctx.select(BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetchSet(BOOK.TITLE);
+     * 
+ */ + public static final Collector> toSet(Function function) { + return Collectors.mapping(function, Collectors.toCollection(LinkedHashSet::new)); + } + + /** + * Create a collector that can collect {@link Record2} resulting from a + * 2-column {@link ResultQuery} into a {@link Map} using the first column as + * key and the second column as value. + *

+ * Collection throws {@link InvalidResultException} if a key is encountered + * more than once. + *

+ * For example: + *

+ *

+     * Map<Integer, String> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toMap());
+     * 
+ *

+ * This is the same as the following, but allows for omitting repeating the + * BOOK.ID and BOOK.TITLE columns: + *

+ *

+     * Map<Integer, String> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetchMap(BOOK.ID, BOOK.TITLE);
+     * 
+ */ + public static final > Collector> toMap() { + return toMap(Record2::value1, Record2::value2); + } + + /** + * Create a collector that can collect {@link Record} resulting from a + * {@link ResultQuery} into a {@link Map} using the result of the argument + * {@link RecordMapper} as key and the record itself as value. + *

+ * Collection throws {@link InvalidResultException} if a key is encountered + * more than once. + *

+ * For example: + *

+ *

+     * Map<Integer, Record2<Integer, String>> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toMap(r -> r.get(BOOK.ID)));
+     * 
+ *

+ * This is the same as the following: + *

+ *

+     * Map<Integer, Record2<Integer, String>> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetchMap(BOOK.ID);
+     * 
+ */ + public static final Collector> toMap(Function keyMapper) { + return toMap(keyMapper, r -> r); + } + + /** + * Create a collector that can collect {@link Record} resulting from a + * {@link ResultQuery} into a {@link Map} using the result of the argument + * {@link RecordMapper} as key and the result of another argument + * {@link RecordMapper} as value. + *

+ * Collection throws {@link InvalidResultException} if a key is encountered + * more than once. + *

+ * For example: + *

+ *

+     * Map<Integer, String> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toMap(r -> r.get(BOOK.ID), r -> r.get(BOOK.TITLE)));
+     * 
+ *

+ * This is the same as the following: + *

+ *

+     * Map<Integer, String> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetchMap(BOOK.ID, BOOK.TITLE);
+     * 
+ */ + public static final Collector> toMap( + Function keyMapper, + Function valueMapper + ) { + return Collectors.toMap( + keyMapper, + valueMapper, + (k1, k2) -> { + throw new InvalidResultException("Key " + k1 + " is not unique in Result"); + }, + LinkedHashMap::new + ); + } + + /** + * Create a collector that can collect {@link Record2} resulting from a + * 2-column {@link ResultQuery} into a {@link Map} using the first column as + * key collecting values of the second column into a list of values. + *

+ * For example: + *

+ *

+     * Map<Integer, List<String>> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toGroups());
+     * 
+ *

+ * This is the same as the following, but allows for omitting repeating the + * BOOK.ID and BOOK.TITLE columns: + *

+ *

+     * Map<Integer, List<String>> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetchGroups(BOOK.ID, BOOK.TITLE);
+     * 
+ */ + public static final > Collector>> toGroups() { + return toGroups(Record2::value1, Record2::value2); + } + + /** + * Create a collector that can collect {@link Record} resulting from a + * {@link ResultQuery} into a {@link Map} using the result of the argument + * {@link RecordMapper} as key collecting the records themselves into a list + * of values. + *

+ * For example: + *

+ *

+     * Map<Integer, List<Record2<Integer, String>>> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toGroups(r -> r.get(BOOK.ID)));
+     * 
+ *

+ * This is the same as the following: + *

+ *

+     * Map<Integer, List<Record2<Integer, String>>> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetchGroups(BOOK.ID);
+     * 
+ */ + @SuppressWarnings("unchecked") + public static final Collector>> toGroups(Function keyMapper) { + return Collectors.groupingBy( + keyMapper, + LinkedHashMap::new, + Collector.[], Result>of( + () -> new Result[1], + (x, r) -> { + if (x[0] == null) + x[0] = Internal.result(r); + + x[0].add(r); + }, + (r1, r2) -> { + r1[0].addAll(r2[0]); + return r1; + }, + r -> r[0] + ) + ); + } + + /** + * Create a collector that can collect {@link Record} resulting from a + * {@link ResultQuery} into a {@link Map} using the result of the argument + * {@link RecordMapper} as key collecting the result of another argument + * {@link RecordMapper} into a list of values. + *

+ * For example: + *

+ *

+     * Map<Integer, List<String>> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .collect(toGroups(r -> r.get(BOOK.ID), r -> r.get(BOOK.TITLE)));
+     * 
+ *

+ * This is the same as the following: + *

+ *

+     * Map<Integer, List<String>> books =
+     * ctx.select(BOOK.ID, BOOK.TITLE)
+     *    .from(BOOK)
+     *    .fetchGroups(BOOK.ID, BOOK.TITLE);
+     * 
+ */ + public static final Collector>> toGroups( + Function keyMapper, + Function valueMapper + ) { + return Collectors.groupingBy( + keyMapper, + LinkedHashMap::new, + Collectors.mapping( + valueMapper, + Collectors.toList() + ) + ); + } + /** diff --git a/jOOQ/src/main/java/org/jooq/Result.java b/jOOQ/src/main/java/org/jooq/Result.java index 96a43b54a0..8e2748ad8d 100644 --- a/jOOQ/src/main/java/org/jooq/Result.java +++ b/jOOQ/src/main/java/org/jooq/Result.java @@ -43,6 +43,8 @@ import java.sql.Statement; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collector; +import java.util.stream.Stream; import org.jooq.exception.DataAccessException; import org.jooq.exception.DataTypeException; @@ -310,6 +312,14 @@ public interface Result extends Fields, List, Attachable, F */ boolean isNotEmpty(); + /** + * Collect all records of this result. + *

+ * This is the same as calling {@link Stream#collect(Collector)} on + * {@link #stream()}. + */ + X collect(Collector collector); + /** * Return the generated result as a list of name/value maps. * diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index ce1824f470..759ee08415 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -804,7 +804,7 @@ abstract class AbstractRecord extends AbstractStore implements Record { @Override public final E into(Class type) { - return (E) Tools.configuration(this).recordMapperProvider().provide((FieldsImpl) fields.fields, type).map(this); + return (E) ((FieldsImpl) fields.fields).mapper(Tools.configuration(this), type).map(this); } // [#10191] Java and Kotlin can produce overloads for this method despite diff --git a/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java b/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java index 44b1b51cec..90c116da2a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java @@ -111,8 +111,7 @@ public abstract class DAOImpl, P, T> implements DAO * using Spring. It is not exposed in the public API. */ public /* non-final */ void setConfiguration(Configuration configuration) { - this.configuration = Tools.configuration(configuration); - this.mapper = this.configuration.recordMapperProvider().provide(table.recordType(), type); + this.mapper = table.recordType().mapper(this.configuration = Tools.configuration(configuration), type); } public final DSLContext ctx() { diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java index 2b2ed21e0c..80b7a14451 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java @@ -377,8 +377,7 @@ public class DefaultRecordMapper implements RecordMapper local = configuration.recordMapperProvider().provide(rowType, Object[].class); - delegate = r -> (E) Stream.of(local.map(r)); + delegate = r -> (E) Stream.of(rowType.mapper(configuration, Object[].class).map(r)); return; } @@ -837,14 +836,14 @@ public class DefaultRecordMapper implements RecordMapper) nestedMappingInfo.row.fields, member.getType())); + nestedMappingInfo.mappers.add( + nestedMappingInfo.row.fields.mapper(configuration, member.getType()) + ); for (Method method : getMatchingSetters(configuration, type, prefix, true)) - nestedMappingInfo.mappers.add(configuration - .recordMapperProvider() - .provide((RecordType) nestedMappingInfo.row.fields, method.getParameterTypes()[0])); + nestedMappingInfo.mappers.add( + nestedMappingInfo.row.fields.mapper(configuration, method.getParameterTypes()[0]) + ); }); } } @@ -1110,9 +1109,9 @@ public class DefaultRecordMapper implements RecordMapper) nestedMappingInfo[i].row.fields, parameterTypes[propertyIndexes[i] != null ? propertyIndexes[i] : i])); + nestedMappingInfo[i].mappers.add( + nestedMappingInfo[i].row.fields.mapper(configuration, parameterTypes[propertyIndexes[i] != null ? propertyIndexes[i] : i]) + ); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DelayedRecordMapper.java b/jOOQ/src/main/java/org/jooq/impl/DelayedRecordMapper.java new file mode 100644 index 0000000000..71d92a8e84 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DelayedRecordMapper.java @@ -0,0 +1,71 @@ +/* + * 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 + * + * http://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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.util.function.Function; + +import org.jooq.Record; +import org.jooq.RecordMapper; +import org.jooq.RecordType; + +/** + * A {@link RecordMapper} implementation offering access to a delegate record + * mapper whose initialisation is delayed until the first record, e.g. for + * {@link ResultQuery} implementations whose {@link RecordType} is unknown prior + * to execution. + * + * @author Lukas Eder + */ +final class DelayedRecordMapper implements RecordMapper { + + final Function, RecordMapper> init; + RecordMapper delegate; + + DelayedRecordMapper(Function, RecordMapper> init) { + this.init = init; + } + + @SuppressWarnings("unchecked") + @Override + public final E map(R record) { + if (delegate == null) + delegate = init.apply((RecordType) ((AbstractRecord) record).fields.fields); + + return delegate.map(record); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java b/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java index 10016e1749..a04c47c1b4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java @@ -50,11 +50,13 @@ import java.util.Collection; import java.util.List; import java.util.stream.Stream; +import org.jooq.Configuration; import org.jooq.Context; import org.jooq.DataType; import org.jooq.Field; import org.jooq.Name; import org.jooq.Record; +import org.jooq.RecordMapper; import org.jooq.RecordType; import org.jooq.Row; import org.jooq.SelectField; @@ -91,6 +93,36 @@ final class FieldsImpl extends AbstractQueryPart implements Re return fields.length; } + @Override + public final RecordMapper mapper(int fieldIndex) { + return r -> r.get(fieldIndex); + } + + @Override + public final RecordMapper mapper(String fieldName) { + return mapper(field(fieldName)); + } + + @Override + public final RecordMapper mapper(Name fieldName) { + return mapper(field(fieldName)); + } + + @Override + public final RecordMapper mapper(Field field) { + return (RecordMapper) mapper(indexOrFail(fieldsRow(), field)); + } + + @Override + public final RecordMapper mapper(Table table) { + return r -> r.into(table); + } + + @Override + public final RecordMapper mapper(Configuration configuration, Class type) { + return configuration.recordMapperProvider().provide(this, type); + } + @Override @SuppressWarnings("unchecked") public final Field field(Field field) { diff --git a/jOOQ/src/main/java/org/jooq/impl/Internal.java b/jOOQ/src/main/java/org/jooq/impl/Internal.java index 3ab6d2a92a..52225b308c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Internal.java +++ b/jOOQ/src/main/java/org/jooq/impl/Internal.java @@ -43,6 +43,7 @@ import static org.jooq.impl.ExpressionOperator.MULTIPLY; import static org.jooq.impl.ExpressionOperator.SUBTRACT; import static org.jooq.impl.Tools.nullSafe; +import java.lang.reflect.Array; import java.util.function.Consumer; import org.jooq.Binding; @@ -61,6 +62,7 @@ import org.jooq.ParamMode; import org.jooq.Parameter; // ... import org.jooq.Record; +import org.jooq.Result; import org.jooq.Row; import org.jooq.Schema; import org.jooq.Sequence; @@ -443,6 +445,10 @@ public final class Internal { }; } + /** + * JDK agnostic abstraction over {@link Class#arrayType()} and + * {@link Array#newInstance(Class, int)}. + */ @SuppressWarnings({ "unchecked", "unused" }) public static final Class arrayType(Class type) { @@ -450,6 +456,13 @@ public final class Internal { - return (Class) java.lang.reflect.Array.newInstance(type, 0).getClass(); + return (Class) Array.newInstance(type, 0).getClass(); + } + + /** + * Create an empty result from a {@link Record} using its row type. + */ + public static final Result result(R record) { + return new ResultImpl<>(Tools.configuration(record), ((AbstractRecord) record).fields); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java index 95a2826d92..847ee1e1d4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java @@ -38,15 +38,17 @@ package org.jooq.impl; +import static org.jooq.Records.toGroups; +import static org.jooq.Records.toList; +import static org.jooq.Records.toMap; +import static org.jooq.Records.toSet; import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.converterOrFail; import static org.jooq.impl.Tools.indexOrFail; -import static org.jooq.impl.Tools.map; import java.lang.reflect.Array; import java.sql.ResultSet; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -57,6 +59,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; +import java.util.stream.Collector; import org.jooq.Configuration; import org.jooq.Converter; @@ -88,6 +91,7 @@ import org.jooq.Record8; import org.jooq.Record9; import org.jooq.RecordHandler; import org.jooq.RecordMapper; +import org.jooq.Records; import org.jooq.Result; import org.jooq.TXTFormat; import org.jooq.Table; @@ -147,6 +151,11 @@ final class ResultImpl extends AbstractResult implements Re // XXX: Result API // ------------------------------------------------------------------------- + @Override + public final X collect(Collector collector) { + return stream().collect(collector); + } + @Override public final boolean isEmpty() { return records.isEmpty(); @@ -174,62 +183,70 @@ final class ResultImpl extends AbstractResult implements Re @Override public final List getValues(Field field) { - return (List) getValues(indexOrFail(fieldsRow(), field)); + return collect(toList(recordType().mapper(field))); } @Override public final List getValues(Field field, Class type) { + // [#9288] TODO: Refactor this return getValues(indexOrFail(fieldsRow(), field), type); } @Override public final List getValues(Field field, Converter converter) { + // [#9288] TODO: Refactor this return Convert.convert(getValues(field), converter); } @Override public final List getValues(int fieldIndex) { - return Tools.map(this, r -> r.get(fieldIndex)); + return collect(toList(recordType().mapper(fieldIndex))); } @Override public final List getValues(int fieldIndex, Class type) { + // [#9288] TODO: Refactor this Converter converter = converterOrFail(this, field(safeIndex(fieldIndex)).getType(), (Class) type); return Tools.map(this, r -> (U) converter.from(r.get(fieldIndex))); } @Override public final List getValues(int fieldIndex, Converter converter) { + // [#9288] TODO: Refactor this return Convert.convert(getValues(fieldIndex), converter); } @Override public final List getValues(String fieldName) { - return getValues(field(fieldName)); + return collect(toList(recordType().mapper(fieldName))); } @Override public final List getValues(String fieldName, Class type) { + // [#9288] TODO: Refactor this return getValues(indexOrFail(fieldsRow(), fieldName), type); } @Override public final List getValues(String fieldName, Converter converter) { + // [#9288] TODO: Refactor this return Convert.convert(getValues(fieldName), converter); } @Override public final List getValues(Name fieldName) { - return getValues(field(fieldName)); + return collect(toList(recordType().mapper(fieldName))); } @Override public final List getValues(Name fieldName, Class type) { + // [#9288] TODO: Refactor this return getValues(indexOrFail(fieldsRow(), fieldName), type); } @Override public final List getValues(Name fieldName, Converter converter) { + // [#9288] TODO: Refactor this return Convert.convert(getValues(fieldName), converter); } @@ -244,65 +261,42 @@ final class ResultImpl extends AbstractResult implements Re @Override public final Map intoMap(Field key) { - return intoMap0(indexOrFail(fieldsRow(), key)); + return collect(toMap(recordType().mapper(key))); } @Override public final Map intoMap(int keyFieldIndex) { - return intoMap0(keyFieldIndex); + return collect(toMap(recordType().mapper(keyFieldIndex))); } @Override public final Map intoMap(String keyFieldName) { - return intoMap(field(keyFieldName)); + return collect(toMap(recordType().mapper(keyFieldName))); } @Override public final Map intoMap(Name keyFieldName) { - return intoMap(field(keyFieldName)); - } - - private final Map intoMap0(int kIndex) { - Map map = new LinkedHashMap<>(); - - for (R record : this) - if (map.put((K) record.get(kIndex), record) != null) - throw new InvalidResultException("Key " + record.get(kIndex) + " is not unique in Result for " + this); - - return map; + return collect(toMap(recordType().mapper(keyFieldName))); } @Override public final Map intoMap(Field key, Field value) { - int kIndex = indexOrFail(fieldsRow(), key); - int vIndex = indexOrFail(fieldsRow(), value); - - return intoMap0(kIndex, vIndex); + return collect(toMap(recordType().mapper(key), recordType().mapper(value))); } @Override public final Map intoMap(int keyFieldIndex, int valueFieldIndex) { - return intoMap0(keyFieldIndex, valueFieldIndex); + return collect(toMap(recordType().mapper(keyFieldIndex), recordType().mapper(valueFieldIndex))); } @Override public final Map intoMap(String keyFieldName, String valueFieldName) { - return intoMap(field(keyFieldName), field(valueFieldName)); + return collect(toMap(recordType().mapper(keyFieldName), recordType().mapper(valueFieldName))); } @Override public final Map intoMap(Name keyFieldName, Name valueFieldName) { - return intoMap(field(keyFieldName), field(valueFieldName)); - } - - private final Map intoMap0(int kIndex, int vIndex) { - Map map = new LinkedHashMap<>(); - - for (R record : this) - if (map.put((K) record.get(kIndex), (V) record.get(vIndex)) != null) - throw new InvalidResultException("Key " + record.get(kIndex) + " is not unique in Result for " + this); - - return map; + return collect(toMap(recordType().mapper(keyFieldName), recordType().mapper(valueFieldName))); } @Override @@ -393,7 +387,7 @@ final class ResultImpl extends AbstractResult implements Re @Override public final Map, E> intoMap(Field[] keys, Class type) { - return intoMap(keys, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return intoMap(keys, recordType().mapper(Tools.configuration(this), type)); } @Override @@ -430,262 +424,172 @@ final class ResultImpl extends AbstractResult implements Re @Override public final Map intoMap(Class keyType) { - return intoMap(Tools.configuration(this).recordMapperProvider().provide(recordType(), keyType)); + return collect(toMap(recordType().mapper(Tools.configuration(this), keyType))); } @Override public final Map intoMap(Class keyType, Class valueType) { - return intoMap( - Tools.configuration(this).recordMapperProvider().provide(recordType(), keyType), - Tools.configuration(this).recordMapperProvider().provide(recordType(), valueType) - ); + return collect(toMap(recordType().mapper(Tools.configuration(this), keyType), recordType().mapper(Tools.configuration(this), valueType))); } @Override public final Map intoMap(Class keyType, RecordMapper valueMapper) { - return intoMap(Tools.configuration(this).recordMapperProvider().provide(recordType(), keyType), valueMapper); + return collect(toMap(recordType().mapper(Tools.configuration(this), keyType), valueMapper)); } @Override public final Map intoMap(RecordMapper keyMapper) { - Map map = new LinkedHashMap<>(); - - for (R record : this) { - K key = keyMapper.map(record); - - if (map.put(key, record) != null) - throw new InvalidResultException("Key list " + key + " is not unique in Result for " + this); - } - - return map; + return collect(toMap(keyMapper)); } @Override public final Map intoMap(RecordMapper keyMapper, Class valueType) { - return intoMap(keyMapper, Tools.configuration(this).recordMapperProvider().provide(recordType(), valueType)); + return collect(toMap(keyMapper, recordType().mapper(Tools.configuration(this), valueType))); } @Override public final Map intoMap(RecordMapper keyMapper, RecordMapper valueMapper) { - Map map = new LinkedHashMap<>(); - - for (R record : this) { - K key = keyMapper.map(record); - V value = valueMapper.map(record); - - if (map.put(key, value) != null) - throw new InvalidResultException("Key list " + key + " is not unique in Result for " + this); - } - - return map; + return collect(toMap(keyMapper, valueMapper)); } @Override public final Map intoMap(Table table) { - Map map = new LinkedHashMap<>(); - - for (R record : this) { - S key = record.into(table); - - if (map.put(key, record) != null) - throw new InvalidResultException("Key list " + key + " is not unique in Result for " + this); - } - - return map; + return collect(toMap(recordType().mapper(table))); } @Override public final Map intoMap(Table keyTable, Table valueTable) { - Map map = new LinkedHashMap<>(); - - for (R record : this) { - S key = record.into(keyTable); - T value = record.into(valueTable); - - if (map.put(key, value) != null) - throw new InvalidResultException("Key list " + key + " is not unique in Result for " + this); - } - - return map; + return collect(toMap(recordType().mapper(keyTable), recordType().mapper(valueTable))); } @Override public final Map intoMap(Table table, Class type) { - return intoMap(table, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toMap(recordType().mapper(table), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map intoMap(Table table, RecordMapper mapper) { - Map map = new LinkedHashMap<>(); - - for (R record : this) { - S key = record.into(table); - - if (map.put(key, mapper.map(record)) != null) - throw new InvalidResultException("Key list " + key + " is not unique in Result for " + this); - } - - return map; + return collect(toMap(recordType().mapper(table), mapper)); } @Override public final Map intoMap(int keyFieldIndex, Class type) { - return intoMap(keyFieldIndex, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toMap(recordType().mapper(keyFieldIndex), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map intoMap(String keyFieldName, Class type) { - return intoMap(keyFieldName, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toMap(recordType().mapper(keyFieldName), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map intoMap(Name keyFieldName, Class type) { - return intoMap(keyFieldName, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toMap(recordType().mapper(keyFieldName), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map intoMap(Field key, Class type) { - return intoMap(key, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toMap(recordType().mapper(key), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map intoMap(int keyFieldIndex, RecordMapper mapper) { - return intoMap0(keyFieldIndex, mapper); + return collect(toMap(recordType().mapper(keyFieldIndex), mapper)); } @Override public final Map intoMap(String keyFieldName, RecordMapper mapper) { - return intoMap(field(keyFieldName), mapper); + return collect(toMap(recordType().mapper(keyFieldName), mapper)); } @Override public final Map intoMap(Name keyFieldName, RecordMapper mapper) { - return intoMap(field(keyFieldName), mapper); + return collect(toMap(recordType().mapper(keyFieldName), mapper)); } @Override public final Map intoMap(Field key, RecordMapper mapper) { - return intoMap0(indexOrFail(fieldsRow(), key), mapper); - } - - private final Map intoMap0(int kIndex, RecordMapper mapper) { - Map map = new LinkedHashMap<>(); - - for (R record : this) - if (map.put((K) record.get(kIndex), mapper.map(record)) != null) - throw new InvalidResultException("Key " + record.get(kIndex) + " is not unique in Result for " + this); - - return map; + return collect(toMap(recordType().mapper(key), mapper)); } @Override public final Map> intoGroups(Field key) { - return intoGroups0(indexOrFail(fieldsRow(), key)); + return collect(toGroups(recordType().mapper(key))); } @Override public final Map> intoGroups(int keyFieldIndex) { - return intoGroups0(keyFieldIndex); + return collect(toGroups(recordType().mapper(keyFieldIndex))); } @Override public final Map> intoGroups(String keyFieldName) { - return intoGroups(field(keyFieldName)); + return collect(toGroups(recordType().mapper(keyFieldName))); } @Override public final Map> intoGroups(Name keyFieldName) { - return intoGroups(field(keyFieldName)); - } - - private final Map> intoGroups0(int keyFieldIndex) { - Map> map = new LinkedHashMap<>(); - - for (R record : this) - map.computeIfAbsent((K) record.get(keyFieldIndex), v -> new ResultImpl<>(configuration, fields)).add(record); - - return map; + return collect(toGroups(recordType().mapper(keyFieldName))); } @Override public final Map> intoGroups(Field key, Field value) { - int kIndex = indexOrFail(fieldsRow(), key); - int vIndex = indexOrFail(fieldsRow(), value); - - return intoGroups0(kIndex, vIndex); + return collect(toGroups(recordType().mapper(key), recordType().mapper(value))); } @Override public final Map> intoGroups(int keyFieldIndex, int valueFieldIndex) { - return (Map) intoGroups0(keyFieldIndex, valueFieldIndex); + return (Map) collect(toGroups(recordType().mapper(keyFieldIndex), recordType().mapper(valueFieldIndex))); } @Override public final Map> intoGroups(String keyFieldName, String valueFieldName) { - return (Map) intoGroups(field(keyFieldName), field(valueFieldName)); + return (Map) collect(toGroups(recordType().mapper(keyFieldName), recordType().mapper(valueFieldName))); } @Override public final Map> intoGroups(Name keyFieldName, Name valueFieldName) { - return (Map) intoGroups(field(keyFieldName), field(valueFieldName)); - } - - private final Map> intoGroups0(int kIndex, int vIndex) { - Map> map = new LinkedHashMap<>(); - - for (R record : this) - map.computeIfAbsent((K) record.get(kIndex), k -> new ArrayList<>()).add((V) record.get(vIndex)); - - return map; + return (Map) collect(toGroups(recordType().mapper(keyFieldName), recordType().mapper(valueFieldName))); } @Override public final Map> intoGroups(int keyFieldIndex, Class type) { - return intoGroups(keyFieldIndex, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toGroups(recordType().mapper(keyFieldIndex), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map> intoGroups(String keyFieldName, Class type) { - return intoGroups(keyFieldName, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toGroups(recordType().mapper(keyFieldName), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map> intoGroups(Name keyFieldName, Class type) { - return intoGroups(keyFieldName, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toGroups(recordType().mapper(keyFieldName), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map> intoGroups(Field key, Class type) { - return intoGroups(key, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toGroups(recordType().mapper(key), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map> intoGroups(Field key, RecordMapper mapper) { - return intoGroups0(indexOrFail(fieldsRow(), key), mapper); + return collect(toGroups(recordType().mapper(key), mapper)); } @Override public final Map> intoGroups(int keyFieldIndex, RecordMapper mapper) { - return intoGroups0(keyFieldIndex, mapper); + return collect(toGroups(recordType().mapper(keyFieldIndex), mapper)); } @Override public final Map> intoGroups(String keyFieldName, RecordMapper mapper) { - return intoGroups(field(keyFieldName), mapper); + return collect(toGroups(recordType().mapper(keyFieldName), mapper)); } @Override public final Map> intoGroups(Name keyFieldName, RecordMapper mapper) { - return intoGroups(field(keyFieldName), mapper); - } - - private final Map> intoGroups0(int keyFieldIndex, RecordMapper mapper) { - Map> map = new LinkedHashMap<>(); - - for (R record : this) - map.computeIfAbsent((K) record.get(keyFieldIndex), k -> new ArrayList<>()).add(mapper.map(record)); - - return map; + return collect(toGroups(recordType().mapper(keyFieldName), mapper)); } @Override @@ -714,7 +618,7 @@ final class ResultImpl extends AbstractResult implements Re for (Field field : keysRow.fields.fields) Tools.copyValue(key, field, record, field); - map.computeIfAbsent(key, k -> new ResultImpl<>(configuration(), this.fields)).add(record); + map.computeIfAbsent(key, k -> new ResultImpl<>(Tools.configuration(this), this.fields)).add(record); } return map; @@ -751,7 +655,7 @@ final class ResultImpl extends AbstractResult implements Re for (Field field : valuesRow.fields.fields) Tools.copyValue(value, field, record, field); - map.computeIfAbsent(key, k -> new ResultImpl<>(configuration(), valuesRow)).add(value); + map.computeIfAbsent(key, k -> new ResultImpl<>(Tools.configuration(this), valuesRow)).add(value); } return map; @@ -759,22 +663,22 @@ final class ResultImpl extends AbstractResult implements Re @Override public final Map> intoGroups(int[] keyFieldIndexes, Class type) { - return intoGroups(keyFieldIndexes, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return intoGroups(keyFieldIndexes, recordType().mapper(Tools.configuration(this), type)); } @Override public final Map> intoGroups(String[] keyFieldNames, Class type) { - return intoGroups(keyFieldNames, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return intoGroups(keyFieldNames, recordType().mapper(Tools.configuration(this), type)); } @Override public final Map> intoGroups(Name[] keyFieldNames, Class type) { - return intoGroups(keyFieldNames, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return intoGroups(keyFieldNames, recordType().mapper(Tools.configuration(this), type)); } @Override public final Map> intoGroups(Field[] keys, Class type) { - return intoGroups(keys, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return intoGroups(keys, recordType().mapper(Tools.configuration(this), type)); } @Override @@ -811,80 +715,58 @@ final class ResultImpl extends AbstractResult implements Re @Override public final Map> intoGroups(Class keyType) { - return intoGroups(Tools.configuration(this).recordMapperProvider().provide(recordType(), keyType)); + return collect(toGroups(recordType().mapper(Tools.configuration(this), keyType))); } @Override public final Map> intoGroups(Class keyType, Class valueType) { - return intoGroups( - Tools.configuration(this).recordMapperProvider().provide(recordType(), keyType), - Tools.configuration(this).recordMapperProvider().provide(recordType(), valueType) - ); + return collect(toGroups(recordType().mapper(Tools.configuration(this), keyType), recordType().mapper(Tools.configuration(this), valueType))); } @Override public final Map> intoGroups(Class keyType, RecordMapper valueMapper) { - return intoGroups(Tools.configuration(this).recordMapperProvider().provide(recordType(), keyType), valueMapper); + return collect(toGroups(recordType().mapper(Tools.configuration(this), keyType), valueMapper)); } @Override public final Map> intoGroups(RecordMapper keyMapper) { - Map> map = new LinkedHashMap<>(); - - for (R record : this) - map.computeIfAbsent(keyMapper.map(record), k -> new ResultImpl<>(configuration(), fields)).add(record); - - return map; + return collect(toGroups(keyMapper)); } @Override public final Map> intoGroups(RecordMapper keyMapper, Class valueType) { - return intoGroups(keyMapper, Tools.configuration(this).recordMapperProvider().provide(recordType(), valueType)); + return collect(toGroups(keyMapper, recordType().mapper(Tools.configuration(this), valueType))); } @Override public final Map> intoGroups(RecordMapper keyMapper, RecordMapper valueMapper) { - Map> map = new LinkedHashMap<>(); - - for (R record : this) - map.computeIfAbsent(keyMapper.map(record), k -> new ArrayList<>()).add(valueMapper.map(record)); - - return map; + return collect(toGroups(keyMapper, valueMapper)); } @Override public final Map> intoGroups(Table table) { - Map> map = new LinkedHashMap<>(); - - for (R record : this) - map.computeIfAbsent(record.into(table), k -> new ResultImpl<>(configuration(), this.fields)).add(record); - - return map; + return collect(toGroups(recordType().mapper(table))); } @Override public final Map> intoGroups(Table keyTable, Table valueTable) { + // [#9288] TODO: Can't use collect(toGroups(recordType().mapper(keyTable), recordType().mapper(valueTable))) yet Map> map = new LinkedHashMap<>(); for (R record : this) - map.computeIfAbsent(record.into(keyTable), k -> DSL.using(configuration()).newResult(valueTable)).add(record.into(valueTable)); + map.computeIfAbsent(record.into(keyTable), k -> DSL.using(Tools.configuration(this)).newResult(valueTable)).add(record.into(valueTable)); return map; } @Override public final Map> intoGroups(Table table, Class type) { - return intoGroups(table, Tools.configuration(this).recordMapperProvider().provide(recordType(), type)); + return collect(toGroups(recordType().mapper(table), recordType().mapper(Tools.configuration(this), type))); } @Override public final Map> intoGroups(Table table, RecordMapper mapper) { - Map> map = new LinkedHashMap<>(); - - for (R record : this) - map.computeIfAbsent(record.into(table), k -> new ArrayList<>()).add(mapper.map(record)); - - return map; + return collect(toGroups(recordType().mapper(table), mapper)); } @Override @@ -960,71 +842,74 @@ final class ResultImpl extends AbstractResult implements Re @Override public final Set intoSet(RecordMapper mapper) { - Set result = new LinkedHashSet<>(); - - for (R record : this) - result.add(mapper.map(record)); - - return result; + return collect(toSet(mapper)); } @Override public final Set intoSet(int fieldIndex) { - return new LinkedHashSet<>(getValues(fieldIndex)); + return collect(toSet(recordType().mapper(fieldIndex))); } @Override public final Set intoSet(int fieldIndex, Class type) { + // [#9288] TODO: Refactor this return new LinkedHashSet<>(getValues(fieldIndex, type)); } @Override public final Set intoSet(int fieldIndex, Converter converter) { + // [#9288] TODO: Refactor this return new LinkedHashSet<>(getValues(fieldIndex, converter)); } @Override public final Set intoSet(String fieldName) { - return new LinkedHashSet<>(getValues(fieldName)); + return collect(toSet(recordType().mapper(fieldName))); } @Override public final Set intoSet(String fieldName, Class type) { + // [#9288] TODO: Refactor this return new LinkedHashSet<>(getValues(fieldName, type)); } @Override public final Set intoSet(String fieldName, Converter converter) { + // [#9288] TODO: Refactor this return new LinkedHashSet<>(getValues(fieldName, converter)); } @Override public final Set intoSet(Name fieldName) { - return new LinkedHashSet<>(getValues(fieldName)); + return collect(toSet(recordType().mapper(fieldName))); } @Override public final Set intoSet(Name fieldName, Class type) { + // [#9288] TODO: Refactor this return new LinkedHashSet<>(getValues(fieldName, type)); } @Override public final Set intoSet(Name fieldName, Converter converter) { + // [#9288] TODO: Refactor this return new LinkedHashSet<>(getValues(fieldName, converter)); } @Override public final Set intoSet(Field field) { - return new LinkedHashSet<>(getValues(field)); + return collect(toSet(recordType().mapper(field))); } @Override public final Set intoSet(Field field, Class type) { + // [#9288] TODO: Refactor this return new LinkedHashSet<>(getValues(field, type)); } @Override public final Set intoSet(Field field, Converter converter) { + // [#9288] TODO: Refactor this return new LinkedHashSet<>(getValues(field, converter)); } @@ -1154,13 +1039,12 @@ final class ResultImpl extends AbstractResult implements Re @Override public final List into(Class type) { - RecordMapper mapper = Tools.configuration(this).recordMapperProvider().provide(recordType(), type); - return Tools.map(this, mapper::map); + return Tools.map(this, recordType().mapper(Tools.configuration(this), type)::map); } @Override public final Result into(Table table) { - Result list = new ResultImpl<>(configuration(), (AbstractRow) table.fieldsRow()); + Result list = new ResultImpl<>(Tools.configuration(this), (AbstractRow) table.fieldsRow()); for (R record : this) list.add(record.into(table)); diff --git a/jOOQ/src/main/java/org/jooq/impl/ResultQueryTrait.java b/jOOQ/src/main/java/org/jooq/impl/ResultQueryTrait.java index e53bc1f1aa..1aa6cea381 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ResultQueryTrait.java +++ b/jOOQ/src/main/java/org/jooq/impl/ResultQueryTrait.java @@ -37,6 +37,9 @@ */ package org.jooq.impl; +import static org.jooq.Records.toGroups; +import static org.jooq.Records.toMap; +import static org.jooq.Records.toSet; import static org.jooq.impl.Tools.blocking; import java.lang.reflect.Array; @@ -52,13 +55,14 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; -// ... import java.util.stream.Collector; import java.util.stream.Stream; +import org.jooq.Configuration; import org.jooq.Converter; import org.jooq.Cursor; import org.jooq.Field; +import org.jooq.Mappable; import org.jooq.Name; import org.jooq.QueryPartInternal; import org.jooq.Record; @@ -86,6 +90,7 @@ import org.jooq.Record8; import org.jooq.Record9; import org.jooq.RecordHandler; import org.jooq.RecordMapper; +import org.jooq.Records; import org.jooq.Result; import org.jooq.ResultQuery; import org.jooq.Results; @@ -105,7 +110,7 @@ import io.r2dbc.spi.ConnectionFactory; * * @author Lukas Eder */ -interface ResultQueryTrait extends QueryPartInternal, ResultQuery { +interface ResultQueryTrait extends QueryPartInternal, ResultQuery, Mappable { @Override default ResultQuery coerce(Field... fields) { @@ -782,82 +787,82 @@ interface ResultQueryTrait extends QueryPartInternal, ResultQu @Override default Map fetchMap(Field key) { - return fetch().intoMap(key); + return collect(toMap(mapper(key))); } @Override default Map fetchMap(int keyFieldIndex) { - return fetch().intoMap(keyFieldIndex); + return collect(toMap(mapper(keyFieldIndex))); } @Override default Map fetchMap(String keyFieldName) { - return fetch().intoMap(keyFieldName); + return collect(toMap(mapper(keyFieldName))); } @Override default Map fetchMap(Name keyFieldName) { - return fetch().intoMap(keyFieldName); + return collect(toMap(mapper(keyFieldName))); } @Override default Map fetchMap(Field key, Field value) { - return fetch().intoMap(key, value); + return collect(toMap(mapper(key), mapper(value))); } @Override default Map fetchMap(int keyFieldIndex, int valueFieldIndex) { - return fetch().intoMap(keyFieldIndex, valueFieldIndex); + return collect(toMap(mapper(keyFieldIndex), mapper(valueFieldIndex))); } @Override default Map fetchMap(String keyFieldName, String valueFieldName) { - return fetch().intoMap(keyFieldName, valueFieldName); + return collect(toMap(mapper(keyFieldName), mapper(valueFieldName))); } @Override default Map fetchMap(Name keyFieldName, Name valueFieldName) { - return fetch().intoMap(keyFieldName, valueFieldName); + return collect(toMap(mapper(keyFieldName), mapper(valueFieldName))); } @Override default Map fetchMap(Field key, Class type) { - return fetch().intoMap(key, type); + return collect(toMap(mapper(key), mapper(Tools.configuration(this), type))); } @Override default Map fetchMap(int keyFieldIndex, Class type) { - return fetch().intoMap(keyFieldIndex, type); + return collect(toMap(mapper(keyFieldIndex), mapper(Tools.configuration(this), type))); } @Override default Map fetchMap(String keyFieldName, Class type) { - return fetch().intoMap(keyFieldName, type); + return collect(toMap(mapper(keyFieldName), mapper(Tools.configuration(this), type))); } @Override default Map fetchMap(Name keyFieldName, Class type) { - return fetch().intoMap(keyFieldName, type); + return collect(toMap(mapper(keyFieldName), mapper(Tools.configuration(this), type))); } @Override default Map fetchMap(Field key, RecordMapper mapper) { - return fetch().intoMap(key, mapper); + return collect(toMap(mapper(key), mapper)); } @Override default Map fetchMap(int keyFieldIndex, RecordMapper mapper) { - return fetch().intoMap(keyFieldIndex, mapper); + return collect(toMap(mapper(keyFieldIndex), mapper)); } @Override default Map fetchMap(String keyFieldName, RecordMapper mapper) { - return fetch().intoMap(keyFieldName, mapper); + return collect(toMap(mapper(keyFieldName), mapper)); } @Override default Map fetchMap(Name keyFieldName, RecordMapper mapper) { - return fetch().intoMap(keyFieldName, mapper); + return collect(toMap(mapper(keyFieldName), mapper)); } @Override @@ -942,52 +947,52 @@ interface ResultQueryTrait extends QueryPartInternal, ResultQu @Override default Map fetchMap(Class keyType) { - return fetch().intoMap(keyType); + return collect(toMap(mapper(Tools.configuration(this), keyType))); } @Override default Map fetchMap(Class keyType, Class valueType) { - return fetch().intoMap(keyType, valueType); + return collect(toMap(mapper(Tools.configuration(this), keyType), mapper(Tools.configuration(this), valueType))); } @Override default Map fetchMap(Class keyType, RecordMapper valueMapper) { - return fetch().intoMap(keyType, valueMapper); + return collect(toMap(mapper(Tools.configuration(this), keyType), valueMapper)); } @Override default Map fetchMap(RecordMapper keyMapper) { - return fetch().intoMap(keyMapper); + return collect(toMap(keyMapper)); } @Override default Map fetchMap(RecordMapper keyMapper, Class valueType) { - return fetch().intoMap(keyMapper, valueType); + return collect(toMap(keyMapper, mapper(Tools.configuration(this), valueType))); } @Override default Map fetchMap(RecordMapper keyMapper, RecordMapper valueMapper) { - return fetch().intoMap(keyMapper, valueMapper); + return collect(toMap(keyMapper, valueMapper)); } @Override default Map fetchMap(Table table) { - return fetch().intoMap(table); + return collect(toMap(mapper(table))); } @Override default Map fetchMap(Table keyTable, Table valueTable) { - return fetch().intoMap(keyTable, valueTable); + return collect(toMap(mapper(keyTable), mapper(valueTable))); } @Override default Map fetchMap(Table table, Class type) { - return fetch().intoMap(table, type); + return collect(toMap(mapper(table), mapper(Tools.configuration(this), type))); } @Override default Map fetchMap(Table table, RecordMapper mapper) { - return fetch().intoMap(table, mapper); + return collect(toMap(mapper(table), mapper)); } @Override @@ -997,82 +1002,82 @@ interface ResultQueryTrait extends QueryPartInternal, ResultQu @Override default Map> fetchGroups(Field key) { - return fetch().intoGroups(key); + return collect(toGroups(mapper(key))); } @Override default Map> fetchGroups(int keyFieldIndex) { - return fetch().intoGroups(keyFieldIndex); + return collect(toGroups(mapper(keyFieldIndex))); } @Override default Map> fetchGroups(String keyFieldName) { - return fetch().intoGroups(keyFieldName); + return collect(toGroups(mapper(keyFieldName))); } @Override default Map> fetchGroups(Name keyFieldName) { - return fetch().intoGroups(keyFieldName); + return collect(toGroups(mapper(keyFieldName))); } @Override default Map> fetchGroups(Field key, Field value) { - return fetch().intoGroups(key, value); + return collect(toGroups(mapper(key), mapper(value))); } @Override default Map> fetchGroups(int keyFieldIndex, int valueFieldIndex) { - return fetch().intoGroups(keyFieldIndex, valueFieldIndex); + return (Map) collect(toGroups(mapper(keyFieldIndex), mapper(valueFieldIndex))); } @Override default Map> fetchGroups(String keyFieldName, String valueFieldName) { - return fetch().intoGroups(keyFieldName, valueFieldName); + return (Map) collect(toGroups(mapper(keyFieldName), mapper(valueFieldName))); } @Override default Map> fetchGroups(Name keyFieldName, Name valueFieldName) { - return fetch().intoGroups(keyFieldName, valueFieldName); + return (Map) collect(toGroups(mapper(keyFieldName), mapper(valueFieldName))); } @Override default Map> fetchGroups(Field key, Class type) { - return fetch().intoGroups(key, type); + return collect(toGroups(mapper(key), mapper(Tools.configuration(this), type))); } @Override default Map> fetchGroups(int keyFieldIndex, Class type) { - return fetch().intoGroups(keyFieldIndex, type); + return collect(toGroups(mapper(keyFieldIndex), mapper(Tools.configuration(this), type))); } @Override default Map> fetchGroups(String keyFieldName, Class type) { - return fetch().intoGroups(keyFieldName, type); + return collect(toGroups(mapper(keyFieldName), mapper(Tools.configuration(this), type))); } @Override default Map> fetchGroups(Name keyFieldName, Class type) { - return fetch().intoGroups(keyFieldName, type); + return collect(toGroups(mapper(keyFieldName), mapper(Tools.configuration(this), type))); } @Override default Map> fetchGroups(Field key, RecordMapper mapper) { - return fetch().intoGroups(key, mapper); + return collect(toGroups(mapper(key), mapper)); } @Override default Map> fetchGroups(int keyFieldIndex, RecordMapper mapper) { - return fetch().intoGroups(keyFieldIndex, mapper); + return collect(toGroups(mapper(keyFieldIndex), mapper)); } @Override default Map> fetchGroups(String keyFieldName, RecordMapper mapper) { - return fetch().intoGroups(keyFieldName, mapper); + return collect(toGroups(mapper(keyFieldName), mapper)); } @Override default Map> fetchGroups(Name keyFieldName, RecordMapper mapper) { - return fetch().intoGroups(keyFieldName, mapper); + return collect(toGroups(mapper(keyFieldName), mapper)); } @Override @@ -1157,52 +1162,53 @@ interface ResultQueryTrait extends QueryPartInternal, ResultQu @Override default Map> fetchGroups(Class keyType) { - return fetch().intoGroups(keyType); + return collect(toGroups(mapper(Tools.configuration(this), keyType))); } @Override default Map> fetchGroups(Class keyType, Class valueType) { - return fetch().intoGroups(keyType, valueType); + return collect(toGroups(mapper(Tools.configuration(this), keyType), mapper(Tools.configuration(this), valueType))); } @Override default Map> fetchGroups(Class keyType, RecordMapper valueMapper) { - return fetch().intoGroups(keyType, valueMapper); + return collect(toGroups(mapper(Tools.configuration(this), keyType), valueMapper)); } @Override default Map> fetchGroups(RecordMapper keyMapper) { - return fetch().intoGroups(keyMapper); + return collect(toGroups(keyMapper)); } @Override default Map> fetchGroups(RecordMapper keyMapper, Class valueType) { - return fetch().intoGroups(keyMapper, valueType); + return collect(toGroups(keyMapper, mapper(Tools.configuration(this), valueType))); } @Override default Map> fetchGroups(RecordMapper keyMapper, RecordMapper valueMapper) { - return fetch().intoGroups(keyMapper, valueMapper); + return collect(toGroups(keyMapper, valueMapper)); } @Override default Map> fetchGroups(Table table) { - return fetch().intoGroups(table); + return collect(toGroups(mapper(table))); } @Override default Map> fetchGroups(Table keyTable, Table valueTable) { + // [#9288] TODO: Can't use collect(toGroups(recordType().mapper(keyTable), recordType().mapper(valueTable))) yet return fetch().intoGroups(keyTable, valueTable); } @Override default Map> fetchGroups(Table table, Class type) { - return fetch().intoGroups(table, type); + return collect(toGroups(mapper(table), mapper(Tools.configuration(this), type))); } @Override default Map> fetchGroups(Table table, RecordMapper mapper) { - return fetch().intoGroups(table, mapper); + return collect(toGroups(mapper(table), mapper)); } @Override @@ -1293,66 +1299,74 @@ interface ResultQueryTrait extends QueryPartInternal, ResultQu @Override default Set fetchSet(RecordMapper mapper) { - return fetch().intoSet(mapper); + return collect(toSet(mapper)); } @Override default Set fetchSet(int fieldIndex) { - return fetch().intoSet(fieldIndex); + return collect(toSet(mapper(fieldIndex))); } @Override default Set fetchSet(int fieldIndex, Class type) { + // [#9288] TODO: Refactor this return fetch().intoSet(fieldIndex, type); } @Override default Set fetchSet(int fieldIndex, Converter converter) { + // [#9288] TODO: Refactor this return fetch().intoSet(fieldIndex, converter); } @Override default Set fetchSet(String fieldName) { - return fetch().intoSet(fieldName); + return collect(toSet(mapper(fieldName))); } @Override default Set fetchSet(String fieldName, Class type) { + // [#9288] TODO: Refactor this return fetch().intoSet(fieldName, type); } @Override default Set fetchSet(String fieldName, Converter converter) { + // [#9288] TODO: Refactor this return fetch().intoSet(fieldName, converter); } @Override default Set fetchSet(Name fieldName) { - return fetch().intoSet(fieldName); + return collect(toSet(mapper(fieldName))); } @Override default Set fetchSet(Name fieldName, Class type) { + // [#9288] TODO: Refactor this return fetch().intoSet(fieldName, type); } @Override default Set fetchSet(Name fieldName, Converter converter) { + // [#9288] TODO: Refactor this return fetch().intoSet(fieldName, converter); } @Override default Set fetchSet(Field field) { - return fetch().intoSet(field); + return collect(toSet(mapper(field))); } @Override default Set fetchSet(Field field, Class type) { + // [#9288] TODO: Refactor this return fetch().intoSet(field, type); } @Override default Set fetchSet(Field field, Converter converter) { + // [#9288] TODO: Refactor this return fetch().intoSet(field, converter); } @Override @@ -1387,4 +1401,35 @@ interface ResultQueryTrait extends QueryPartInternal, ResultQu return false; } + + + @Override + default RecordMapper mapper(int fieldIndex) { + return new DelayedRecordMapper<>(t -> t.mapper(fieldIndex)); + } + + @Override + default RecordMapper mapper(String fieldName) { + return new DelayedRecordMapper<>(t -> t.mapper(fieldName)); + } + + @Override + default RecordMapper mapper(Name fieldName) { + return new DelayedRecordMapper<>(t -> t.mapper(fieldName)); + } + + @Override + default RecordMapper mapper(Field field) { + return new DelayedRecordMapper<>(t -> t.mapper(field)); + } + + @Override + default RecordMapper mapper(Table table) { + return new DelayedRecordMapper<>(t -> t.mapper(table)); + } + + @Override + default RecordMapper mapper(Configuration configuration, Class type) { + return new DelayedRecordMapper<>(t -> t.mapper(configuration, type)); + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 5271d3ae23..5d72bdacbb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -56,7 +56,6 @@ import static org.jooq.impl.CacheType.REFLECTION_CACHE_HAS_COLUMN_ANNOTATIONS; // ... // ... // ... -import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DERBY; // ... import static org.jooq.SQLDialect.FIREBIRD; @@ -247,6 +246,7 @@ import org.jooq.EnumType; import org.jooq.ExecuteContext; import org.jooq.ExecuteListener; import org.jooq.Field; +import org.jooq.Fields; import org.jooq.ForeignKey; import org.jooq.JSON; import org.jooq.JSONB; @@ -262,7 +262,6 @@ import org.jooq.QueryPart; import org.jooq.Record; import org.jooq.Record1; import org.jooq.RecordQualifier; -import org.jooq.RecordType; import org.jooq.RenderContext; import org.jooq.RenderContext.CastMode; import org.jooq.Result; @@ -1687,11 +1686,11 @@ final class Tools { return map(values, (v, i) -> field(v, types[i]), Field[]::new); } - static final IllegalArgumentException indexFail(Row row, Field field) { + static final IllegalArgumentException indexFail(Fields row, Field field) { return new IllegalArgumentException("Field (" + field + ") is not contained in Row " + row); } - static final int indexOrFail(Row row, Field field) { + static final int indexOrFail(Fields row, Field field) { int result = row.indexOf(field); if (result < 0) @@ -1700,11 +1699,11 @@ final class Tools { return result; } - static final IllegalArgumentException indexFail(Row row, String fieldName) { + static final IllegalArgumentException indexFail(Fields row, String fieldName) { throw new IllegalArgumentException("Field (" + fieldName + ") is not contained in Row " + row); } - static final int indexOrFail(Row row, String fieldName) { + static final int indexOrFail(Fields row, String fieldName) { int result = row.indexOf(fieldName); if (result < 0) @@ -1713,11 +1712,11 @@ final class Tools { return result; } - static final IllegalArgumentException indexFail(Row row, Name fieldName) { + static final IllegalArgumentException indexFail(Fields row, Name fieldName) { throw new IllegalArgumentException("Field (" + fieldName + ") is not contained in Row " + row); } - static final int indexOrFail(Row row, Name fieldName) { + static final int indexOrFail(Fields row, Name fieldName) { int result = row.indexOf(fieldName); if (result < 0) @@ -1726,45 +1725,6 @@ final class Tools { return result; } - /** - * A utility method that fails with an exception if - * {@link RecordType#indexOf(Field)} doesn't return any index. - */ - static final int indexOrFail(RecordType row, Field field) { - int result = row.indexOf(field); - - if (result < 0) - throw new IllegalArgumentException("Field (" + field + ") is not contained in RecordType " + row); - - return result; - } - - /** - * A utility method that fails with an exception if - * {@link RecordType#indexOf(String)} doesn't return any index. - */ - static final int indexOrFail(RecordType row, String fieldName) { - int result = row.indexOf(fieldName); - - if (result < 0) - throw new IllegalArgumentException("Field (" + fieldName + ") is not contained in RecordType " + row); - - return result; - } - - /** - * A utility method that fails with an exception if - * {@link RecordType#indexOf(Name)} doesn't return any index. - */ - static final int indexOrFail(RecordType row, Name fieldName) { - int result = row.indexOf(fieldName); - - if (result < 0) - throw new IllegalArgumentException("Field (" + fieldName + ") is not contained in RecordType " + row); - - return result; - } - private static final List newListWithCapacity(Iterable it) { return it instanceof Collection ? new ArrayList<>(((Collection) it).size()) : new ArrayList<>(); }