ptupitsyn commented on a change in pull request #455:
URL: https://github.com/apache/ignite-3/pull/455#discussion_r767708455



##########
File path: modules/api/src/main/java/org/apache/ignite/table/KeyValueView.java
##########
@@ -37,19 +38,76 @@
     /**
      * Gets a value associated with the given key.
      *
+     * <p>Note: If the value mapper implies a value can be {@code null}, then 
a suitable method
+     * {@link #getNullable(Object)} must be used instead.
+     *
      * @param key A key which associated the value is to be returned. The key 
cannot be {@code null}.
      * @return Value or {@code null}, if it does not exist.
+     * @throws IllegalStateException If value for the key exists, and it is 
{@code null}.
+     * @see #getNullable(Object)
      */
     V get(@NotNull K key);
 
     /**
      * Asynchronously gets a value associated with the given key.
      *
+     * <p>Note: If the value mapper implies a value can be {@code null}, then 
a suitable method
+     * {@link #getNullableAsync(Object)} must be used instead.
+     *
      * @param key A key which associated the value is to be returned. The key 
cannot be {@code null}.
      * @return Future representing pending completion of the operation.
+     * @see #getNullableAsync(Object)
+     * @see #get(Object)
      */
     @NotNull CompletableFuture<V> getAsync(@NotNull K key);
 
+    /**
+     * Gets a nullable value associated with the given key.
+     *
+     * @param key A key which associated the value is to be returned. The key 
cannot be {@code null}.
+     * @return Wrapped nullable value or {@code null}, if it does not exist.
+     */
+    default NullableValue<V> getNullable(K key) {
+        throw new UnsupportedOperationException("Not implemented yet.");

Review comment:
       Can you please add a comment with the ticket number?

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,
+ * annotation, and so on.
+ *
+ * @param <T> Type of which objects the mapper handles.
+ * @apiNote Implementation shouldn't use this interface directly, please, use 
{@link PojoMapper} or {@link OneColumnMapper}
+ *         instead.
+ * @see PojoMapper
+ * @see OneColumnMapper
  */
 public interface Mapper<T> {
     /**
-     * Creates a mapper for a class.
+     * Shortcut method creates a mapper for class. Natively supported types 
will be mapped to a single schema column, otherwise individual
+     * object fields will be mapped to columns with the same name.
+     *
+     * <p>Note: Natively supported types can be mapped to a single key/value 
column, otherwise table operation will ends up with exception.
+     * Use {@link #of(Class, String)} instead, to map to a concrete column 
name.
      *
-     * @param cls Key class.
-     * @param <K> Key type.
-     * @return Mapper.
+     * @param cls Target type.
+     * @return Mapper for key objects representing a single key column.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
      */
-    static <K> Mapper<K> of(Class<K> cls) {
-        return identity(cls);
+    static <O> Mapper<O> of(Class<O> cls) {
+        if (nativelySupported(cls)) {
+            // TODO: Cache mappers (IGNITE-16094).
+            return new OneColumnMapperImpl<>(cls, null, null);
+        } else {
+            return builder(cls).automap().build();
+        }
     }
 
     /**
-     * Creates a mapper builder for a class.
+     * Creates a mapper for the case when an object represents an only 
one-column.
+     *
+     * <p>The mapper can be used as key, value, or record mapper. However, 
single column record looks as degraded case.

Review comment:
       ```suggestion
        * <p>The mapper can be used as a key, value, or record mapper. However, 
a single-column record looks like a degraded case.
   ```

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");
+        } else if (columnToFields.put(Objects.requireNonNull(columnName), 
requireValidField(fieldName)) != null) {
             throw new IllegalArgumentException("Mapping for a column already 
exists: " + columnName);
         }
 
+        for (int i = 0; i < fieldColumnPairs.length; i += 2) {
+            if (columnToFields.put(Objects.requireNonNull(fieldColumnPairs[i + 
1]), requireValidField(fieldColumnPairs[i])) != null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + columnName);
+            }
+        }
+
         return this;
     }
 
     /**
-     * Map a field to a type of given class.
+     * Maps a field to a column with using type converter. The value will be 
converted before write to and after read from column using
+     * provided converter.
      *
-     * @param fieldName   Field name.
-     * @param targetClass Target class.
-     * @return {@code this} for chaining.
+     * @param <ObjectT>  MUST match the object field type, if the individual 
field mapped to given column.
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.
+     * @param fieldName  Field name.
+     * @param columnName Column name.
+     * @param converter  Converter for objects of {@link ColumnT} and {@link 
ObjectT}.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, Class<?> 
targetClass) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+    public <ObjectT, ColumnT> MapperBuilder<T> map(
+            @NotNull String fieldName,
+            @NotNull String columnName,
+            @NotNull TypeConverter<ObjectT, ColumnT> converter
+    ) {
+        map(fieldName, columnName);
+        convert(converter, columnName);
+
+        return this;
     }
 
     /**
-     * Adds a functional mapping for a field, the result depends on function 
call for every particular row.
+     * Adds a manual functional mapping for an object and row represented by 
tuple.
      *
-     * @param fieldName       Field name.
-     * @param mappingFunction Mapper function.
+     * @param objectToRow Object to tuple function.
+     * @param rowToObject Tuple to object function.
      * @return {@code this} for chaining.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, Function<Tuple, 
Object> mappingFunction) {
+    public MapperBuilder<T> map(Function<T, Tuple> objectToRow, 
Function<Tuple, T> rowToObject) {
+        ensureNotStale();
+
         throw new UnsupportedOperationException("Not implemented yet.");
     }
 
     /**
-     * Sets a target class to deserialize to.
+     * Sets a converter for a column, which value must be converted before 
write/after read.
+     *
+     * @param converter  Converter for objects of {@link ColumnT} and {@link 
ObjectT}.
+     * @param columnName Column name.
+     * @param <ObjectT>  MUST match the object field type, if a field mapped 
to given column, or the object type {@link T}
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.
+     */
+    public <ObjectT, ColumnT> MapperBuilder<T> convert(
+            @NotNull TypeConverter<ObjectT, ColumnT> converter,
+            @NotNull String columnName
+    ) {
+        ensureNotStale();
+
+        if (columnConverters.put(columnName, converter) != null) {
+            throw new IllegalArgumentException("Column converter already 
exists: " + columnName);
+        }
+
+        return this;
+    }
+
+    /**
+     * Make mapper treat missed columns as they mapped to the field of the 
same name. If class {@link T} has no field for the missed column,
+     * then left the column unmapped.
      *
-     * @param targetClass Target class.
      * @return {@code this} for chaining.
      */
-    public MapperBuilder<T> deserializeTo(@NotNull Class<?> targetClass) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+    public MapperBuilder<T> automap() {
+        ensureNotStale();
+
+        automapFlag = true;
+
+        return this;
     }
 
     /**
      * Builds mapper.
      *
      * @return Mapper.
-     * @throws IllegalStateException if nothing were mapped or more than one 
column were mapped to the same field.
+     * @throws IllegalStateException if nothing were mapped, or more than one 
column were mapped to the same field.

Review comment:
       ```suggestion
        * @throws IllegalStateException if nothing was mapped, or more than one 
column was mapped to the same field.
   ```

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,

Review comment:
       What will happen in this case? An exception?

##########
File path: modules/api/src/main/java/org/apache/ignite/lang/NullableValue.java
##########
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.ignite.lang;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Nullable value interface, which instances wraps real value, is used to 
distinct cases: 'value is absent', and 'value is {@code null}'.

Review comment:
       ```suggestion
    * Represents a value that can be {@code null}. Used to distinguish 'value 
is absent' and `value is null` cases.
   ```

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,
+ * annotation, and so on.
+ *
+ * @param <T> Type of which objects the mapper handles.
+ * @apiNote Implementation shouldn't use this interface directly, please, use 
{@link PojoMapper} or {@link OneColumnMapper}
+ *         instead.
+ * @see PojoMapper
+ * @see OneColumnMapper
  */
 public interface Mapper<T> {
     /**
-     * Creates a mapper for a class.
+     * Shortcut method creates a mapper for class. Natively supported types 
will be mapped to a single schema column, otherwise individual

Review comment:
       ```suggestion
        * Creates a mapper for the specified class. Natively supported types 
will be mapped to a single schema column, otherwise individual
   ```

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,
+ * annotation, and so on.
+ *
+ * @param <T> Type of which objects the mapper handles.
+ * @apiNote Implementation shouldn't use this interface directly, please, use 
{@link PojoMapper} or {@link OneColumnMapper}
+ *         instead.
+ * @see PojoMapper
+ * @see OneColumnMapper
  */
 public interface Mapper<T> {
     /**
-     * Creates a mapper for a class.
+     * Shortcut method creates a mapper for class. Natively supported types 
will be mapped to a single schema column, otherwise individual
+     * object fields will be mapped to columns with the same name.
+     *
+     * <p>Note: Natively supported types can be mapped to a single key/value 
column, otherwise table operation will ends up with exception.

Review comment:
       ```suggestion
        * <p>Note: Natively supported types can be mapped to a single key/value 
column, otherwise table operation will throw an exception.
   ```

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,
+ * annotation, and so on.
+ *
+ * @param <T> Type of which objects the mapper handles.
+ * @apiNote Implementation shouldn't use this interface directly, please, use 
{@link PojoMapper} or {@link OneColumnMapper}
+ *         instead.
+ * @see PojoMapper
+ * @see OneColumnMapper
  */
 public interface Mapper<T> {
     /**
-     * Creates a mapper for a class.
+     * Shortcut method creates a mapper for class. Natively supported types 
will be mapped to a single schema column, otherwise individual
+     * object fields will be mapped to columns with the same name.
+     *
+     * <p>Note: Natively supported types can be mapped to a single key/value 
column, otherwise table operation will ends up with exception.
+     * Use {@link #of(Class, String)} instead, to map to a concrete column 
name.
      *
-     * @param cls Key class.
-     * @param <K> Key type.
-     * @return Mapper.
+     * @param cls Target type.
+     * @return Mapper for key objects representing a single key column.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
      */
-    static <K> Mapper<K> of(Class<K> cls) {
-        return identity(cls);
+    static <O> Mapper<O> of(Class<O> cls) {
+        if (nativelySupported(cls)) {
+            // TODO: Cache mappers (IGNITE-16094).
+            return new OneColumnMapperImpl<>(cls, null, null);
+        } else {
+            return builder(cls).automap().build();
+        }
     }
 
     /**
-     * Creates a mapper builder for a class.
+     * Creates a mapper for the case when an object represents an only 
one-column.

Review comment:
       ```suggestion
        * Creates a mapper for the case when an object represents only one 
column.
   ```

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -26,9 +28,20 @@
 import org.jetbrains.annotations.NotNull;
 
 /**
- * Mapper builder.
+ * Mapper builder provides methods for mapping object fields to columns.
  *
- * @param <T> Mapped type.
+ * <p>By default, a user must explicitly map needed fields with columns
+ * in one-to-one manner using {@link #map} and/or {@link #map(String, String, 
TypeConverter)} methods, all missed columns and/or fields
+ * become unmapped, and will be ignored during further table operations.
+ *
+ * <p>Calling {@link #automap()} method changes default behavior, and maps all 
the missed fields to the

Review comment:
       ```suggestion
    * <p>Calling {@link #automap()} method maps all matching fields to columns 
by name. Any fields that don't match a column by name are ignored.
   ```

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,
+ * annotation, and so on.
+ *
+ * @param <T> Type of which objects the mapper handles.
+ * @apiNote Implementation shouldn't use this interface directly, please, use 
{@link PojoMapper} or {@link OneColumnMapper}
+ *         instead.
+ * @see PojoMapper
+ * @see OneColumnMapper
  */
 public interface Mapper<T> {
     /**
-     * Creates a mapper for a class.
+     * Shortcut method creates a mapper for class. Natively supported types 
will be mapped to a single schema column, otherwise individual
+     * object fields will be mapped to columns with the same name.
+     *
+     * <p>Note: Natively supported types can be mapped to a single key/value 
column, otherwise table operation will ends up with exception.
+     * Use {@link #of(Class, String)} instead, to map to a concrete column 
name.
      *
-     * @param cls Key class.
-     * @param <K> Key type.
-     * @return Mapper.
+     * @param cls Target type.
+     * @return Mapper for key objects representing a single key column.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
      */
-    static <K> Mapper<K> of(Class<K> cls) {
-        return identity(cls);
+    static <O> Mapper<O> of(Class<O> cls) {
+        if (nativelySupported(cls)) {
+            // TODO: Cache mappers (IGNITE-16094).
+            return new OneColumnMapperImpl<>(cls, null, null);
+        } else {
+            return builder(cls).automap().build();
+        }
     }
 
     /**
-     * Creates a mapper builder for a class.
+     * Creates a mapper for the case when an object represents an only 
one-column.
+     *
+     * <p>The mapper can be used as key, value, or record mapper. However, 
single column record looks as degraded case.
+     *
+     * @param cls        Parametrized type of which objects the mapper will 
handle.
+     * @param columnName Column name to map object to.
+     * @return Mapper for objects representing a one column.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
+     */
+    static <O> Mapper<O> of(Class<O> cls, String columnName) {
+        return new OneColumnMapperImpl<>(ensureNativelySupported(cls), 
columnName, null);
+    }
+
+    /**
+     * Creates a mapper for the case when an object represents an only 
one-column and additional transformation is required.

Review comment:
       ```suggestion
        * Creates a mapper for the case when an object represents only one 
column and additional transformation is required.
   ```

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");

Review comment:
       ```suggestion
               throw new IllegalArgumentException("fieldColumnPairs length 
should be even.");
   ```

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");
+        } else if (columnToFields.put(Objects.requireNonNull(columnName), 
requireValidField(fieldName)) != null) {
             throw new IllegalArgumentException("Mapping for a column already 
exists: " + columnName);
         }
 
+        for (int i = 0; i < fieldColumnPairs.length; i += 2) {
+            if (columnToFields.put(Objects.requireNonNull(fieldColumnPairs[i + 
1]), requireValidField(fieldColumnPairs[i])) != null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + columnName);
+            }
+        }
+
         return this;
     }
 
     /**
-     * Map a field to a type of given class.
+     * Maps a field to a column with using type converter. The value will be 
converted before write to and after read from column using

Review comment:
       ```suggestion
        * Maps a field to a column with a type converter. The value will be 
converted before write to and after read from column using
   ```

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -26,9 +28,20 @@
 import org.jetbrains.annotations.NotNull;
 
 /**
- * Mapper builder.
+ * Mapper builder provides methods for mapping object fields to columns.
  *
- * @param <T> Mapped type.
+ * <p>By default, a user must explicitly map needed fields with columns

Review comment:
       ```suggestion
    * <p>By default, the user must explicitly map required fields to columns
   ```

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,
+ * annotation, and so on.
+ *
+ * @param <T> Type of which objects the mapper handles.
+ * @apiNote Implementation shouldn't use this interface directly, please, use 
{@link PojoMapper} or {@link OneColumnMapper}
+ *         instead.
+ * @see PojoMapper
+ * @see OneColumnMapper
  */
 public interface Mapper<T> {
     /**
-     * Creates a mapper for a class.
+     * Shortcut method creates a mapper for class. Natively supported types 
will be mapped to a single schema column, otherwise individual
+     * object fields will be mapped to columns with the same name.
+     *
+     * <p>Note: Natively supported types can be mapped to a single key/value 
column, otherwise table operation will ends up with exception.
+     * Use {@link #of(Class, String)} instead, to map to a concrete column 
name.
      *
-     * @param cls Key class.
-     * @param <K> Key type.
-     * @return Mapper.
+     * @param cls Target type.
+     * @return Mapper for key objects representing a single key column.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
      */
-    static <K> Mapper<K> of(Class<K> cls) {
-        return identity(cls);
+    static <O> Mapper<O> of(Class<O> cls) {
+        if (nativelySupported(cls)) {
+            // TODO: Cache mappers (IGNITE-16094).
+            return new OneColumnMapperImpl<>(cls, null, null);
+        } else {
+            return builder(cls).automap().build();
+        }
     }
 
     /**
-     * Creates a mapper builder for a class.
+     * Creates a mapper for the case when an object represents an only 
one-column.
+     *
+     * <p>The mapper can be used as key, value, or record mapper. However, 
single column record looks as degraded case.
+     *
+     * @param cls        Parametrized type of which objects the mapper will 
handle.
+     * @param columnName Column name to map object to.
+     * @return Mapper for objects representing a one column.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
+     */
+    static <O> Mapper<O> of(Class<O> cls, String columnName) {
+        return new OneColumnMapperImpl<>(ensureNativelySupported(cls), 
columnName, null);
+    }
+
+    /**
+     * Creates a mapper for the case when an object represents an only 
one-column and additional transformation is required.
+     *
+     * <p>The mapper can be used as key, value, or record mapper. However, 
single column record looks as degraded case.
      *
-     * @param cls Value class.
-     * @param <V> Value type.
+     * @param cls        Parametrized type of which objects the mapper will 
handle.
+     * @param columnName Column name to map object to.
+     * @param <ObjectT>  Mapper target type.
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.
+     * @return Mapper for objects representing a one column.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
+     */
+    static <ObjectT, ColumnT> Mapper<ObjectT> of(Class<ObjectT> cls, String 
columnName, TypeConverter<ObjectT, ColumnT> converter) {
+        return new OneColumnMapperImpl<>(cls, columnName, converter);
+    }
+
+    /**
+     * Shortcut method creates a mapper for a case when object individual 
fields map to the column by their names.
+     *
+     * <p>The mapper can be used as key, value, or record mapper.
+     *
+     * @param cls              Parametrized type of which objects the mapper 
will handle.
+     * @param fieldName        Object field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return Mapper .
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or {@code cls} is of
+     *                                  unsupported kind.
+     */
+    static <O> Mapper<O> of(Class<O> cls, String fieldName, String columnName, 
String... fieldColumnPairs) {
+        if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException(
+                    "Missed a column name, which the field is mapped to: " + 
fieldColumnPairs[fieldColumnPairs.length - 1]);
+        }
+
+        return builder(cls).map(fieldName, columnName, 
fieldColumnPairs).build();
+    }
+
+    /**
+     * Creates a mapper builder for objects of given class.
+     *
+     * <p>Note: Builder itself can't be reused.
+     *
+     * @param cls Parametrized type of which objects the mapper will handle. 
Class MUST have the default constructor,
      * @return Mapper builder.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
      */
-    static <V> MapperBuilder<V> builderFor(Class<V> cls) {
-        return new MapperBuilder<>(cls);
+    static <O> MapperBuilder<O> builder(Class<O> cls) {
+        if (nativelySupported(cls)) {
+            return new MapperBuilder<>(cls, null);
+        } else {
+            return new MapperBuilder<>(cls);
+        }
     }
 
     /**
-     * Creates identity mapper which is used for simple types that have native 
support or objects with field names that match column names.
+     * Ensures class is of natively supported kind and can be used in 
one-column mapping.
      *
-     * @param targetClass Target type class.
-     * @param <T>         Target type.
-     * @return Mapper.
+     * @param cls Class to validate.
+     * @return {@code cls} if it is of natively supported kind.
+     * @throws IllegalArgumentException If {@code cls} is invalid and can't be 
used in one-column mapping.
      */
-    static <T> Mapper<T> identity(Class<T> targetClass) {
-        // TODO: Cache mappers (IGNITE-16094).
-        return new IdentityMapper<T>(targetClass);
+    static <O> Class<O> ensureNativelySupported(Class<O> cls) {
+        if (nativelySupported(cls)) {
+            return cls;
+        }
+
+        throw new IllegalArgumentException("Class has no native support (type 
converter required): " + cls.getName());
     }
 
     /**
-     * Return mapped type.
+     * Ensures class is of the supported kind for POJO mapping.
      *
-     * @return Mapped type.
+     * @param cls Class to validate.
+     * @return {@code cls} if it is valid POJO.
+     * @throws IllegalArgumentException If {@code cls} can't be used as POJO 
for mapping and/or of invalid kind.
      */
-    Class<T> targetType();
+    static <O> Class<O> ensureValidPojo(Class<O> cls) {
+        if (nativelySupported(cls) || cls.isAnonymousClass() || 
cls.isLocalClass() || cls.isSynthetic() || cls.isPrimitive() || cls.isEnum()
+                    || cls.isArray() || cls.isAnnotation() || 
(cls.isMemberClass() && !Modifier.isStatic(cls.getModifiers()))
+                    || Modifier.isAbstract(cls.getModifiers())) {
+            throw new IllegalArgumentException("Class is of unsupported kind: 
" + cls.getName());

Review comment:
       Can we make an exception more helpful and indicate what exactly is wrong 
with the class?
   E.g. `"Unsupported class. Anonymous types are not supported: " + 
cls.getName()`.

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");
+        } else if (columnToFields.put(Objects.requireNonNull(columnName), 
requireValidField(fieldName)) != null) {
             throw new IllegalArgumentException("Mapping for a column already 
exists: " + columnName);
         }
 
+        for (int i = 0; i < fieldColumnPairs.length; i += 2) {
+            if (columnToFields.put(Objects.requireNonNull(fieldColumnPairs[i + 
1]), requireValidField(fieldColumnPairs[i])) != null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + columnName);

Review comment:
       Is there any disadvantage if we allow the user to overwrite previous 
mappings?

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,
+ * annotation, and so on.
+ *
+ * @param <T> Type of which objects the mapper handles.
+ * @apiNote Implementation shouldn't use this interface directly, please, use 
{@link PojoMapper} or {@link OneColumnMapper}
+ *         instead.
+ * @see PojoMapper
+ * @see OneColumnMapper
  */
 public interface Mapper<T> {
     /**
-     * Creates a mapper for a class.
+     * Shortcut method creates a mapper for class. Natively supported types 
will be mapped to a single schema column, otherwise individual
+     * object fields will be mapped to columns with the same name.
+     *
+     * <p>Note: Natively supported types can be mapped to a single key/value 
column, otherwise table operation will ends up with exception.
+     * Use {@link #of(Class, String)} instead, to map to a concrete column 
name.
      *
-     * @param cls Key class.
-     * @param <K> Key type.
-     * @return Mapper.
+     * @param cls Target type.
+     * @return Mapper for key objects representing a single key column.
+     * @throws IllegalArgumentException If {@code cls} is of unsupported kind.
      */
-    static <K> Mapper<K> of(Class<K> cls) {
-        return identity(cls);
+    static <O> Mapper<O> of(Class<O> cls) {
+        if (nativelySupported(cls)) {

Review comment:
       Here and below: I think we should check all user-provided arguments for 
`null`.

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");
+        } else if (columnToFields.put(Objects.requireNonNull(columnName), 
requireValidField(fieldName)) != null) {
             throw new IllegalArgumentException("Mapping for a column already 
exists: " + columnName);
         }
 
+        for (int i = 0; i < fieldColumnPairs.length; i += 2) {
+            if (columnToFields.put(Objects.requireNonNull(fieldColumnPairs[i + 
1]), requireValidField(fieldColumnPairs[i])) != null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + columnName);
+            }
+        }
+
         return this;
     }
 
     /**
-     * Map a field to a type of given class.
+     * Maps a field to a column with using type converter. The value will be 
converted before write to and after read from column using
+     * provided converter.
      *
-     * @param fieldName   Field name.
-     * @param targetClass Target class.
-     * @return {@code this} for chaining.
+     * @param <ObjectT>  MUST match the object field type, if the individual 
field mapped to given column.
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.

Review comment:
       ```suggestion
        * @param <ColumnT> Column type. Must be assignable to the table column 
type.
   ```

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");
+        } else if (columnToFields.put(Objects.requireNonNull(columnName), 
requireValidField(fieldName)) != null) {
             throw new IllegalArgumentException("Mapping for a column already 
exists: " + columnName);
         }
 
+        for (int i = 0; i < fieldColumnPairs.length; i += 2) {
+            if (columnToFields.put(Objects.requireNonNull(fieldColumnPairs[i + 
1]), requireValidField(fieldColumnPairs[i])) != null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + columnName);
+            }
+        }
+
         return this;
     }
 
     /**
-     * Map a field to a type of given class.
+     * Maps a field to a column with using type converter. The value will be 
converted before write to and after read from column using
+     * provided converter.
      *
-     * @param fieldName   Field name.
-     * @param targetClass Target class.
-     * @return {@code this} for chaining.
+     * @param <ObjectT>  MUST match the object field type, if the individual 
field mapped to given column.
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.
+     * @param fieldName  Field name.
+     * @param columnName Column name.
+     * @param converter  Converter for objects of {@link ColumnT} and {@link 
ObjectT}.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, Class<?> 
targetClass) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+    public <ObjectT, ColumnT> MapperBuilder<T> map(
+            @NotNull String fieldName,
+            @NotNull String columnName,
+            @NotNull TypeConverter<ObjectT, ColumnT> converter
+    ) {
+        map(fieldName, columnName);
+        convert(converter, columnName);
+
+        return this;
     }
 
     /**
-     * Adds a functional mapping for a field, the result depends on function 
call for every particular row.
+     * Adds a manual functional mapping for an object and row represented by 
tuple.
      *
-     * @param fieldName       Field name.
-     * @param mappingFunction Mapper function.
+     * @param objectToRow Object to tuple function.
+     * @param rowToObject Tuple to object function.
      * @return {@code this} for chaining.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, Function<Tuple, 
Object> mappingFunction) {
+    public MapperBuilder<T> map(Function<T, Tuple> objectToRow, 
Function<Tuple, T> rowToObject) {
+        ensureNotStale();
+
         throw new UnsupportedOperationException("Not implemented yet.");
     }
 
     /**
-     * Sets a target class to deserialize to.
+     * Sets a converter for a column, which value must be converted before 
write/after read.
+     *
+     * @param converter  Converter for objects of {@link ColumnT} and {@link 
ObjectT}.
+     * @param columnName Column name.
+     * @param <ObjectT>  MUST match the object field type, if a field mapped 
to given column, or the object type {@link T}
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.

Review comment:
       Same as above.

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");
+        } else if (columnToFields.put(Objects.requireNonNull(columnName), 
requireValidField(fieldName)) != null) {
             throw new IllegalArgumentException("Mapping for a column already 
exists: " + columnName);
         }
 
+        for (int i = 0; i < fieldColumnPairs.length; i += 2) {
+            if (columnToFields.put(Objects.requireNonNull(fieldColumnPairs[i + 
1]), requireValidField(fieldColumnPairs[i])) != null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + columnName);
+            }
+        }
+
         return this;
     }
 
     /**
-     * Map a field to a type of given class.
+     * Maps a field to a column with using type converter. The value will be 
converted before write to and after read from column using
+     * provided converter.
      *
-     * @param fieldName   Field name.
-     * @param targetClass Target class.
-     * @return {@code this} for chaining.
+     * @param <ObjectT>  MUST match the object field type, if the individual 
field mapped to given column.
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.
+     * @param fieldName  Field name.
+     * @param columnName Column name.
+     * @param converter  Converter for objects of {@link ColumnT} and {@link 
ObjectT}.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, Class<?> 
targetClass) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+    public <ObjectT, ColumnT> MapperBuilder<T> map(
+            @NotNull String fieldName,
+            @NotNull String columnName,
+            @NotNull TypeConverter<ObjectT, ColumnT> converter
+    ) {
+        map(fieldName, columnName);
+        convert(converter, columnName);
+
+        return this;
     }
 
     /**
-     * Adds a functional mapping for a field, the result depends on function 
call for every particular row.
+     * Adds a manual functional mapping for an object and row represented by 
tuple.
      *
-     * @param fieldName       Field name.
-     * @param mappingFunction Mapper function.
+     * @param objectToRow Object to tuple function.
+     * @param rowToObject Tuple to object function.
      * @return {@code this} for chaining.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, Function<Tuple, 
Object> mappingFunction) {
+    public MapperBuilder<T> map(Function<T, Tuple> objectToRow, 
Function<Tuple, T> rowToObject) {
+        ensureNotStale();
+
         throw new UnsupportedOperationException("Not implemented yet.");
     }
 
     /**
-     * Sets a target class to deserialize to.
+     * Sets a converter for a column, which value must be converted before 
write/after read.
+     *
+     * @param converter  Converter for objects of {@link ColumnT} and {@link 
ObjectT}.
+     * @param columnName Column name.
+     * @param <ObjectT>  MUST match the object field type, if a field mapped 
to given column, or the object type {@link T}
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.
+     */
+    public <ObjectT, ColumnT> MapperBuilder<T> convert(
+            @NotNull TypeConverter<ObjectT, ColumnT> converter,
+            @NotNull String columnName
+    ) {
+        ensureNotStale();
+
+        if (columnConverters.put(columnName, converter) != null) {
+            throw new IllegalArgumentException("Column converter already 
exists: " + columnName);
+        }
+
+        return this;
+    }
+
+    /**
+     * Make mapper treat missed columns as they mapped to the field of the 
same name. If class {@link T} has no field for the missed column,

Review comment:
       ```suggestion
        * Maps all matching fields to columns by name. Any fields that don't 
match a column by name are ignored.
   ```

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");
+        } else if (columnToFields.put(Objects.requireNonNull(columnName), 
requireValidField(fieldName)) != null) {
             throw new IllegalArgumentException("Mapping for a column already 
exists: " + columnName);
         }
 
+        for (int i = 0; i < fieldColumnPairs.length; i += 2) {
+            if (columnToFields.put(Objects.requireNonNull(fieldColumnPairs[i + 
1]), requireValidField(fieldColumnPairs[i])) != null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + columnName);
+            }
+        }
+
         return this;
     }
 
     /**
-     * Map a field to a type of given class.
+     * Maps a field to a column with using type converter. The value will be 
converted before write to and after read from column using
+     * provided converter.
      *
-     * @param fieldName   Field name.
-     * @param targetClass Target class.
-     * @return {@code this} for chaining.
+     * @param <ObjectT>  MUST match the object field type, if the individual 
field mapped to given column.

Review comment:
       Value type. Must match the object field type if the individual field is 
mapped to a given column.

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/OneColumnMapper.java
##########
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.ignite.table.mapper;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Maps whole object of natively supported type to a one column. Used to map 
object itself, but not the object fields.
+ *
+ * @apiNote When mapper is used for mapping key/value objects, and the schema 
contains single key/value column, it is possible to
+ *         map object without specifying a column name. However, if there are 
more columns and no concrete one was specified, then table
+ *         operations with using such mapper may fail with a schema mismatch 
exception due to ambiguity.
+ */
+public interface OneColumnMapper<ObjectT> extends Mapper<ObjectT> {
+    /**
+     * Returns a column name the object is mapped to, or {@code null} if not 
specified. If column name wasn't specified, mapper will to map

Review comment:
       ```suggestion
        * Returns a column name the object is mapped to, or {@code null} if not 
specified. If column name wasn't specified, the mapper maps the entire
   ```

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
##########
@@ -37,93 +50,205 @@
     /** Column-to-field name mapping. */
     private Map<String, String> columnToFields;
 
+    /** Column converters. */
+    private Map<String, TypeConverter<?, ?>> columnConverters = new 
HashMap<>();
+
+    /** Column name for one-column mapping. */
+    private final String mappedToColumn;
+
+    /** Flag indicates, if all unmapped object fields should be mapped to the 
columns with same names automatically. */
+    private boolean automapFlag = false;
+
+    /** {@code True} if the {@link #build()} method was called, {@code false} 
otherwise. */
+    private boolean isStale;
+
     /**
-     * Creates a mapper builder for a type.
+     * Creates a mapper builder for a POJO type.
      *
      * @param targetType Target type.
      */
     MapperBuilder(@NotNull Class<T> targetType) {
-        this.targetType = targetType;
+        this.targetType = Mapper.ensureValidPojo(targetType);
 
+        mappedToColumn = null;
         columnToFields = new HashMap<>(targetType.getDeclaredFields().length);
     }
 
     /**
-     * Add mapping for a field to a column.
+     * Creates a mapper builder for a natively supported type.
+     *
+     * @param targetType   Target type.
+     * @param mappedColumn Column name to map to.
+     */
+    MapperBuilder(@NotNull Class<T> targetType, String mappedColumn) {
+        this.targetType = Mapper.ensureNativelySupported(targetType);
+
+        mappedToColumn = mappedColumn;
+        columnToFields = null;
+    }
+
+    /**
+     * Ensure this instance is valid.
      *
-     * @param fieldName  Field name.
-     * @param columnName Column name.
-     * @return {@code this} for chaining.
-     * @throws IllegalArgumentException if a column was already mapped to some 
field.
      * @throws IllegalStateException if tries to reuse the builder after a 
mapping has been built.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName) {
-        if (columnToFields == null) {
+    private void ensureNotStale() {
+        if (isStale) {
             throw new IllegalStateException("Mapper builder can't be reused.");
         }
+    }
+
+    /**
+     * Ensure field name is valid and field with this name exists.
+     *
+     * @param fieldName Field name.
+     * @return Field name for chaining.
+     * @throws IllegalArgumentException If field is {@code null} or class has 
no declared field with given name.
+     */
+    private String requireValidField(String fieldName) {
+        try {
+            if (fieldName == null || targetType.getDeclaredField(fieldName) == 
null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + fieldName);
+            }
+        } catch (NoSuchFieldException e) {
+            throw new IllegalArgumentException(
+                    String.format("Field not found for class: field=%s, 
class=%s", fieldName, targetType.getName()));
+        }
+
+        return fieldName;
+    }
+
+    /**
+     * Maps a field to a column.
+     *
+     * @param fieldName        Field name.
+     * @param columnName       Column name.
+     * @param fieldColumnPairs Vararg that accepts (fieldName, columnName) 
pairs.
+     * @return {@code this} for chaining.
+     * @throws IllegalArgumentException If a field name has not paired column 
name in {@code fieldColumnPairs}, or a column was already
+     *                                  mapped to another field.
+     * @throws IllegalStateException    if tries to reuse the builder after a 
mapping has been built.
+     */
+    public MapperBuilder<T> map(@NotNull String fieldName, @NotNull String 
columnName, String... fieldColumnPairs) {
+        ensureNotStale();
 
-        if (columnToFields.put(Objects.requireNonNull(columnName), 
Objects.requireNonNull(fieldName)) != null) {
+        if (columnToFields == null) {
+            throw new IllegalArgumentException("Natively supported types 
doesn't support field mapping.");
+        } else if (fieldColumnPairs.length % 2 != 0) {
+            throw new IllegalArgumentException("Missed a column name, which 
the field is mapped to.");
+        } else if (columnToFields.put(Objects.requireNonNull(columnName), 
requireValidField(fieldName)) != null) {
             throw new IllegalArgumentException("Mapping for a column already 
exists: " + columnName);
         }
 
+        for (int i = 0; i < fieldColumnPairs.length; i += 2) {
+            if (columnToFields.put(Objects.requireNonNull(fieldColumnPairs[i + 
1]), requireValidField(fieldColumnPairs[i])) != null) {
+                throw new IllegalArgumentException("Mapping for a column 
already exists: " + columnName);
+            }
+        }
+
         return this;
     }
 
     /**
-     * Map a field to a type of given class.
+     * Maps a field to a column with using type converter. The value will be 
converted before write to and after read from column using
+     * provided converter.
      *
-     * @param fieldName   Field name.
-     * @param targetClass Target class.
-     * @return {@code this} for chaining.
+     * @param <ObjectT>  MUST match the object field type, if the individual 
field mapped to given column.
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.
+     * @param fieldName  Field name.
+     * @param columnName Column name.
+     * @param converter  Converter for objects of {@link ColumnT} and {@link 
ObjectT}.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, Class<?> 
targetClass) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+    public <ObjectT, ColumnT> MapperBuilder<T> map(
+            @NotNull String fieldName,
+            @NotNull String columnName,
+            @NotNull TypeConverter<ObjectT, ColumnT> converter
+    ) {
+        map(fieldName, columnName);
+        convert(converter, columnName);
+
+        return this;
     }
 
     /**
-     * Adds a functional mapping for a field, the result depends on function 
call for every particular row.
+     * Adds a manual functional mapping for an object and row represented by 
tuple.
      *
-     * @param fieldName       Field name.
-     * @param mappingFunction Mapper function.
+     * @param objectToRow Object to tuple function.
+     * @param rowToObject Tuple to object function.
      * @return {@code this} for chaining.
      */
-    public MapperBuilder<T> map(@NotNull String fieldName, Function<Tuple, 
Object> mappingFunction) {
+    public MapperBuilder<T> map(Function<T, Tuple> objectToRow, 
Function<Tuple, T> rowToObject) {
+        ensureNotStale();
+
         throw new UnsupportedOperationException("Not implemented yet.");
     }
 
     /**
-     * Sets a target class to deserialize to.
+     * Sets a converter for a column, which value must be converted before 
write/after read.
+     *
+     * @param converter  Converter for objects of {@link ColumnT} and {@link 
ObjectT}.
+     * @param columnName Column name.
+     * @param <ObjectT>  MUST match the object field type, if a field mapped 
to given column, or the object type {@link T}
+     * @param <ColumnT>  MUST be a type, which compatible with the column type.
+     */
+    public <ObjectT, ColumnT> MapperBuilder<T> convert(
+            @NotNull TypeConverter<ObjectT, ColumnT> converter,

Review comment:
       Should the column name come first?

##########
File path: 
modules/api/src/main/java/org/apache/ignite/table/mapper/OneColumnMapper.java
##########
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.ignite.table.mapper;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Maps whole object of natively supported type to a one column. Used to map 
object itself, but not the object fields.

Review comment:
       ```suggestion
    * Maps the whole object of natively supported type to a single column. Used 
to map the object itself, but not the object fields.
   ```

##########
File path: modules/api/src/main/java/org/apache/ignite/table/mapper/Mapper.java
##########
@@ -17,61 +17,178 @@
 
 package org.apache.ignite.table.mapper;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.BitSet;
+import java.util.UUID;
 
 /**
  * Mapper interface defines methods that are required for a marshaller to map 
class field names to table columns.
  *
- * @param <T> Mapped type.
+ * <p>NB: Anonymous, local, inner classes, and special types are NOT 
supported, and mapper will not be created for them: e.g. interfaces,
+ * annotation, and so on.
+ *
+ * @param <T> Type of which objects the mapper handles.
+ * @apiNote Implementation shouldn't use this interface directly, please, use 
{@link PojoMapper} or {@link OneColumnMapper}
+ *         instead.
+ * @see PojoMapper
+ * @see OneColumnMapper
  */
 public interface Mapper<T> {
     /**
-     * Creates a mapper for a class.
+     * Shortcut method creates a mapper for class. Natively supported types 
will be mapped to a single schema column, otherwise individual
+     * object fields will be mapped to columns with the same name.
+     *
+     * <p>Note: Natively supported types can be mapped to a single key/value 
column, otherwise table operation will ends up with exception.

Review comment:
       > otherwise table operation will throw an exception
   
   It is not clear in which case an exception will be thrown:
   - When a type is not supported natively
   - When there is more than 1 column
   




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to