You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by il...@apache.org on 2020/04/06 14:46:01 UTC

[ignite] branch master updated: IGNITE-12863 Initialize PojoField accessor after deserialization - Fixes #7624.

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

ilyak pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new c0f3708  IGNITE-12863 Initialize PojoField accessor after deserialization - Fixes #7624.
c0f3708 is described below

commit c0f3708f8a2cc7d31d46dcea92512df12a3c2a84
Author: Aleksandr Serbin <al...@gmail.com>
AuthorDate: Mon Apr 6 17:39:06 2020 +0300

    IGNITE-12863 Initialize PojoField accessor after deserialization - Fixes #7624.
    
    Signed-off-by: Ilya Kasnacheev <il...@gmail.com>
---
 .../persistence/KeyPersistenceSettings.java        |  35 +++++--
 .../persistence/KeyValuePersistenceSettings.java   |  15 +--
 .../persistence/PersistenceController.java         |   8 +-
 .../cassandra/persistence/PersistenceSettings.java |  72 ++++++++++----
 .../store/cassandra/persistence/PojoField.java     |  14 ++-
 .../store/cassandra/persistence/PojoKeyField.java  |  14 ++-
 .../cassandra/persistence/PojoValueField.java      |  16 +++
 .../persistence/ValuePersistenceSettings.java      |  26 ++++-
 .../ignite/tests/IgnitePersistentStoreTest.java    | 109 +++++++++++++++++++--
 9 files changed, 252 insertions(+), 57 deletions(-)

diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java
index 2f2e18e..dd45058 100644
--- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java
+++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.cache.store.cassandra.persistence;
 
+import java.io.IOException;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -27,7 +28,7 @@ import org.w3c.dom.NodeList;
 /**
  * Stores persistence settings for Ignite cache key
  */
-public class KeyPersistenceSettings extends PersistenceSettings {
+public class KeyPersistenceSettings extends PersistenceSettings<PojoKeyField> {
     /** Partition key XML tag. */
     private static final String PARTITION_KEY_ELEMENT = "partitionKey";
 
@@ -38,13 +39,13 @@ public class KeyPersistenceSettings extends PersistenceSettings {
     private static final String FIELD_ELEMENT = "field";
 
     /** POJO fields. */
-    private List<PojoField> fields = new LinkedList<>();
+    private List<PojoKeyField> fields = new LinkedList<>();
 
     /** Partition key fields. */
-    private List<PojoField> partKeyFields = new LinkedList<>();
+    private List<PojoKeyField> partKeyFields = new LinkedList<>();
 
     /** Cluster key fields. */
-    private List<PojoField> clusterKeyFields = new LinkedList<>();
+    private List<PojoKeyField> clusterKeyFields = new LinkedList<>();
 
     /**
      * Creates key persistence settings object based on it's XML configuration.
@@ -84,10 +85,10 @@ public class KeyPersistenceSettings extends PersistenceSettings {
                     getJavaClass().getName() + "'");
         }
 
-        List<PojoField> filteredFields = new LinkedList<>();
+        List<PojoKeyField> filteredFields = new LinkedList<>();
 
         // Find all fields annotated by @AffinityKeyMapped
-        for (PojoField field : partKeyFields) {
+        for (PojoKeyField field : partKeyFields) {
             if (field.getAnnotation(AffinityKeyMapped.class) != null)
                 filteredFields.add(field);
         }
@@ -101,7 +102,7 @@ public class KeyPersistenceSettings extends PersistenceSettings {
         filteredFields = new LinkedList<>();
 
         // Removing out all fields which are already in partition key fields list
-        for (PojoField field : clusterKeyFields) {
+        for (PojoKeyField field : clusterKeyFields) {
             if (!PojoField.containsField(partKeyFields, field.getName()))
                 filteredFields.add(field);
         }
@@ -118,20 +119,25 @@ public class KeyPersistenceSettings extends PersistenceSettings {
     }
 
     /** {@inheritDoc} */
-    @Override public List<PojoField> getFields() {
+    @Override public List<PojoKeyField> getFields() {
         return fields;
     }
 
     /** {@inheritDoc} */
-    @Override protected PojoField createPojoField(Element el, Class clazz) {
+    @Override protected PojoKeyField createPojoField(Element el, Class clazz) {
         return new PojoKeyField(el, clazz);
     }
 
     /** {@inheritDoc} */
-    @Override protected PojoField createPojoField(PojoFieldAccessor accessor) {
+    @Override protected PojoKeyField createPojoField(PojoFieldAccessor accessor) {
         return new PojoKeyField(accessor);
     }
 
+    /** {@inheritDoc} */
+    @Override protected PojoKeyField createPojoField(PojoKeyField field, Class clazz) {
+        return new PojoKeyField(field, clazz);
+    }
+
     /**
      * Returns Cassandra DDL for primary key.
      *
@@ -231,4 +237,13 @@ public class KeyPersistenceSettings extends PersistenceSettings {
 
         return cols;
     }
+
+    /**
+     * @see java.io.Serializable
+     */
+    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+
+        fields = enrichFields(fields);
+    }
 }
diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyValuePersistenceSettings.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyValuePersistenceSettings.java
index 5f21bbd..9c21f7e 100644
--- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyValuePersistenceSettings.java
+++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyValuePersistenceSettings.java
@@ -221,7 +221,7 @@ public class KeyValuePersistenceSettings implements Serializable {
      *
      * @return POJO fields list.
      */
-    public List<PojoField> getKeyFields() {
+    public List<PojoKeyField> getKeyFields() {
         return keyPersistenceSettings.getFields();
     }
 
@@ -230,7 +230,7 @@ public class KeyValuePersistenceSettings implements Serializable {
      *
      * @return POJO fields list.
      */
-    public List<PojoField> getValueFields() {
+    public List<PojoValueField> getValueFields() {
         return valPersistenceSettings.getFields();
     }
 
@@ -316,11 +316,12 @@ public class KeyValuePersistenceSettings implements Serializable {
     public List<String> getIndexDDLStatements(String table) {
         List<String> idxDDLs = new LinkedList<>();
 
-        Set<String> keyColumns = new HashSet<>(keyPersistenceSettings.getTableColumns());
-        List<PojoField> fields = valPersistenceSettings.getFields();
+        Set<String> keyCols = new HashSet<>(keyPersistenceSettings.getTableColumns());
+
+        List<PojoValueField> fields = valPersistenceSettings.getFields();
 
         for (PojoField field : fields) {
-            if (!keyColumns.contains(field.getColumn()) && ((PojoValueField)field).isIndexed())
+            if (!keyCols.contains(field.getColumn()) && ((PojoValueField)field).isIndexed())
                 idxDDLs.add(((PojoValueField)field).getIndexDDL(keyspace, table));
         }
 
@@ -456,8 +457,8 @@ public class KeyValuePersistenceSettings implements Serializable {
                 "there are no value persistence settings specified");
         }
 
-        List<PojoField> keyFields = keyPersistenceSettings.getFields();
-        List<PojoField> valFields = valPersistenceSettings.getFields();
+        List<PojoKeyField> keyFields = keyPersistenceSettings.getFields();
+        List<PojoValueField> valFields = valPersistenceSettings.getFields();
 
         if (PersistenceStrategy.POJO == keyPersistenceSettings.getStrategy() &&
             (keyFields == null || keyFields.isEmpty())) {
diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceController.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceController.java
index e64ff5f..3b54a8a 100644
--- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceController.java
+++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceController.java
@@ -42,10 +42,10 @@ public class PersistenceController {
     private final KeyValuePersistenceSettings persistenceSettings;
 
     /** List of key unique POJO fields (skipping aliases pointing to the same Cassandra table column). */
-    private final List<PojoField> keyUniquePojoFields;
+    private final List<? extends PojoField> keyUniquePojoFields;
 
     /** List of value unique POJO fields (skipping aliases pointing to the same Cassandra table column). */
-    private final List<PojoField> valUniquePojoFields;
+    private final List<? extends PojoField> valUniquePojoFields;
 
     /** CQL statement template to insert row into Cassandra table. */
     private final String writeStatementTempl;
@@ -91,7 +91,7 @@ public class PersistenceController {
 
         keyUniquePojoFields = settings.getKeyPersistenceSettings().cassandraUniqueFields();
 
-        List<PojoField> _valUniquePojoFields = settings.getValuePersistenceSettings().cassandraUniqueFields();
+        List<? extends PojoField> _valUniquePojoFields = settings.getValuePersistenceSettings().cassandraUniqueFields();
 
         if (_valUniquePojoFields == null || _valUniquePojoFields.isEmpty()) {
             valUniquePojoFields = _valUniquePojoFields;
@@ -421,7 +421,7 @@ public class PersistenceController {
      *
      * @return next offset
      */
-    private int bindValues(PersistenceStrategy stgy, Serializer serializer, List<PojoField> fields, Object obj,
+    private int bindValues(PersistenceStrategy stgy, Serializer serializer, List<? extends PojoField> fields, Object obj,
                             Object[] values, int offset) {
         if (PersistenceStrategy.PRIMITIVE == stgy) {
             if (PropertyMappingHelper.getCassandraType(obj.getClass()) == null ||
diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java
index c8a0a90..0ae3045 100644
--- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java
+++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java
@@ -19,14 +19,11 @@ package org.apache.ignite.cache.store.cassandra.persistence;
 
 import com.datastax.driver.core.DataType;
 import java.beans.PropertyDescriptor;
+import java.io.IOException;
 import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.LinkedList;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.Collections;
+import java.util.*;
 
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.ignite.IgniteException;
@@ -42,7 +39,7 @@ import org.w3c.dom.NodeList;
  * Stores persistence settings, which describes how particular key/value
  * from Ignite cache should be stored in Cassandra.
  */
-public abstract class PersistenceSettings implements Serializable {
+public abstract class PersistenceSettings<F extends PojoField> implements Serializable {
     /** Xml attribute specifying persistence strategy. */
     private static final String STRATEGY_ATTR = "strategy";
 
@@ -75,7 +72,7 @@ public abstract class PersistenceSettings implements Serializable {
      * List of POJO fields having unique mapping to Cassandra columns - skipping aliases pointing
      *  to the same Cassandra table column.
      */
-    private List<PojoField> casUniqueFields;
+    private List<F> casUniqueFields;
 
     /**
      * Extracts property descriptor from the descriptors list by its name.
@@ -229,7 +226,7 @@ public abstract class PersistenceSettings implements Serializable {
      *
      * @return list of fields.
      */
-    public abstract List<PojoField> getFields();
+    public abstract List<F> getFields();
 
     /**
      * Returns POJO field by Cassandra table column name.
@@ -239,7 +236,7 @@ public abstract class PersistenceSettings implements Serializable {
      * @return POJO field or null if not exists.
      */
     public PojoField getFieldByColumn(String column) {
-        List<PojoField> fields = getFields();
+        List<F> fields = getFields();
 
         if (fields == null || fields.isEmpty())
             return null;
@@ -258,7 +255,7 @@ public abstract class PersistenceSettings implements Serializable {
      *
      * @return List of fields.
      */
-    public List<PojoField> cassandraUniqueFields() {
+    public List<F> cassandraUniqueFields() {
         return casUniqueFields;
     }
 
@@ -293,7 +290,7 @@ public abstract class PersistenceSettings implements Serializable {
         if (PersistenceStrategy.PRIMITIVE == stgy)
             return "  \"" + col + "\" " + PropertyMappingHelper.getCassandraType(javaCls);
 
-        List<PojoField> fields = getFields();
+        List<F> fields = getFields();
 
         if (fields == null || fields.isEmpty()) {
             throw new IllegalStateException("There are no POJO fields found for '" + javaCls.toString()
@@ -306,7 +303,7 @@ public abstract class PersistenceSettings implements Serializable {
 
         StringBuilder builder = new StringBuilder();
 
-        for (PojoField field : fields) {
+        for (F field : fields) {
             if ((ignoreColumns != null && ignoreColumns.contains(field.getColumn())) ||
                     processedColumns.contains(field.getColumn())) {
                 continue;
@@ -336,14 +333,23 @@ public abstract class PersistenceSettings implements Serializable {
      * @param el XML element describing POJO field
      * @param clazz POJO java class.
      */
-    protected abstract PojoField createPojoField(Element el, Class clazz);
+    protected abstract F createPojoField(Element el, Class clazz);
 
     /**
      * Creates instance of {@link PojoField} from its field accessor.
      *
      * @param accessor field accessor.
      */
-    protected abstract PojoField createPojoField(PojoFieldAccessor accessor);
+    protected abstract F createPojoField(PojoFieldAccessor accessor);
+
+    /**
+     * Creates instance of {@link PojoField} based on the other instance and java class
+     * to initialize accessor.
+     *
+     * @param field PojoField instance
+     * @param clazz java class
+     */
+    protected abstract F createPojoField(F field, Class clazz);
 
     /**
      * Class instance initialization.
@@ -357,7 +363,7 @@ public abstract class PersistenceSettings implements Serializable {
             return;
         }
 
-        List<PojoField> fields = getFields();
+        List<F> fields = getFields();
 
         if (fields == null || fields.isEmpty())
             return;
@@ -365,7 +371,7 @@ public abstract class PersistenceSettings implements Serializable {
         tableColumns = new LinkedList<>();
         casUniqueFields = new LinkedList<>();
 
-        for (PojoField field : fields) {
+        for (F field : fields) {
             if (!tableColumns.contains(field.getColumn())) {
                 tableColumns.add(field.getColumn());
                 casUniqueFields.add(field);
@@ -381,7 +387,7 @@ public abstract class PersistenceSettings implements Serializable {
      *
      * @param fields List of fields to be persisted into Cassandra.
      */
-    protected void checkDuplicates(List<PojoField> fields) {
+    protected void checkDuplicates(List<F> fields) {
         if (fields == null || fields.isEmpty())
             return;
 
@@ -419,14 +425,14 @@ public abstract class PersistenceSettings implements Serializable {
      * @param fieldNodes Field nodes to process.
      * @return POJO fields list.
      */
-    protected List<PojoField> detectPojoFields(NodeList fieldNodes) {
-        List<PojoField> detectedFields = new LinkedList<>();
+    protected List<F> detectPojoFields(NodeList fieldNodes) {
+        List<F> detectedFields = new LinkedList<>();
 
         if (fieldNodes != null && fieldNodes.getLength() != 0) {
             int cnt = fieldNodes.getLength();
 
             for (int i = 0; i < cnt; i++) {
-                PojoField field = createPojoField((Element)fieldNodes.item(i), getJavaClass());
+                F field = createPojoField((Element)fieldNodes.item(i), getJavaClass());
 
                 // Just checking that such field exists in the class
                 PropertyMappingHelper.getPojoFieldAccessor(getJavaClass(), field.getName());
@@ -518,4 +524,30 @@ public abstract class PersistenceSettings implements Serializable {
             throw new IgniteException("Failed to instantiate class '" + clazz + "' using default constructor", e);
         }
     }
+
+    /**
+     * @see java.io.Serializable
+     */
+    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        casUniqueFields = Collections.unmodifiableList(enrichFields(casUniqueFields));
+    }
+
+    /**
+     * Sets accessor for the given {@code src} fields.
+     * Required as accessor is transient and is not present
+     * after deserialization.
+     */
+    protected List<F> enrichFields(List<F> src) {
+        if (src != null) {
+            List<F> enriched = new ArrayList<>(src.size());
+
+            for (F sourceField : src)
+                enriched.add(createPojoField(sourceField, getJavaClass()));
+
+            return enriched;
+        }
+        else
+            return null;
+    }
 }
diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java
index 386628b..d2bc4dc 100644
--- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java
+++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java
@@ -61,7 +61,7 @@ public abstract class PojoField implements Serializable {
      * @param fieldName field name.
      * @return true if list contains field or false otherwise.
      */
-    public static boolean containsField(List<PojoField> fields, String fieldName) {
+    public static boolean containsField(List<? extends PojoField> fields, String fieldName) {
         if (fields == null || fields.isEmpty())
             return false;
 
@@ -111,6 +111,18 @@ public abstract class PojoField implements Serializable {
     }
 
     /**
+     * Creates instance of {@link PojoField} from the other instance
+     * and java class.
+     */
+    public PojoField(PojoField field, Class<?> pojoCls) {
+        this.name = field.name;
+        this.col = field.col;
+        this.colDDL = field.colDDL;
+
+        init(PropertyMappingHelper.getPojoFieldAccessor(pojoCls, name));
+    }
+
+    /**
      * @return field name.
      */
     public String getName() {
diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java
index 52b4584..2b02fe5 100644
--- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java
+++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java
@@ -24,7 +24,6 @@ import org.w3c.dom.Element;
  * Descriptor for Ignite key POJO class
  */
 public class PojoKeyField extends PojoField {
-
     /**
      * Specifies sort order for POJO key field
      */
@@ -61,6 +60,19 @@ public class PojoKeyField extends PojoField {
     }
 
     /**
+     * Constructs instance of {@code PojoKeyField} based on the other instance and java class
+     * to initialize accessor.
+     *
+     * @param field PojoKeyField instance
+     * @param pojoCls java class of the corresponding POJO
+     */
+    public PojoKeyField(PojoKeyField field, Class<?> pojoCls) {
+        super(field, pojoCls);
+
+        sortOrder = field.sortOrder;
+    }
+
+    /**
      * Constructs Ignite cache key POJO object descriptor.
      *
      * @param accessor property descriptor.
diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java
index 36bcd0c..0427e6c 100644
--- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java
+++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java
@@ -90,6 +90,22 @@ public class PojoValueField extends PojoField {
         isIndexed = sqlField != null && sqlField.index();
     }
 
+    /**
+     * Constructs instance of {@code PojoValueField} based on the other instance and java class
+     * to initialize accessor.
+     *
+     * @param field PojoValueField instance
+     * @param pojoCls java class of the corresponding POJO
+     */
+    public PojoValueField(PojoValueField field, Class<?> pojoCls) {
+        super(field, pojoCls);
+
+        isStatic = field.isStatic;
+        isIndexed = field.isIndexed;
+        idxCls = field.idxCls;
+        idxOptions = field.idxOptions;
+    }
+
     /** {@inheritDoc} */
     @Override public String getColumnDDL() {
         String colDDL = super.getColumnDDL();
diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java
index 5824b6f..5e106af 100644
--- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java
+++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.cache.store.cassandra.persistence;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
@@ -27,12 +28,12 @@ import org.w3c.dom.NodeList;
 /**
  * Stores persistence settings for Ignite cache value
  */
-public class ValuePersistenceSettings extends PersistenceSettings {
+public class ValuePersistenceSettings extends PersistenceSettings<PojoValueField> {
     /** XML element describing value field settings. */
     private static final String FIELD_ELEMENT = "field";
 
     /** Value fields. */
-    private List<PojoField> fields = new LinkedList<>();
+    private List<PojoValueField> fields = new LinkedList<>();
 
     /**
      * Creates class instance from XML configuration.
@@ -63,7 +64,7 @@ public class ValuePersistenceSettings extends PersistenceSettings {
     /**
      * @return List of value fields.
      */
-    @Override public List<PojoField> getFields() {
+    @Override public List<PojoValueField> getFields() {
         return fields == null ? null : Collections.unmodifiableList(fields);
     }
 
@@ -73,12 +74,27 @@ public class ValuePersistenceSettings extends PersistenceSettings {
     }
 
     /** {@inheritDoc} */
-    @Override protected PojoField createPojoField(Element el, Class clazz) {
+    @Override protected PojoValueField createPojoField(Element el, Class clazz) {
         return new PojoValueField(el, clazz);
     }
 
     /** {@inheritDoc} */
-    @Override protected PojoField createPojoField(PojoFieldAccessor accessor) {
+    @Override protected PojoValueField createPojoField(PojoFieldAccessor accessor) {
         return new PojoValueField(accessor);
     }
+
+    /** {@inheritDoc} */
+    @Override protected PojoValueField createPojoField(PojoValueField field, Class clazz) {
+        return new PojoValueField(field, clazz);
+    }
+
+    /**
+     * @see java.io.Serializable
+     */
+    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+
+        fields = enrichFields(fields);
+    }
+
 }
diff --git a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java
index 579bbe1..e68fd5c 100644
--- a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java
+++ b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java
@@ -18,9 +18,7 @@
 package org.apache.ignite.tests;
 
 import com.datastax.driver.core.SimpleStatement;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Map;
+import com.datastax.driver.core.policies.RoundRobinPolicy;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteTransactions;
@@ -28,11 +26,17 @@ import org.apache.ignite.Ignition;
 import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.cache.CachePeekMode;
 import org.apache.ignite.cache.store.CacheStore;
+import org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory;
+import org.apache.ignite.cache.store.cassandra.datasource.DataSource;
+import org.apache.ignite.cache.store.cassandra.persistence.KeyValuePersistenceSettings;
 import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.tests.pojos.*;
 import org.apache.ignite.tests.utils.CacheStoreHelper;
+import org.apache.ignite.tests.utils.CassandraAdminCredentials;
 import org.apache.ignite.tests.utils.CassandraHelper;
 import org.apache.ignite.tests.utils.TestsHelper;
 import org.apache.ignite.transactions.Transaction;
@@ -45,12 +49,11 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 import org.springframework.core.io.ClassPathResource;
 
-import org.apache.ignite.tests.pojos.Product;
-import org.apache.ignite.tests.pojos.ProductOrder;
-import org.apache.ignite.tests.pojos.Person;
-import org.apache.ignite.tests.pojos.SimplePerson;
-import org.apache.ignite.tests.pojos.PersonId;
-import org.apache.ignite.tests.pojos.SimplePersonId;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
 
 /**
  * Unit tests for Ignite caches which utilizing {@link org.apache.ignite.cache.store.cassandra.CassandraCacheStore}
@@ -787,6 +790,94 @@ public class IgnitePersistentStoreTest {
         LOGGER.info("-----------------------------------------------------------------------------------");
     }
 
+    /*
+     * KeyValuePersistenceSettings is passed directly, not as a bean and should be
+     * serialized and deserialized correctly
+     */
+    @Test
+    public void directPersistenceConfigTest() throws IOException {
+        Map<PersonId, Person> personMap = TestsHelper.generatePersonIdsPersonsMap();
+        PersonId id = TestsHelper.generateRandomPersonId();
+        Person person = TestsHelper.generateRandomPerson(id.getPersonNumber());
+
+        IgniteConfiguration config = igniteConfig();
+
+        Ignition.stopAll(true);
+
+        try (Ignite ignite = Ignition.start(config)) {
+            LOGGER.info("Running POJO strategy write tests");
+            IgniteCache<PersonId, Person> cache = ignite.getOrCreateCache("cache1");
+
+            LOGGER.info("Running single operation write tests");
+            cache.put(id, TestsHelper.generateRandomPerson(id.getPersonNumber()));
+            cache.put(id, person);
+            LOGGER.info("Single operation write tests passed");
+
+            LOGGER.info("Running bulk operation write tests");
+            cache.putAll(personMap);
+            LOGGER.info("Bulk operation write tests passed");
+        }
+
+        LOGGER.info("POJO strategy write tests passed");
+
+        Ignition.stopAll(true);
+
+        try (Ignite ignite = Ignition.start(config)) {
+            LOGGER.info("Running POJO strategy read tests");
+            IgniteCache<PersonId, Person> cache = ignite.getOrCreateCache("cache1");
+
+            Person actualPerson = cache.get(id);
+            if (!person.equals(actualPerson))
+                throw new RuntimeException("Person value was incorrectly deserialized from Cassandra");
+
+            LOGGER.info("Single operation read tests passed");
+
+            LOGGER.info("Running bulk operation read tests");
+
+            Map<PersonId, Person> actualPersonMap = cache.getAll(personMap.keySet());
+            if (!TestsHelper.checkPersonMapsEqual(actualPersonMap, personMap, true))
+                throw new RuntimeException("Person values batch was incorrectly deserialized from Cassandra");
+
+            LOGGER.info("Bulk operation read tests passed");
+
+            LOGGER.info("POJO strategy read tests passed");
+
+            LOGGER.info("Running POJO strategy delete tests");
+
+            cache.remove(id);
+            cache.removeAll(personMap.keySet());
+
+            LOGGER.info("POJO strategy delete tests passed");
+        }
+    }
+
+    private IgniteConfiguration igniteConfig() throws IOException {
+        URL url = getClass().getClassLoader().getResource("org/apache/ignite/tests/persistence/pojo/persistence-settings-3.xml");
+        String persistence = U.readFileToString(url.getFile(), "UTF-8");
+
+        KeyValuePersistenceSettings persistenceSettings = new KeyValuePersistenceSettings(persistence);
+
+        DataSource dataSource = new DataSource();
+        dataSource.setContactPoints(CassandraHelper.getContactPointsArray());
+        dataSource.setCredentials(new CassandraAdminCredentials());
+        dataSource.setLoadBalancingPolicy(new RoundRobinPolicy());
+
+        CassandraCacheStoreFactory<String, Person> storeFactory = new CassandraCacheStoreFactory<>();
+        storeFactory.setDataSource(dataSource);
+        storeFactory.setPersistenceSettings(persistenceSettings);
+
+        CacheConfiguration<String, Person> cacheConfiguration = new CacheConfiguration<>();
+        cacheConfiguration.setName("cache1");
+        cacheConfiguration.setReadThrough(true);
+        cacheConfiguration.setWriteThrough(true);
+        cacheConfiguration.setCacheStoreFactory(storeFactory);
+
+        IgniteConfiguration config = new IgniteConfiguration();
+        config.setCacheConfiguration(cacheConfiguration);
+
+        return config;
+    }
+
     /** */
     public static class PojoPerson {
         /** */