You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2021/11/09 15:50:25 UTC

[ignite-3] 01/02: IGNITE-14484: Implement RecordView.

This is an automated email from the ASF dual-hosted git repository.

amashenkov pushed a commit to branch ignite-14484
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 5bc9e141a63d8ca3c29db50e1e6f8ebd072251c1
Author: Andrew Mashenkov <an...@gmail.com>
AuthorDate: Tue Nov 9 18:43:23 2021 +0300

    IGNITE-14484: Implement RecordView.
---
 .../internal/schema/marshaller/KvMarshaller.java   |   9 +-
 .../schema/marshaller/RecordMarshaller.java}       |  35 ++-
 .../marshaller/reflection/KvMarshallerImpl.java    |   7 +-
 .../schema/marshaller/reflection/Marshaller.java   |  12 +-
 ...rshallerImpl.java => RecordMarshallerImpl.java} |  82 +++--
 .../ignite/internal/table/KeyValueViewImpl.java    |   9 +-
 .../ignite/internal/table/RecordViewImpl.java      | 234 +++++++++-----
 .../internal/table/KeyValueViewOperationsTest.java |   6 +-
 ...ionsTest.java => RecordViewOperationsTest.java} | 343 +++++++++------------
 9 files changed, 392 insertions(+), 345 deletions(-)

diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/KvMarshaller.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/KvMarshaller.java
index 21706bc..c772894 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/KvMarshaller.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/KvMarshaller.java
@@ -31,27 +31,30 @@ import org.jetbrains.annotations.Nullable;
  */
 public interface KvMarshaller<K, V> {
     /**
-     * Marshal key and value objects to a table row.
+     * Marshal given key and value objects to a table row.
      *
      * @param key Key object to marshal.
      * @param val Value object to marshal or {@code null}.
      * @return Table row with columns from given key-value pair.
+     * @throws MarshallerException If failed to marshal key and/or value.
      */
     BinaryRow marshal(@NotNull K key, V val) throws MarshallerException;
     
     /**
-     * Unmarshal row to a key object.
+     * Unmarshal given row to a key object.
      *
      * @param row Table row.
      * @return Key object.
+     * @throws MarshallerException If failed to unmarshal row.
      */
     @NotNull K unmarshalKey(@NotNull Row row) throws MarshallerException;
     
     /**
-     * Unmarshal row to a value object.
+     * Unmarshal given row to a value object.
      *
      * @param row Table row.
      * @return Value object.
+     * @throws MarshallerException If failed to unmarshal row.
      */
     @Nullable V unmarshalValue(@NotNull Row row) throws MarshallerException;
 }
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/schema/marshaller/RecordSerializer.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/RecordMarshaller.java
similarity index 54%
rename from modules/table/src/main/java/org/apache/ignite/internal/schema/marshaller/RecordSerializer.java
rename to modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/RecordMarshaller.java
index c74a5c8..a3db866 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/schema/marshaller/RecordSerializer.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/RecordMarshaller.java
@@ -17,31 +17,40 @@
 
 package org.apache.ignite.internal.schema.marshaller;
 
+import org.apache.ignite.internal.schema.BinaryRow;
 import org.apache.ignite.internal.schema.row.Row;
 import org.jetbrains.annotations.NotNull;
 
 /**
- * Record serializer interface.
+ * Record marshaller interface provides method to marshal/unmarshal record objects to/from a row.
+ *
+ * @param <R> Record type.
  */
-public interface RecordSerializer<R> {
+public interface RecordMarshaller<R> {
     /**
-     * @param red Record to serialize.
+     * Marshals given record to a row.
+     *
+     * @param rec Record to marshal.
      * @return Table row with columns set from given object.
+     * @throws MarshallerException If failed to marshal record.
      */
-    Row serialize(@NotNull R red);
-
+    BinaryRow marshal(@NotNull R rec) throws MarshallerException;
+    
     /**
-     * @param row Table row.
-     * @return Deserialized record object.
+     * Marshals key part of given record to a row.
+     *
+     * @param keyRec Record to marshal.
+     * @return Table row with key columns set from given object.
+     * @throws MarshallerException If failed to marshal record.
      */
-    R deserialize(@NotNull Row row);
-
+    BinaryRow marshalKey(@NotNull R keyRec) throws MarshallerException;
+    
     /**
-     * Deserializes row and fills given record object fields.
+     * Unmarshal given row to a record object.
      *
      * @param row Table row.
-     * @param rec Record object to fill.
-     * @return Given record with filled fields from the given row.
+     * @return Record object.
+     * @throws MarshallerException If failed to unmarshal row.
      */
-    R deserialize(@NotNull Row row, @NotNull R rec);
+    R unmarshal(@NotNull Row row) throws MarshallerException;
 }
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/KvMarshallerImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/KvMarshallerImpl.java
index e234a3b..3bd131b 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/KvMarshallerImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/KvMarshallerImpl.java
@@ -63,10 +63,13 @@ public class KvMarshallerImpl<K, V> implements KvMarshaller<K, V> {
         keyClass = keyMapper.targetType();
         valClass = valueMapper.targetType();
         
-        keyMarsh = Marshaller.createMarshaller(schema.keyColumns(), keyMapper);
-        valMarsh = Marshaller.createMarshaller(schema.valueColumns(), valueMapper);
+        keyMarsh = Marshaller.createMarshaller(schema.keyColumns().columns(), keyMapper);
+        valMarsh = Marshaller.createMarshaller(schema.valueColumns().columns(), valueMapper);
     }
     
+    /**
+     * @return Marshaller schema version.
+     */
     public int schemaVersion() {
         return schema.version();
     }
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java
index cd33ae0..bd5acfb 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java
@@ -41,24 +41,24 @@ public abstract class Marshaller {
      * @param mapper Mapper.
      * @return Marshaller.
      */
-    public static <T> Marshaller createMarshaller(Columns cols, Mapper<T> mapper) {
+    public static <T> Marshaller createMarshaller(Column[] cols, Mapper<T> mapper) {
         final BinaryMode mode = MarshallerUtil.mode(mapper.targetType());
 
         if (mode != null) {
-            final Column col = cols.column(0);
+            final Column col = cols[0];
 
-            assert cols.length() == 1;
+            assert cols.length == 1;
             assert mode.typeSpec() == col.type().spec() : "Target type is not compatible.";
             assert !mapper.targetType().isPrimitive() : "Non-nullable types are not allowed.";
 
             return new SimpleMarshaller(FieldAccessor.createIdentityAccessor(col, col.schemaIndex(), mode));
         }
 
-        FieldAccessor[] fieldAccessors = new FieldAccessor[cols.length()];
+        FieldAccessor[] fieldAccessors = new FieldAccessor[cols.length];
     
         // Build handlers.
-        for (int i = 0; i < cols.length(); i++) {
-            final Column col = cols.column(i);
+        for (int i = 0; i < cols.length; i++) {
+            final Column col = cols[i];
         
             String fieldName = mapper.columnToField(col.name());
         
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/KvMarshallerImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/RecordMarshallerImpl.java
similarity index 71%
copy from modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/KvMarshallerImpl.java
copy to modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/RecordMarshallerImpl.java
index e234a3b..302c582 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/KvMarshallerImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/RecordMarshallerImpl.java
@@ -24,94 +24,88 @@ import org.apache.ignite.internal.schema.BinaryRow;
 import org.apache.ignite.internal.schema.ByteBufferRow;
 import org.apache.ignite.internal.schema.Columns;
 import org.apache.ignite.internal.schema.SchemaDescriptor;
-import org.apache.ignite.internal.schema.marshaller.KvMarshaller;
 import org.apache.ignite.internal.schema.marshaller.MarshallerException;
+import org.apache.ignite.internal.schema.marshaller.RecordMarshaller;
 import org.apache.ignite.internal.schema.row.Row;
 import org.apache.ignite.internal.schema.row.RowAssembler;
+import org.apache.ignite.internal.util.ArrayUtils;
 import org.apache.ignite.table.mapper.Mapper;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 /**
- * Key-value marshaller for given schema and mappers.
+ * Record marshaller for given schema and mappers.
  *
- * @param <K> Key type.
- * @param <V> Value type.
+ * @param <R> Record type.
  */
-public class KvMarshallerImpl<K, V> implements KvMarshaller<K, V> {
+public class RecordMarshallerImpl<R> implements RecordMarshaller<R> {
     /** Schema. */
     private final SchemaDescriptor schema;
     
     /** Key marshaller. */
     private final Marshaller keyMarsh;
     
-    /** Value marshaller. */
-    private final Marshaller valMarsh;
+    /** Record marshaller. */
+    private final Marshaller recMarsh;
     
-    /** Key type. */
-    private final Class<K> keyClass;
-    
-    /** Value type. */
-    private final Class<V> valClass;
+    /** Record type. */
+    private final Class<R> recClass;
     
     /**
      * Creates KV marshaller.
      */
-    public KvMarshallerImpl(SchemaDescriptor schema, Mapper<K> keyMapper, Mapper<V> valueMapper) {
+    public RecordMarshallerImpl(SchemaDescriptor schema, Mapper<R> mapper) {
         this.schema = schema;
         
-        keyClass = keyMapper.targetType();
-        valClass = valueMapper.targetType();
+        recClass = mapper.targetType();
+        
+        keyMarsh = Marshaller.createMarshaller(schema.keyColumns().columns(), mapper);
         
-        keyMarsh = Marshaller.createMarshaller(schema.keyColumns(), keyMapper);
-        valMarsh = Marshaller.createMarshaller(schema.valueColumns(), valueMapper);
+        recMarsh = Marshaller.createMarshaller(
+                ArrayUtils.concat(schema.keyColumns().columns(), schema.valueColumns().columns()),
+                mapper
+        );
     }
     
+    /**
+     * @return Marshaller schema version.
+     */
     public int schemaVersion() {
         return schema.version();
     }
     
     /** {@inheritDoc} */
     @Override
-    public BinaryRow marshal(@NotNull K key, V val) throws MarshallerException {
-        assert keyClass.isInstance(key);
-        assert val == null || valClass.isInstance(val);
-        
-        final RowAssembler asm = createAssembler(Objects.requireNonNull(key), val);
+    public BinaryRow marshal(@NotNull R rec) throws MarshallerException {
+        assert recClass.isInstance(rec);
         
-        keyMarsh.writeObject(key, asm);
+        final RowAssembler asm = createAssembler(Objects.requireNonNull(rec), rec);
         
-        if (val != null) {
-            valMarsh.writeObject(val, asm);
-        }
+        recMarsh.writeObject(rec, asm);
         
         return new ByteBufferRow(asm.toBytes());
     }
     
     /** {@inheritDoc} */
-    @NotNull
     @Override
-    public K unmarshalKey(@NotNull Row row) throws MarshallerException {
-        final Object o = keyMarsh.readObject(row);
+    public BinaryRow marshalKey(@NotNull R rec) throws MarshallerException {
+        assert recClass.isInstance(rec);
+        
+        final RowAssembler asm = createAssembler(Objects.requireNonNull(rec), null);
         
-        assert keyClass.isInstance(o);
+        keyMarsh.writeObject(rec, asm);
         
-        return (K) o;
+        return new ByteBufferRow(asm.toBytes());
     }
     
     /** {@inheritDoc} */
-    @Nullable
+    @NotNull
     @Override
-    public V unmarshalValue(@NotNull Row row) throws MarshallerException {
-        if (!row.hasValue()) {
-            return null;
-        }
-        
-        final Object o = valMarsh.readObject(row);
+    public R unmarshal(@NotNull Row row) throws MarshallerException {
+        final Object o = recMarsh.readObject(row);
         
-        assert o == null || valClass.isInstance(o);
+        assert recClass.isInstance(o);
         
-        return (V) o;
+        return (R) o;
     }
     
     /**
@@ -122,8 +116,8 @@ public class KvMarshallerImpl<K, V> implements KvMarshaller<K, V> {
      * @return Row assembler.
      */
     private RowAssembler createAssembler(Object key, Object val) {
-        ObjectStatistic keyStat = collectObjectStats(schema.keyColumns(), keyMarsh, key);
-        ObjectStatistic valStat = collectObjectStats(schema.valueColumns(), valMarsh, val);
+        ObjectStatistic keyStat = collectObjectStats(schema.keyColumns(), recMarsh, key);
+        ObjectStatistic valStat = collectObjectStats(schema.valueColumns(), recMarsh, val);
         
         return new RowAssembler(schema, keyStat.nonNullColsSize, keyStat.nonNullCols,
                 valStat.nonNullColsSize, valStat.nonNullCols);
@@ -146,7 +140,7 @@ public class KvMarshallerImpl<K, V> implements KvMarshaller<K, V> {
         int size = 0;
         
         for (int i = cols.firstVarlengthColumn(); i < cols.length(); i++) {
-            final Object val = marsh.value(obj, i);
+            final Object val = marsh.value(obj, cols.column(i).schemaIndex());
             
             if (val == null || cols.column(i).type().spec().fixedLength()) {
                 continue;
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/KeyValueViewImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/KeyValueViewImpl.java
index fa0a9ae..0c3813a 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/KeyValueViewImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/KeyValueViewImpl.java
@@ -42,14 +42,10 @@ import org.jetbrains.annotations.Nullable;
  * Key-value view implementation.
  */
 public class KeyValueViewImpl<K, V> extends AbstractTableView implements KeyValueView<K, V> {
-    /**
-     * Marshaller factory.
-     */
+    /** Marshaller factory. */
     private final Function<SchemaDescriptor, KvMarshallerImpl<K, V>> marshallerFactory;
     
-    /**
-     * Marshaller.
-     */
+    /** Key-value marshaller. */
     private KvMarshallerImpl<K, V> marsh;
     
     /**
@@ -433,5 +429,4 @@ public class KeyValueViewImpl<K, V> extends AbstractTableView implements KeyValu
             throw new IgniteException(e);
         }
     }
-    
 }
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/RecordViewImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/RecordViewImpl.java
index 849061b..850b406 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/RecordViewImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/RecordViewImpl.java
@@ -22,11 +22,15 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
 import org.apache.ignite.internal.schema.BinaryRow;
 import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.internal.schema.SchemaRegistry;
-import org.apache.ignite.internal.schema.marshaller.RecordSerializer;
+import org.apache.ignite.internal.schema.marshaller.MarshallerException;
+import org.apache.ignite.internal.schema.marshaller.RecordMarshaller;
+import org.apache.ignite.internal.schema.marshaller.reflection.RecordMarshallerImpl;
 import org.apache.ignite.internal.schema.row.Row;
+import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.table.InvokeProcessor;
 import org.apache.ignite.table.RecordView;
 import org.apache.ignite.table.mapper.Mapper;
@@ -38,6 +42,12 @@ import org.jetbrains.annotations.Nullable;
  * Record view implementation.
  */
 public class RecordViewImpl<R> extends AbstractTableView implements RecordView<R> {
+    /** Marshaller factory. */
+    private final Function<SchemaDescriptor, RecordMarshallerImpl<R>> marshallerFactory;
+    
+    /** Record marshaller. */
+    private RecordMarshallerImpl<R> marsh;
+    
     /**
      * Constructor.
      *
@@ -48,202 +58,222 @@ public class RecordViewImpl<R> extends AbstractTableView implements RecordView<R
      */
     public RecordViewImpl(InternalTable tbl, SchemaRegistry schemaReg, Mapper<R> mapper, @Nullable Transaction tx) {
         super(tbl, schemaReg, tx);
+        
+        marshallerFactory = (schema) -> new RecordMarshallerImpl<>(schema, mapper);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public R get(@NotNull R keyRec) {
         return sync(getAsync(keyRec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<R> getAsync(@NotNull R keyRec) {
         Objects.requireNonNull(keyRec);
-
-        RecordSerializer<R> marsh = serializer();
-
-        Row keyRow = marsh.serialize(keyRec);  // Convert to portable format to pass TX/storage layer.
-
+        
+        BinaryRow keyRow = marshalKey(keyRec);  // Convert to portable format to pass TX/storage layer.
+        
         return tbl.get(keyRow, tx)  // Load async.
                 .thenApply(this::wrap) // Binary -> schema-aware row
-                .thenApply(marsh::deserialize); // Deserialize.
+                .thenApply(this::unmarshal); // Deserialize.
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public Collection<R> getAll(@NotNull Collection<R> keyRecs) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(getAllAsync(keyRecs));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Collection<R>> getAllAsync(@NotNull Collection<R> keyRecs) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public void upsert(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        sync(upsertAsync(rec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Void> upsertAsync(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        BinaryRow keyRow = marshal(Objects.requireNonNull(rec));
+        
+        return tbl.upsert(keyRow, tx).thenAccept(ignore -> {
+        });
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public void upsertAll(@NotNull Collection<R> recs) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        sync(upsertAllAsync(recs));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Void> upsertAllAsync(@NotNull Collection<R> recs) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public R getAndUpsert(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(getAndUpsertAsync(rec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<R> getAndUpsertAsync(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        BinaryRow keyRow = marshal(Objects.requireNonNull(rec));
+    
+        return tbl.getAndUpsert(keyRow, tx).thenApply(this::unmarshal);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public boolean insert(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(insertAsync(rec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Boolean> insertAsync(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        BinaryRow keyRow = marshal(Objects.requireNonNull(rec));
+    
+        return tbl.insert(keyRow, tx);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public Collection<R> insertAll(@NotNull Collection<R> recs) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(insertAllAsync(recs));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Collection<R>> insertAllAsync(@NotNull Collection<R> recs) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public boolean replace(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(replaceAsync(rec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public boolean replace(@NotNull R oldRec, @NotNull R newRec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(replaceAsync(oldRec, newRec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Boolean> replaceAsync(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        BinaryRow newRow = marshal(rec);
+    
+        return tbl.replace(newRow, tx);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Boolean> replaceAsync(@NotNull R oldRec, @NotNull R newRec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        BinaryRow oldRow = marshal(oldRec);
+        BinaryRow newRow = marshal(newRec);
+    
+        return tbl.replace(oldRow, newRow, tx);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public R getAndReplace(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(getAndReplaceAsync(rec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<R> getAndReplaceAsync(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        BinaryRow row = marshal(rec);
+    
+        return tbl.getAndReplace(row, tx).thenApply(this::unmarshal);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public boolean delete(@NotNull R keyRec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(deleteAsync(keyRec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Boolean> deleteAsync(@NotNull R keyRec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        BinaryRow row = marshalKey(keyRec);
+    
+        return tbl.delete(row, tx);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public boolean deleteExact(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(deleteExactAsync(rec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Boolean> deleteExactAsync(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        BinaryRow row = marshal(rec);
+    
+        return tbl.deleteExact(row, tx);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public R getAndDelete(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(getAndDeleteAsync(rec));
     }
-
+    
     /** {@inheritDoc} */
     @Override
-    public @NotNull CompletableFuture<R> getAndDeleteAsync(@NotNull R rec) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+    public @NotNull CompletableFuture<R> getAndDeleteAsync(@NotNull R keyRec) {
+        BinaryRow row = marshalKey(keyRec);
+    
+        return tbl.getAndDelete(row, tx).thenApply(this::unmarshal);
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public Collection<R> deleteAll(@NotNull Collection<R> recs) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(deleteAllAsync(recs));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Collection<R>> deleteAllAsync(@NotNull Collection<R> recs) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public Collection<R> deleteAllExact(@NotNull Collection<R> recs) {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return sync(deleteAllExactAsync(recs));
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull CompletableFuture<Collection<R>> deleteAllExactAsync(@NotNull Collection<R> recs) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public <T extends Serializable> T invoke(@NotNull R keyRec, InvokeProcessor<R, R, T> proc) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull <T extends Serializable> CompletableFuture<T> invokeAsync(
@@ -252,7 +282,7 @@ public class RecordViewImpl<R> extends AbstractTableView implements RecordView<R
     ) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public <T extends Serializable> Map<R, T> invokeAll(
@@ -261,7 +291,7 @@ public class RecordViewImpl<R> extends AbstractTableView implements RecordView<R
     ) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public @NotNull <T extends Serializable> CompletableFuture<Map<R, T>> invokeAllAsync(
@@ -270,20 +300,80 @@ public class RecordViewImpl<R> extends AbstractTableView implements RecordView<R
     ) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /** {@inheritDoc} */
     @Override
     public RecordViewImpl<R> withTransaction(Transaction tx) {
         throw new UnsupportedOperationException("Not implemented yet.");
     }
-
+    
     /**
+     * @param schemaVersion Schema version.
      * @return Marshaller.
      */
-    private RecordSerializer<R> serializer() {
-        throw new UnsupportedOperationException("Not implemented yet.");
+    private RecordMarshaller<R> marshaller(int schemaVersion) {
+        if (marsh == null || marsh.schemaVersion() == schemaVersion) {
+            // TODO: Cache marshaller for schema version or upgrade row?
+            marsh = marshallerFactory.apply(schemaReg.schema(schemaVersion));
+        }
+        
+        return marsh;
     }
-
+    
+    /**
+     * Marshals given record to a row.
+     *
+     * @param rec Record object.
+     * @return Binary row.
+     */
+    private BinaryRow marshal(@NotNull R rec) {
+        final RecordMarshaller<R> marsh = marshaller(schemaReg.lastSchemaVersion());
+        
+        try {
+            return marsh.marshal(rec);
+        } catch (MarshallerException e) {
+            throw new IgniteException(e);
+        }
+    }
+    
+    /**
+     * Marshals given key record to a row.
+     *
+     * @param rec Record key object.
+     * @return Binary row.
+     */
+    private BinaryRow marshalKey(@NotNull R rec) {
+        final RecordMarshaller<R> marsh = marshaller(schemaReg.lastSchemaVersion());
+        
+        try {
+            return marsh.marshalKey(rec);
+        } catch (MarshallerException e) {
+            throw new IgniteException(e);
+        }
+    }
+    
+    /**
+     * Unmarshal value object from given binary row.
+     *
+     * @param binaryRow Binary row.
+     * @return Value object.
+     */
+    private R unmarshal(BinaryRow binaryRow) {
+        if (binaryRow == null || !binaryRow.hasValue()) {
+            return null;
+        }
+        
+        Row row = schemaReg.resolve(binaryRow);
+        
+        RecordMarshaller<R> marshaller = marshaller(row.schemaVersion());
+        
+        try {
+            return marshaller.unmarshal(row);
+        } catch (MarshallerException e) {
+            throw new IgniteException(e);
+        }
+    }
+    
     /**
      * @param row Binary row.
      * @return Schema-aware row.
@@ -292,9 +382,9 @@ public class RecordViewImpl<R> extends AbstractTableView implements RecordView<R
         if (row == null) {
             return null;
         }
-
+        
         final SchemaDescriptor rowSchema = schemaReg.schema(row.schemaVersion()); // Get a schema for row.
-
+        
         return new Row(rowSchema, row);
     }
 }
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/KeyValueViewOperationsTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/KeyValueViewOperationsTest.java
index d1e988d..ab27f25 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/KeyValueViewOperationsTest.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/KeyValueViewOperationsTest.java
@@ -135,11 +135,11 @@ public class KeyValueViewOperationsTest {
         
         assertNull(tbl.get(key));
         
-        // Insert new tuple.
+        // Insert new KV pair.
         assertNull(tbl.getAndPut(key, obj));
-        
         assertEquals(obj, tbl.get(key));
-        
+    
+        // Update KV pair.
         assertEquals(obj, tbl.getAndPut(key, obj2));
         assertEquals(obj2, tbl.getAndPut(key, obj3));
         
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/KeyValueViewOperationsTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/RecordViewOperationsTest.java
similarity index 50%
copy from modules/table/src/test/java/org/apache/ignite/internal/table/KeyValueViewOperationsTest.java
copy to modules/table/src/test/java/org/apache/ignite/internal/table/RecordViewOperationsTest.java
index d1e988d..7313272 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/KeyValueViewOperationsTest.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/RecordViewOperationsTest.java
@@ -32,12 +32,10 @@ import static org.apache.ignite.internal.schema.NativeTypes.timestamp;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Objects;
 import java.util.Random;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -48,8 +46,9 @@ import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.internal.schema.testobjects.TestObjectWithAllTypes;
 import org.apache.ignite.internal.table.impl.DummyInternalTableImpl;
 import org.apache.ignite.internal.table.impl.DummySchemaManagerImpl;
-import org.apache.ignite.table.KeyValueView;
+import org.apache.ignite.table.RecordView;
 import org.apache.ignite.table.mapper.Mapper;
+import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -57,7 +56,7 @@ import org.junit.jupiter.api.Test;
  */
 //TODO: IGNITE-14487 Add bulk operations tests.
 //TODO: IGNITE-14487 Add async operations tests.
-public class KeyValueViewOperationsTest {
+public class RecordViewOperationsTest {
     
     private final Random rnd = new Random();
     
@@ -65,35 +64,31 @@ public class KeyValueViewOperationsTest {
      *
      */
     @Test
-    public void put() {
-        final TestKeyObject key = TestKeyObject.randomObject(rnd);
-        final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj3 = TestObjectWithAllTypes.randomObject(rnd);
+    public void upsert() {
+        final TestObjectWithAllTypes key = key(rnd);
         
-        KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
+        final TestObjectWithAllTypes obj = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj2 = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj3 = randomObject(rnd, key);
         
-        assertNull(tbl.get(key));
+        RecordView<TestObjectWithAllTypes> tbl = recordView();
         
-        // Put KV pair.
-        tbl.put(key, obj);
+        assertNull(tbl.get(key));
         
-        assertEquals(obj, tbl.get(key));
+        // Insert new row.
+        tbl.upsert(obj);
         assertEquals(obj, tbl.get(key));
         
-        // Update KV pair.
-        tbl.put(key, obj2);
-        
-        assertEquals(obj2, tbl.get(key));
+        // Upsert row.
+        tbl.upsert(obj2);
         assertEquals(obj2, tbl.get(key));
         
-        // Remove KV pair.
-        tbl.put(key, null);
-        
+        // Remove row.
+        tbl.delete(key);
         assertNull(tbl.get(key));
         
-        // Put KV pair.
-        tbl.put(key, obj3);
+        // Insert new row.
+        tbl.upsert(obj3);
         assertEquals(obj3, tbl.get(key));
     }
     
@@ -101,23 +96,21 @@ public class KeyValueViewOperationsTest {
      *
      */
     @Test
-    public void putIfAbsent() {
-        final TestKeyObject key = TestKeyObject.randomObject(rnd);
-        final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
+    public void insert() {
+        final TestObjectWithAllTypes key = key(rnd);
+        final TestObjectWithAllTypes obj = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj2 = randomObject(rnd, key);
         
-        KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
+        RecordView<TestObjectWithAllTypes> tbl = recordView();
         
         assertNull(tbl.get(key));
         
-        // Insert new KV pair.
-        assertTrue(tbl.putIfAbsent(key, obj));
-        
+        // Insert new row.
+        assertTrue(tbl.insert(obj));
         assertEquals(obj, tbl.get(key));
         
-        // Update KV pair.
-        assertFalse(tbl.putIfAbsent(key, obj2));
-        
+        // Ignore existed row pair.
+        assertFalse(tbl.insert(obj2));
         assertEquals(obj, tbl.get(key));
     }
     
@@ -125,23 +118,23 @@ public class KeyValueViewOperationsTest {
      *
      */
     @Test
-    public void getAndPut() {
-        final TestKeyObject key = TestKeyObject.randomObject(rnd);
-        final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj3 = TestObjectWithAllTypes.randomObject(rnd);
+    public void getAndUpsert() {
+        final TestObjectWithAllTypes key = key(rnd);
+        final TestObjectWithAllTypes obj = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj2 = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj3 = randomObject(rnd, key);
         
-        KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
+        RecordView<TestObjectWithAllTypes> tbl = recordView();
         
         assertNull(tbl.get(key));
         
-        // Insert new tuple.
-        assertNull(tbl.getAndPut(key, obj));
-        
+        // Insert new row.
+        assertNull(tbl.getAndUpsert(obj));
         assertEquals(obj, tbl.get(key));
         
-        assertEquals(obj, tbl.getAndPut(key, obj2));
-        assertEquals(obj2, tbl.getAndPut(key, obj3));
+        // Update exited row.
+        assertEquals(obj, tbl.getAndUpsert(obj2));
+        assertEquals(obj2, tbl.getAndUpsert(obj3));
         
         assertEquals(obj3, tbl.get(key));
     }
@@ -149,69 +142,64 @@ public class KeyValueViewOperationsTest {
     /**
      *
      */
-    @Test
+ /*   @Test
     public void contains() {
-        final TestKeyObject key = TestKeyObject.randomObject(rnd);
+        final TestObjectWithAllTypes key = key(rnd);
         final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
-        final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
-    
-        KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
+        final TestObjectWithAllTypes obj = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj2 = randomObject(rnd, key);
+        
+        RecordView<TestObjectWithAllTypes> tbl = recordView();
         
         // Not-existed value.
         assertFalse(tbl.contains(key));
         
         // Put KV pair.
-        tbl.put(key, obj);
+        tbl.upsert(obj);
         assertTrue(tbl.contains(key));
         
         // Delete key.
-        assertTrue(tbl.remove(key));
+        assertTrue(tbl.delete(key));
         assertFalse(tbl.contains(key));
         
         // Put KV pair.
-        tbl.put(key, obj2);
+        tbl.upsert(obj2);
         assertTrue(tbl.contains(key));
         
         // Delete key.
-        tbl.remove(key2);
+        tbl.delete(key2);
         assertFalse(tbl.contains(key2));
-    }
+    }*/
     
     /**
      *
      */
     @Test
     public void remove() {
-        final TestKeyObject key = TestKeyObject.randomObject(rnd);
-        final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
-        final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
+        final TestObjectWithAllTypes key = key(rnd);
+        final TestObjectWithAllTypes obj = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj2 = randomObject(rnd, key);
         
-        KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
+        RecordView<TestObjectWithAllTypes> tbl = recordView();
         
-        // Put KV pair.
-        tbl.put(key, obj);
+        // Delete not existed key.
+        assertNull(tbl.get(key));
+        assertFalse(tbl.delete(key));
+        
+        // Insert a new row.
+        tbl.upsert(obj);
         
-        // Delete existed key.
+        // Delete existed row.
         assertEquals(obj, tbl.get(key));
-        assertTrue(tbl.remove(key));
+        assertTrue(tbl.delete(key));
         assertNull(tbl.get(key));
         
-        // Delete already deleted key.
-        assertFalse(tbl.remove(key));
+        // Delete already deleted row.
+        assertFalse(tbl.delete(key));
         
-        // Put KV pair.
-        tbl.put(key, obj2);
+        // Insert a new row.
+        tbl.upsert(obj2);
         assertEquals(obj2, tbl.get(key));
-        
-        // Delete existed key.
-        assertTrue(tbl.remove(key));
-        assertNull(tbl.get(key));
-        
-        // Delete not existed key.
-        assertNull(tbl.get(key2));
-        assertFalse(tbl.remove(key2));
     }
     
     /**
@@ -219,47 +207,38 @@ public class KeyValueViewOperationsTest {
      */
     @Test
     public void removeExact() {
-        final TestKeyObject key = TestKeyObject.randomObject(rnd);
-        final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
-        final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
+        final TestObjectWithAllTypes key = key(rnd);
+        final TestObjectWithAllTypes obj = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj2 = randomObject(rnd, key);
         
-        KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
+        RecordView<TestObjectWithAllTypes> tbl = recordView();
         
-        // Put KV pair.
-        tbl.put(key, obj);
+        // Insert a new row.
+        tbl.upsert(obj);
         assertEquals(obj, tbl.get(key));
         
-        // Fails to delete KV pair with unexpected value.
-        assertFalse(tbl.remove(key, obj2));
+        // Fails to delete row with unexpected value.
+        assertFalse(tbl.deleteExact(obj2));
         assertEquals(obj, tbl.get(key));
         
-        // Delete KV pair with expected value.
-        assertTrue(tbl.remove(key, obj));
-        assertNull(tbl.get(key));
-        
-        // Once again.
-        assertFalse(tbl.remove(key, obj));
+        // Delete row with expected value.
+        assertTrue(tbl.deleteExact(obj));
         assertNull(tbl.get(key));
         
         // Try to remove non-existed key.
-        assertFalse(tbl.remove(key, obj));
+        assertFalse(tbl.deleteExact(obj));
         assertNull(tbl.get(key));
         
-        // Put KV pair.
-        tbl.put(key, obj2);
-        assertEquals(obj2, tbl.get(key));
-        
-        // Check null value ignored.
-        assertThrows(Throwable.class, () -> tbl.remove(key, null));
+        // Insert a new row.
+        tbl.upsert(obj2);
         assertEquals(obj2, tbl.get(key));
         
-        // Delete KV pair with expected value.
-        assertTrue(tbl.remove(key, obj2));
+        // Delete row with expected value.
+        assertTrue(tbl.delete(obj2));
         assertNull(tbl.get(key));
         
-        assertFalse(tbl.remove(key2, obj2));
-        assertNull(tbl.get(key2));
+        assertFalse(tbl.delete(obj2));
+        assertNull(tbl.get(obj2));
     }
     
     /**
@@ -267,38 +246,34 @@ public class KeyValueViewOperationsTest {
      */
     @Test
     public void replace() {
-        final TestKeyObject key = TestKeyObject.randomObject(rnd);
-        final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
-        final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj3 = TestObjectWithAllTypes.randomObject(rnd);
+        final TestObjectWithAllTypes key = key(rnd);
+        final TestObjectWithAllTypes obj = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj2 = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj3 = randomObject(rnd, key);
         
-        KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
+        RecordView<TestObjectWithAllTypes> tbl = recordView();
         
-        // Ignore replace operation for non-existed KV pair.
-        assertFalse(tbl.replace(key, obj));
+        // Ignore replace operation for non-existed row.
+        assertFalse(tbl.replace(obj));
         assertNull(tbl.get(key));
         
-        tbl.put(key, obj);
+        // Insert new row.
+        tbl.upsert(obj);
         
-        // Replace existed KV pair.
-        assertTrue(tbl.replace(key, obj2));
+        // Replace existed row.
+        assertTrue(tbl.replace(obj2));
         assertEquals(obj2, tbl.get(key));
         
-        // Remove existed KV pair.
-        assertTrue(tbl.replace(key, null));
-        assertNull(tbl.get(key));
+        // Replace existed row.
+        assertTrue(tbl.replace(obj3));
+        assertEquals(obj3, tbl.get(key));
         
-        // Ignore replace operation for non-existed KV pair.
-        assertFalse(tbl.replace(key, obj3));
+        // Remove existed row.
+        assertTrue(tbl.delete(key));
         assertNull(tbl.get(key));
         
-        tbl.put(key, obj3);
-        assertEquals(obj3, tbl.get(key));
-        
-        // Remove non-existed KV pair.
-        assertFalse(tbl.replace(key2, null));
-        assertNull(tbl.get(key2));
+        tbl.upsert(obj);
+        assertEquals(obj, tbl.get(key));
     }
     
     /**
@@ -306,51 +281,52 @@ public class KeyValueViewOperationsTest {
      */
     @Test
     public void replaceExact() {
-        final TestKeyObject key = TestKeyObject.randomObject(rnd);
-        final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
-        final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
-        final TestObjectWithAllTypes obj3 = TestObjectWithAllTypes.randomObject(rnd);
+        final TestObjectWithAllTypes key = key(rnd);
+        final TestObjectWithAllTypes obj = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj2 = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj3 = randomObject(rnd, key);
+        final TestObjectWithAllTypes obj4 = randomObject(rnd, key);
+        
+        RecordView<TestObjectWithAllTypes> tbl = recordView();
+        
+        // Ignore replace operation for non-existed row.
+        assertFalse(tbl.replace(obj, obj2));
+        assertNull(tbl.get(key));
         
-        KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
+        // Insert new row.
+        tbl.upsert(obj);
         
-        // Insert KV pair.
-        assertTrue(tbl.replace(key, null, obj));
+        // Ignore un-exepected row replacement.
+        assertFalse(tbl.replace(obj2, obj3));
         assertEquals(obj, tbl.get(key));
-        assertNull(tbl.get(key2));
         
-        // Ignore replace operation for non-existed KV pair.
-        assertFalse(tbl.replace(key2, obj, obj2));
-        assertNull(tbl.get(key2));
+        // Replace existed row.
+        assertTrue(tbl.replace(obj, obj2));
+        assertEquals(obj2, tbl.get(key));
         
         // Replace existed KV pair.
-        assertTrue(tbl.replace(key, obj, obj2));
-        assertEquals(obj2, tbl.get(key));
+        assertTrue(tbl.replace(obj2, obj3));
+        assertEquals(obj3, tbl.get(key));
         
-        // Remove existed KV pair.
-        assertTrue(tbl.replace(key, obj2, null));
+        // Remove existed row.
+        assertTrue(tbl.delete(key));
         assertNull(tbl.get(key));
         
-        // Insert KV pair.
-        assertTrue(tbl.replace(key, null, obj3));
-        assertEquals(obj3, tbl.get(key));
-        
-        // Remove non-existed KV pair.
-        assertTrue(tbl.replace(key2, null, null));
+        assertFalse(tbl.replace(key, obj4));
+        assertNull(tbl.get(key));
     }
     
     /**
      * @return Key-value view.
      */
-    private KeyValueViewImpl<TestKeyObject, TestObjectWithAllTypes> kvView() {
-        Mapper<TestKeyObject> keyMapper = Mapper.identity(TestKeyObject.class);
-        Mapper<TestObjectWithAllTypes> valMapper = Mapper.identity(TestObjectWithAllTypes.class);
+    private RecordViewImpl<TestObjectWithAllTypes> recordView() {
+        Mapper<TestObjectWithAllTypes> recMapper = Mapper.identity(TestObjectWithAllTypes.class);
         
         Column[] valCols = {
                 new Column("primitiveByteCol", INT8, false),
                 new Column("primitiveShortCol", INT16, false),
                 new Column("primitiveIntCol", INT32, false),
-                new Column("primitiveLongCol", INT64, false),
+//                new Column("primitiveLongCol", INT64, false),
                 new Column("primitiveFloatCol", FLOAT, false),
                 new Column("primitiveDoubleCol", DOUBLE, false),
                 
@@ -378,64 +354,41 @@ public class KeyValueViewOperationsTest {
         
         SchemaDescriptor schema = new SchemaDescriptor(
                 1,
-                new Column[]{new Column("id", NativeTypes.INT64, false)},
+                new Column[]{new Column("primitiveLongCol", NativeTypes.INT64, false)},
                 valCols
         );
-    
+        
         // Validate all types are tested.
         Set<NativeTypeSpec> testedTypes = Arrays.stream(valCols).map(c -> c.type().spec())
                 .collect(Collectors.toSet());
         Set<NativeTypeSpec> missedTypes = Arrays.stream(NativeTypeSpec.values())
                 .filter(t -> !testedTypes.contains(t)).collect(Collectors.toSet());
-    
+        
         assertEquals(Collections.emptySet(), missedTypes);
-    
-        return new KeyValueViewImpl<>(
+        
+        return new RecordViewImpl<>(
                 new DummyInternalTableImpl(),
                 new DummySchemaManagerImpl(schema),
-                keyMapper,
-                valMapper,
+                recMapper,
                 null
         );
     }
     
-    /**
-     * Test object.
-     */
-    @SuppressWarnings({"InstanceVariableMayNotBeInitialized", "unused"})
-    public static class TestKeyObject {
-        public static TestKeyObject randomObject(Random rnd) {
-            return new TestKeyObject(rnd.nextLong());
-        }
-        
-        private long id;
-        
-        private TestKeyObject() {
-        }
-        
-        public TestKeyObject(long id) {
-            this.id = id;
-        }
-        
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            
-            TestKeyObject that = (TestKeyObject) o;
-            
-            return id == that.id;
-        }
-        
-        @Override
-        public int hashCode() {
-            return Objects.hash(id);
-        }
+    @NotNull
+    private TestObjectWithAllTypes randomObject(Random rnd, TestObjectWithAllTypes key) {
+        TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
+        
+        obj.setPrimitiveLongCol(key.getPrimitiveLongCol());
+        
+        return obj;
     }
     
+    @NotNull
+    private static TestObjectWithAllTypes key(Random rnd) {
+        TestObjectWithAllTypes key = new TestObjectWithAllTypes();
+        
+        key.setPrimitiveLongCol(rnd.nextLong());
+        
+        return key;
+    }
 }