IGNITE-13519 Add thin client support to ignite-spring-data - Fixes #26.

Signed-off-by: Aleksey Plekhanov <plehanov.alex@gmail.com>
diff --git a/modules/spring-data-2.0-ext/pom.xml b/modules/spring-data-2.0-ext/pom.xml
index 00f7fa5..f096b93 100644
--- a/modules/spring-data-2.0-ext/pom.xml
+++ b/modules/spring-data-2.0-ext/pom.xml
@@ -149,6 +149,12 @@
             <version>${ignite.version}</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring-data-commons</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 
     <profiles>
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/Query.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/Query.java
index 7b44d19..2db7c25 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/Query.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/Query.java
@@ -21,6 +21,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
 
 /**
  * Annotation to provide a user defined query for a method.
@@ -36,7 +38,10 @@
     String value() default "";
 
     /**
-     * Whether annotated repository method must use TextQuery search.
+     * Whether annotated repository method must use TextQuery search. Note that text queries are not supported if
+     * {@link IgniteClient} is used for accessing the Ignite cluster, use {@link Ignite} node instance instead.
+     *
+     * @see RepositoryConfig#igniteInstance()
      */
     boolean textQuery() default false;
 
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/RepositoryConfig.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/RepositoryConfig.java
index 359b75b..a156d81 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/RepositoryConfig.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/RepositoryConfig.java
@@ -23,6 +23,8 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 
 /**
@@ -44,16 +46,14 @@
     String cacheName() default "";
 
     /**
-     * Ignite instance string. Default "igniteInstance".
-     *
-     * @return {@link Ignite} instance spring bean name
+     * Name of the Spring Bean that must provide {@link Ignite} or {@link IgniteClient} instance for accessing the
+     * Ignite cluster.
      */
     String igniteInstance() default "igniteInstance";
 
     /**
-     * Ignite cfg string. Default "igniteCfg".
-     *
-     * @return {@link IgniteConfiguration} spring bean name
+     * Name of the Spring Bean that must provide {@link IgniteConfiguration} or {@link ClientConfiguration} that is used
+     * for instantination of Ignite node or Ignite thin client respectively for accessing the Ignite cluster.
      */
     String igniteCfg() default "igniteCfg";
 
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/query/IgniteRepositoryQuery.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/query/IgniteRepositoryQuery.java
index 96ff57b..03b0bb4 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/query/IgniteRepositoryQuery.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/query/IgniteRepositoryQuery.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.springdata20.repository.query;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -32,27 +35,21 @@
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.cache.Cache;
 import org.apache.commons.lang.ArrayUtils;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.binary.BinaryObjectBuilder;
-import org.apache.ignite.binary.BinaryType;
+import org.apache.commons.lang.reflect.FieldUtils;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.cache.query.Query;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.SqlQuery;
 import org.apache.ignite.cache.query.TextQuery;
-import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.binary.BinaryUtils;
-import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
-import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
-import org.apache.ignite.internal.processors.cache.binary.IgniteBinaryImpl;
-import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
-import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
-import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteCacheClientProxy;
+import org.apache.ignite.internal.util.GridUnsafe;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.springdata20.repository.config.DynamicQueryConfig;
 import org.apache.ignite.springdata20.repository.query.StringQuery.ParameterBinding;
@@ -75,6 +72,8 @@
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.util.StringUtils;
 
+import static org.apache.ignite.internal.processors.query.QueryUtils.KEY_FIELD_NAME;
+import static org.apache.ignite.internal.processors.query.QueryUtils.VAL_FIELD_NAME;
 import static org.apache.ignite.springdata20.repository.support.IgniteRepositoryFactory.isFieldQuery;
 
 /**
@@ -160,9 +159,6 @@
  */
 @SuppressWarnings("unchecked")
 public class IgniteRepositoryQuery implements RepositoryQuery {
-    /** */
-    private static final TreeMap<String, Class<?>> binaryFieldClass = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
     /**
      * Defines the way how to process query result
      */
@@ -201,20 +197,14 @@
     /** */
     private final Class<?> type;
 
+    /** Domain entitiy field descriptors mapped to their names in lower case. */
+    private final Map<String, Field> domainEntitiyFields;
+
     /** */
     private final IgniteQuery staticQuery;
 
     /** */
-    private final IgniteCache cache;
-
-    /** */
-    private final Ignite ignite;
-
-    /** Required by qryStr field query type for binary manipulation */
-    private IgniteBinaryImpl igniteBinary;
-
-    /** */
-    private BinaryType igniteBinType;
+    private final IgniteCacheProxy<?, ?> cache;
 
     /** */
     private final Method mtd;
@@ -258,7 +248,6 @@
     /**
      * Instantiates a new Ignite repository query.
      *
-     * @param ignite                               the ignite
      * @param metadata                             Metadata.
      * @param staticQuery                          Query.
      * @param mtd                                  Method.
@@ -267,12 +256,12 @@
      * @param staticQueryConfiguration             the query configuration
      * @param queryMethodEvaluationContextProvider the query method evaluation context provider
      */
-    public IgniteRepositoryQuery(Ignite ignite,
+    public IgniteRepositoryQuery(
         RepositoryMetadata metadata,
         @Nullable IgniteQuery staticQuery,
         Method mtd,
         ProjectionFactory factory,
-        IgniteCache cache,
+        IgniteCacheProxy<? ,?> cache,
         @Nullable DynamicQueryConfig staticQueryConfiguration,
         EvaluationContextProvider queryMethodEvaluationContextProvider) {
         this.metadata = metadata;
@@ -280,8 +269,10 @@
         this.factory = factory;
         type = metadata.getDomainType();
 
+        domainEntitiyFields = Arrays.stream(type.getDeclaredFields())
+            .collect(Collectors.toMap(field -> field.getName().toLowerCase(), field -> field));
+
         this.cache = cache;
-        this.ignite = ignite;
 
         this.staticQueryConfiguration = staticQueryConfiguration;
         this.staticQuery = staticQuery;
@@ -313,8 +304,6 @@
                 "When passing dynamicQuery = true via org.apache.ignite.springdata.repository.config.Query "
                     + "annotation, you must provide a non null method parameter of type DynamicQueryConfig");
         }
-        // ensure domain class is registered on marshaller to transform row to entity
-        registerClassOnMarshaller(((IgniteEx)ignite).context(), type);
     }
 
     /**
@@ -351,7 +340,20 @@
 
         Query iQry = prepareQuery(qry, config, returnStgy, parameters);
 
-        QueryCursor qryCursor = cache.query(iQry);
+        QueryCursor qryCursor;
+
+        try {
+            qryCursor = cache.query(iQry);
+        }
+        catch (IllegalArgumentException e) {
+            if (cache instanceof IgniteCacheClientProxy) {
+                throw new IllegalStateException(String.format("Query of type %s is not supported by thin client." +
+                    " Check %s#%s method configuration or use Ignite node instance to connect to the Ignite cluster.",
+                    iQry.getClass().getSimpleName(), mtd.getDeclaringClass().getName(), mtd.getName()), e);
+            }
+
+            throw e;
+        }
 
         return transformQueryCursor(qry, returnStgy, parameters, qryCursor);
     }
@@ -384,22 +386,6 @@
         return index;
     }
 
-    /** */
-    private synchronized IgniteBinaryImpl binary() {
-        if (igniteBinary == null)
-            igniteBinary = (IgniteBinaryImpl)ignite.binary();
-
-        return igniteBinary;
-    }
-
-    /** */
-    private synchronized BinaryType binType() {
-        if (igniteBinType == null)
-            igniteBinType = binary().type(type);
-
-        return igniteBinType;
-    }
-
     /**
      * @param mtd Method.
      * @param isFieldQry Is field query.
@@ -561,15 +547,12 @@
             // take control over single primite result from queries, i.e. DELETE, SELECT COUNT, UPDATE ...
             boolean singlePrimitiveResult = isPrimitiveOrWrapper(returnClass);
 
-            final List<GridQueryFieldMetadata> meta = ((QueryCursorEx)qryCursor).fieldsMeta();
+            FieldsQueryCursor<?> fieldQryCur = (FieldsQueryCursor<?>)qryCursor;
 
             Function<List<?>, ?> cWrapperTransformFunction = null;
 
-            if (type.equals(returnClass)) {
-                IgniteBinaryImpl binary = binary();
-                BinaryType binType = binType();
-                cWrapperTransformFunction = row -> rowToEntity(binary, binType, row, meta);
-            }
+            if (type.equals(returnClass))
+                cWrapperTransformFunction = row -> rowToEntity(row, fieldQryCur);
             else {
                 if (hasProjection || singlePrimitiveResult) {
                     if (singlePrimitiveResult)
@@ -577,11 +560,11 @@
                     else {
                         // Map row -> projection class
                         cWrapperTransformFunction = row -> factory
-                            .createProjection(returnClass, rowToMap(row, meta));
+                            .createProjection(returnClass, rowToMap(row, fieldQryCur));
                     }
                 }
                 else
-                    cWrapperTransformFunction = row -> rowToMap(row, meta);
+                    cWrapperTransformFunction = row -> rowToMap(row, fieldQryCur);
             }
 
             QueryCursorWrapper<?, ?> cWrapper = new QueryCursorWrapper<>((QueryCursor<List<?>>)qryCursor,
@@ -613,17 +596,15 @@
             }
         }
         else {
-            Iterable<CacheEntryImpl> qryIter = (Iterable<CacheEntryImpl>)qryCursor;
-
-            Function<CacheEntryImpl, ?> cWrapperTransformFunction;
+            Function<Cache.Entry<?, ?>, ?> cWrapperTransformFunction;
 
             if (hasProjection && !type.equals(returnClass))
                 cWrapperTransformFunction = row -> factory.createProjection(returnClass, row.getValue());
             else
-                cWrapperTransformFunction = row -> row.getValue();
+                cWrapperTransformFunction = Cache.Entry::getValue;
 
-            QueryCursorWrapper<?, ?> cWrapper = new QueryCursorWrapper<>((QueryCursor<CacheEntryImpl>)qryCursor,
-                cWrapperTransformFunction);
+            QueryCursorWrapper<Cache.Entry<?, ?>, ?> cWrapper = new QueryCursorWrapper<>(
+                (QueryCursor<Cache.Entry<?, ?>>)qryCursor, cWrapperTransformFunction);
 
             switch (returnStgy) {
                 case PAGE_OF_VALUES:
@@ -641,7 +622,7 @@
                     }
                     return null;
                 case CACHE_ENTRY:
-                    Iterator<?> iter2 = qryIter.iterator();
+                    Iterator<?> iter2 = qryCursor.iterator();
                     if (iter2.hasNext()) {
                         Object resp2 = iter2.next();
                         U.closeQuiet(qryCursor);
@@ -838,98 +819,59 @@
         return query;
     }
 
-    /** */
-    private static Map<String, Object> rowToMap(final List<?> row, final List<GridQueryFieldMetadata> meta) {
+    /**
+     * @param row SQL result row.
+     * @param cursor SQL query result cursor through which {@param row} was obtained.
+     * @return SQL result row values mapped to corresponding column names.
+     */
+    private static Map<String, Object> rowToMap(final List<?> row, FieldsQueryCursor<?> cursor) {
         // use treemap with case insensitive property name
         final TreeMap<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        for (int i = 0; i < meta.size(); i++) {
+
+        for (int i = 0; i < row.size(); i++) {
             // don't want key or val columns
-            final String metaField = meta.get(i).fieldName().toLowerCase();
-            if (!metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME) && !metaField.equalsIgnoreCase(
-                QueryUtils.VAL_FIELD_NAME))
-                map.put(metaField, row.get(i));
+            final String fieldName = cursor.getFieldName(i).toLowerCase();
+
+            if (!KEY_FIELD_NAME.equalsIgnoreCase(fieldName) && !VAL_FIELD_NAME.equalsIgnoreCase(fieldName))
+                map.put(fieldName, row.get(i));
         }
+
         return map;
     }
 
     /**
      * convert row ( with list of field values) into domain entity
+     *
+     * @param row SQL query result row.
+     * @param cursor SQL query result cursor through which {@param row} was obtained.
+     * @return Entitiy instance.
      */
-    private <V> V rowToEntity(final IgniteBinaryImpl binary,
-        final BinaryType binType,
-        final List<?> row,
-        final List<GridQueryFieldMetadata> meta) {
-        // additional data returned by query not present on domain object type
-        final TreeMap<String, Object> metadata = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        final BinaryObjectBuilder bldr = binary.builder(binType.typeName());
+    private <V> V rowToEntity(List<?> row, FieldsQueryCursor<?> cursor) {
+        Constructor<?> ctor;
 
-        for (int i = 0; i < row.size(); i++) {
-            final GridQueryFieldMetadata fMeta = meta.get(i);
-            final String metaField = fMeta.fieldName();
-            // add existing entity fields to binary object
-            if (binType.field(fMeta.fieldName()) != null && !metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME)
-                && !metaField.equalsIgnoreCase(QueryUtils.VAL_FIELD_NAME)) {
-                final Object fieldValue = row.get(i);
-                if (fieldValue != null) {
-                    final Class<?> clazz = getClassForBinaryField(binary, binType, fMeta);
-                    // null values must not be set into binary objects
-                    bldr.setField(metaField, fixExpectedType(fieldValue, clazz));
-                }
-            }
-            else {
-                // don't want key or val column... but wants null values
-                if (!metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME) && !metaField.equalsIgnoreCase(
-                    QueryUtils.VAL_FIELD_NAME))
-                    metadata.put(metaField, row.get(i));
-            }
-        }
-        return bldr.build().deserialize();
-    }
-
-    /**
-     * Obtains real field class from resultset metadata field whether it's available
-     */
-    private Class<?> getClassForBinaryField(final IgniteBinaryImpl binary,
-        final BinaryType binType,
-        final GridQueryFieldMetadata fieldMeta) {
         try {
-            final String fieldId = fieldMeta.schemaName() + "." + fieldMeta.typeName() + "." + fieldMeta.fieldName();
+            ctor = type.getDeclaredConstructor();
 
-            if (binaryFieldClass.containsKey(fieldId))
-                return binaryFieldClass.get(fieldId);
+            ctor.setAccessible(true);
+        }
+        catch (NoSuchMethodException | SecurityException ignored) {
+            ctor = null;
+        }
 
-            Class<?> clazz = null;
+        try {
+            Object res = ctor == null ? GridUnsafe.allocateInstance(type) : ctor.newInstance();
 
-            synchronized (binaryFieldClass) {
+            for (int i = 0; i < row.size(); i++) {
+                Field entityField = domainEntitiyFields.get(cursor.getFieldName(i).toLowerCase());
 
-                if (binaryFieldClass.containsKey(fieldId))
-                    return binaryFieldClass.get(fieldId);
-
-                String fieldName = null;
-
-                // search field name on binary type (query returns case insensitive
-                // field name) but BinaryType is not case insensitive
-                for (final String fname : binType.fieldNames()) {
-                    if (fname.equalsIgnoreCase(fieldMeta.fieldName())) {
-                        fieldName = fname;
-                        break;
-                    }
-                }
-
-                final CacheObjectBinaryProcessorImpl proc = (CacheObjectBinaryProcessorImpl)binary.processor();
-
-                // search for class by typeId, if not found use
-                // fieldMeta.fieldTypeName() class
-                clazz = BinaryUtils.resolveClass(proc.binaryContext(), binary.typeId(binType.fieldTypeName(fieldName)),
-                    fieldMeta.fieldTypeName(), ignite.configuration().getClassLoader(), true);
-
-                binaryFieldClass.put(fieldId, clazz);
+                if (entityField != null)
+                    FieldUtils.writeField(entityField, res, fixExpectedType(row.get(i), entityField.getType()), true);
             }
 
-            return clazz;
+            return (V)res;
         }
-        catch (final Exception e) {
-            return null;
+        catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+            throw new IgniteException("Unable to allocate instance of domain entity class " + type.getName(), e);
         }
     }
 
@@ -954,21 +896,6 @@
     }
 
     /**
-     * @param ctx   Context.
-     * @param clazz Clazz.
-     */
-    private static void registerClassOnMarshaller(final GridKernalContext ctx, final Class<?> clazz) {
-        try {
-            // ensure class registration for marshaller on cluster...
-            if (!U.isJdk(clazz))
-                U.marshal(ctx, clazz.newInstance());
-        }
-        catch (final Exception ignored) {
-            // silent
-        }
-    }
-
-    /**
      * Ignite QueryCursor wrapper.
      * <p>
      * Ensures closing underline cursor when there is no data.
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java
index 87f637d..dacb3e4 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java
@@ -20,10 +20,15 @@
 import java.util.Map;
 import java.util.Optional;
 import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteClientProxy;
 import org.apache.ignite.springdata20.repository.IgniteRepository;
 import org.apache.ignite.springdata20.repository.config.DynamicQueryConfig;
 import org.apache.ignite.springdata20.repository.config.Query;
@@ -73,7 +78,7 @@
     private final Map<Class<?>, String> repoToCache = new HashMap<>();
 
     /** Mapping of a repository to a ignite instance. */
-    private final Map<Class<?>, Ignite> repoToIgnite = new HashMap<>();
+    private final Map<Class<?>, IgniteProxy> repoToIgnite = new HashMap<>();
 
     /**
      * Creates the factory with initialized {@link Ignite} instance.
@@ -89,35 +94,51 @@
     }
 
     /** */
-    private Ignite igniteForRepoConfig(RepositoryConfig config) {
+    private IgniteProxy igniteForRepoConfig(RepositoryConfig config) {
         try {
-            String igniteInstanceName = evaluateExpression(config.igniteInstance());
-            return (Ignite)ctx.getBean(igniteInstanceName);
+            Object igniteInstanceBean = ctx.getBean(evaluateExpression(config.igniteInstance()));
+
+            if (igniteInstanceBean instanceof Ignite)
+                return new IgniteProxyImpl((Ignite)igniteInstanceBean);
+            else if (igniteInstanceBean instanceof IgniteClient)
+                return new IgniteClientProxy((IgniteClient)igniteInstanceBean);
+
+            throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" +
+                " \"igniteInstance\" property of repository configuration must be one of the following types: " +
+                Ignite.class.getName() + ", " + IgniteClient.class.getName());
         }
         catch (BeansException ex) {
             try {
-                String igniteConfigName = evaluateExpression(config.igniteCfg());
-                IgniteConfiguration cfg = (IgniteConfiguration)ctx.getBean(igniteConfigName);
-                try {
-                    // first try to attach to existing ignite instance
-                    return Ignition.ignite(cfg.getIgniteInstanceName());
+                Object igniteCfgBean = ctx.getBean(evaluateExpression(config.igniteCfg()));
+
+                if (igniteCfgBean instanceof IgniteConfiguration) {
+                    try {
+                        // first try to attach to existing ignite instance
+                        return new IgniteProxyImpl(Ignition.ignite(((IgniteConfiguration)igniteCfgBean).getIgniteInstanceName()));
+                    }
+                    catch (Exception ignored) {
+                        // nop
+                    }
+                    return new IgniteProxyImpl(Ignition.start((IgniteConfiguration)igniteCfgBean));
                 }
-                catch (Exception ignored) {
-                    // nop
-                }
-                return Ignition.start(cfg);
+                else if (igniteCfgBean instanceof ClientConfiguration)
+                    return new IgniteClientProxy(Ignition.startClient((ClientConfiguration)igniteCfgBean));
+
+                throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" +
+                    " the \"igniteCfg\" property of repository configuration must be one of the following types: [" +
+                    IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']');
+
             }
             catch (BeansException ex2) {
                 try {
                     String igniteSpringCfgPath = evaluateExpression(config.igniteSpringCfgPath());
                     String path = (String)ctx.getBean(igniteSpringCfgPath);
-                    return Ignition.start(path);
+                    return new IgniteProxyImpl(Ignition.start(path));
                 }
                 catch (BeansException ex3) {
-                    throw new IgniteException("Failed to initialize Ignite repository factory. Ignite instance or"
-                        + " IgniteConfiguration or a path to Ignite's spring XML "
-                        + "configuration must be defined in the"
-                        + " application configuration");
+                    throw new IgniteException("Failed to initialize Ignite repository factory. No beans required for" +
+                        " repository configuration were found. Check \"igniteInstance\", \"igniteCfg\"," +
+                        " \"igniteSpringCfgPath\" parameters of " + RepositoryConfig.class.getName() + "class.");
                 }
             }
         }
@@ -176,14 +197,14 @@
     }
 
     /** Control underlying cache creation to avoid cache creation by mistake */
-    private IgniteCache getRepositoryCache(Class<?> repoIf) {
-        Ignite ignite = repoToIgnite.get(repoIf);
+    private IgniteCacheProxy<?, ?> getRepositoryCache(Class<?> repoIf) {
+        IgniteProxy ignite = repoToIgnite.get(repoIf);
 
         RepositoryConfig config = repoIf.getAnnotation(RepositoryConfig.class);
 
         String cacheName = repoToCache.get(repoIf);
 
-        IgniteCache c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName);
+        IgniteCacheProxy<?, ?> c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName);
 
         if (c == null) {
             throw new IllegalStateException(
@@ -198,7 +219,7 @@
 
     /** {@inheritDoc} */
     @Override protected Object getTargetRepository(RepositoryInformation metadata) {
-        Ignite ignite = repoToIgnite.get(metadata.getRepositoryInterface());
+        IgniteProxy ignite = repoToIgnite.get(metadata.getRepositoryInterface());
 
         return getTargetRepositoryViaReflection(metadata, ignite,
             getRepositoryCache(metadata.getRepositoryInterface()));
@@ -209,8 +230,6 @@
         EvaluationContextProvider evaluationContextProvider) {
         return Optional.of((mtd, metadata, factory, namedQueries) -> {
             final Query annotation = mtd.getAnnotation(Query.class);
-            final Ignite ignite = repoToIgnite.get(metadata.getRepositoryInterface());
-
             if (annotation != null && (StringUtils.hasText(annotation.value()) || annotation.textQuery() || annotation
                 .dynamicQuery())) {
 
@@ -224,7 +243,7 @@
                     annotation.textQuery(), false, IgniteQueryGenerator.getOptions(mtd)) : null;
 
                 if (key != QueryLookupStrategy.Key.CREATE) {
-                    return new IgniteRepositoryQuery(ignite, metadata, query, mtd, factory,
+                    return new IgniteRepositoryQuery(metadata, query, mtd, factory,
                         getRepositoryCache(metadata.getRepositoryInterface()),
                         annotatedIgniteQuery ? DynamicQueryConfig.fromQueryAnnotation(annotation) : null,
                         evaluationContextProvider);
@@ -237,7 +256,7 @@
                     + ".config.Query annotation.");
             }
 
-            return new IgniteRepositoryQuery(ignite, metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd,
+            return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd,
                 factory, getRepositoryCache(metadata.getRepositoryInterface()),
                 DynamicQueryConfig.fromQueryAnnotation(annotation), evaluationContextProvider);
         });
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java
index 50a7d4c..b089c22 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java
@@ -29,7 +29,12 @@
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
 import org.apache.ignite.springdata20.repository.IgniteRepository;
+import org.apache.ignite.springdata20.repository.config.RepositoryConfig;
 import org.jetbrains.annotations.Nullable;
 import org.springframework.context.annotation.Conditional;
 
@@ -44,15 +49,19 @@
  */
 @Conditional(ConditionFalse.class)
 public class IgniteRepositoryImpl<V, K extends Serializable> implements IgniteRepository<V, K> {
+    /** Error message indicating that operation is spported only if {@link Ignite} instance is used to access the cluster. */
+    private static final String UNSUPPORTED_ERR_MSG = "Current operation is supported only if Ignite node instance is" +
+        " used to access the Ignite cluster. See " + RepositoryConfig.class.getName() + "#igniteInstance.";
+
     /**
      * Ignite Cache bound to the repository
      */
-    private final IgniteCache<K, V> cache;
+    private final IgniteCacheProxy<K, V> cache;
 
     /**
      * Ignite instance bound to the repository
      */
-    private final Ignite ignite;
+    private final IgniteProxy ignite;
 
     /**
      * Repository constructor.
@@ -60,19 +69,25 @@
      * @param ignite the ignite
      * @param cache  Initialized cache instance.
      */
-    public IgniteRepositoryImpl(Ignite ignite, IgniteCache<K, V> cache) {
+    public IgniteRepositoryImpl(IgniteProxy ignite, IgniteCacheProxy<K, V> cache) {
         this.cache = cache;
         this.ignite = ignite;
     }
 
     /** {@inheritDoc} */
     @Override public IgniteCache<K, V> cache() {
-        return cache;
+        if (cache instanceof IgniteCacheProxyImpl)
+            return ((IgniteCacheProxyImpl<K, V>)cache).delegate();
+
+        throw new UnsupportedOperationException(UNSUPPORTED_ERR_MSG);
     }
 
     /** {@inheritDoc} */
     @Override public Ignite ignite() {
-        return ignite;
+        if (ignite instanceof IgniteProxyImpl)
+            return ((IgniteProxyImpl)ignite).delegate();
+
+        throw new UnsupportedOperationException(UNSUPPORTED_ERR_MSG);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
new file mode 100644
index 0000000..6dcadb8
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.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.springdata;
+
+import org.apache.ignite.springdata.compoundkey.CityRepository;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data operation with compound key when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCompoundKeyTest extends IgniteSpringDataCompoundKeyTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(CityRepository.class);
+    }
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
new file mode 100644
index 0000000..ae42434
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.springdata;
+
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.IgniteClientConfigRepository;
+import org.apache.ignite.springdata20.repository.support.IgniteRepositoryFactory;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelfTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(PersonRepository.class);
+    }
+
+    /** Text queries are not supported when {@link IgniteClient} is used for acessing the Ignite cluster. */
+    @Override public void testUpdateQueryMixedCaseProjectionIndexedParameterLuceneTextQuery() {
+        GridTestUtils.assertThrows(log,
+            () -> repo.textQueryByFirstNameWithProjectionNamedParameter("person"), IllegalStateException.class,
+            "Query of type TextQuery is not supported by thin client. Check" +
+                " org.apache.ignite.springdata.misc.PersonRepository#textQueryByFirstNameWithProjectionNamedParameter" +
+                " method configuration or use Ignite node instance to connect to the Ignite cluster.");
+    }
+
+    /**
+     * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster.
+     */
+    @Test
+    public void testRepositoryWithClientConfiguration() {
+        IgniteRepositoryFactory factory = new IgniteRepositoryFactory(ctx);
+
+        IgniteClientConfigRepository repo = factory.getRepository(IgniteClientConfigRepository.class);
+
+        assertTrue(repo.count() > 0);
+    }
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
new file mode 100644
index 0000000..27d3645
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.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.springdata;
+
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.Person;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.PersonRepositoryOtherIgniteInstance;
+import org.apache.ignite.springdata.misc.PersonSecondRepository;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data query operations when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataQueriesSelfTest extends IgniteSpringDataQueriesSelfTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(PersonRepository.class);
+        repo2 = ctx.getBean(PersonSecondRepository.class);
+        repoTWO = ctx.getBean(PersonRepositoryOtherIgniteInstance.class);
+
+        for (int i = 0; i < CACHE_SIZE; i++) {
+            repo.save(i, new Person("person" + Integer.toHexString(i),
+                "lastName" + Integer.toHexString((i + 16) % 256)));
+            repoTWO.save(i, new Person("TWOperson" + Integer.toHexString(i),
+                "lastName" + Integer.toHexString((i + 16) % 256)));
+        }
+    }
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
index 6a69f32..06e0c28 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
@@ -36,10 +36,10 @@
  * */
 public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest {
     /** Application context */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /** City repository */
-    private static CityRepository repo;
+    protected static CityRepository repo;
 
     /** Cache name */
     private static final String CACHE_NAME = "City";
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
index 38785ef..a543044 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
@@ -37,10 +37,10 @@
  */
 public class IgniteSpringDataCrudSelfTest extends GridCommonAbstractTest {
     /** Repository. */
-    private static PersonRepository repo;
+    protected static PersonRepository repo;
 
     /** Context. */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /** Number of entries to store */
     private static int CACHE_SIZE = 1000;
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
index 9c16f6d..0445e0d 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
@@ -20,6 +20,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import javax.cache.Cache;
 import org.apache.ignite.springdata.misc.ApplicationConfiguration;
 import org.apache.ignite.springdata.misc.Person;
@@ -39,23 +40,23 @@
  */
 public class IgniteSpringDataQueriesSelfTest extends GridCommonAbstractTest {
     /** Repository. */
-    private static PersonRepository repo;
+    protected static PersonRepository repo;
 
     /** Repository 2. */
-    private static PersonSecondRepository repo2;
+    protected static PersonSecondRepository repo2;
 
     /**
      * Repository Ignite Instance cluster TWO.
      */
-    private static PersonRepositoryOtherIgniteInstance repoTWO;
+    protected static PersonRepositoryOtherIgniteInstance repoTWO;
 
     /** Context. */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /**
      * Number of entries to store
      */
-    private static int CACHE_SIZE = 1000;
+    protected static int CACHE_SIZE = 1000;
 
     /**
      * Performs context initialization before tests.
@@ -406,4 +407,35 @@
             assertTrue(list.get(0) instanceof Integer);
         }
     }
+
+    /** Tests conversion of SQL select query result to domain entity objects. */
+    @Test
+    public void testRowToEntityConversion() {
+        Set<Person> res = new HashSet<>(repo.queryWithRowToEntityConversion());
+
+        Set<Person> exp = new HashSet<>();
+
+        repo.findAll().forEach(exp::add);
+
+        assertEquals(exp, res);
+    }
+
+    /**
+     * Tests conversion of SQL select query result to domain entity objects if result rows don't contain all fields
+     * of domain entity class.
+     */
+    @Test
+    public void testIncompleteRowToEntityConversion() {
+        Set<Person> res = new HashSet<>(repo.queryWithIncompleteRowToEntityConversion());
+
+        Set<Person> exp = new HashSet<>();
+
+        repo.findAll().forEach(p -> {
+            p.setSecondName(null);
+
+            exp.add(p);
+        });
+
+        assertEquals(exp, res);
+    }
 }
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
new file mode 100644
index 0000000..2122231
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
@@ -0,0 +1,105 @@
+/*
+ * 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.springdata.misc;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.springdata.misc.SampleEvaluationContextExtension.SamplePassParamExtension;
+import org.apache.ignite.springdata20.repository.config.EnableIgniteRepositories;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.repository.query.spi.EvaluationContextExtension;
+
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+import static org.apache.ignite.springdata.misc.ApplicationConfiguration.IGNITE_INSTANCE_ONE;
+import static org.apache.ignite.springdata.misc.ApplicationConfiguration.IGNITE_INSTANCE_TWO;
+
+/** Spring Application configuration for repository testing in case thin client is used for accessing the cluster. */
+@Configuration
+@EnableIgniteRepositories({"org.apache.ignite.springdata.compoundkey", "org.apache.ignite.springdata.misc"})
+public class IgniteClientApplicationConfiguration {
+    /** Test cache name. */
+    public static final String CACHE_NAME = "PersonCache";
+
+    /** */
+    @Bean
+    public CacheNamesBean cacheNames() {
+        CacheNamesBean bean = new CacheNamesBean();
+
+        bean.setPersonCacheName(CACHE_NAME);
+
+        return bean;
+    }
+
+    /** */
+    @Bean
+    public EvaluationContextExtension sampleSpELExtension() {
+        return new SampleEvaluationContextExtension();
+    }
+
+    /** */
+    @Bean(value = "sampleExtensionBean")
+    public SamplePassParamExtension sampleExtensionBean() {
+        return new SamplePassParamExtension();
+    }
+
+    /** */
+    @Bean
+    public Ignite igniteServerNode() {
+        return Ignition.start(igniteConfiguration(IGNITE_INSTANCE_ONE, CLI_CONN_PORT));
+    }
+
+    /** Ignite client instance bean with default name. */
+    @Bean
+    public IgniteClient igniteInstance() {
+        return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT)
+            .setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false)));
+    }
+
+    /** Ignite client instance bean with non-default name. */
+    @Bean
+    public IgniteClient igniteInstanceTWO() {
+        Ignition.start(igniteConfiguration(IGNITE_INSTANCE_TWO, 10801));
+
+        return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:10801"));
+    }
+
+    /** Ignite client configuraition bean. */
+    @Bean
+    public ClientConfiguration clientConfiguration() {
+        return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT);
+    }
+
+    /** Ingite configuration for server node. */
+    private static IgniteConfiguration igniteConfiguration(String igniteInstanceName, int cliConnPort) {
+        return new IgniteConfiguration()
+            .setIgniteInstanceName(igniteInstanceName)
+            .setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(new TcpDiscoveryVmIpFinder(true)))
+            .setCacheConfiguration(new CacheConfiguration<>(CACHE_NAME).setIndexedTypes(Integer.class, Person.class))
+            .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(cliConnPort))
+            .setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false));
+    }
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java
new file mode 100644
index 0000000..ccbfa08
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java
@@ -0,0 +1,29 @@
+/*
+ * 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.springdata.misc;
+
+import java.io.Serializable;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata20.repository.IgniteRepository;
+import org.apache.ignite.springdata20.repository.config.RepositoryConfig;
+
+/** Repository for testing repository configurion approach through {@link ClientConfiguration}. */
+@RepositoryConfig(cacheName = "PersonCache", igniteCfg = "clientConfiguration")
+public interface IgniteClientConfigRepository extends IgniteRepository<Object, Serializable> {
+    // No-op.
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
index 531c67f..62c3054 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.springdata.misc;
 
+import java.util.Date;
 import java.util.Objects;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
 import org.apache.ignite.cache.query.annotations.QueryTextField;
@@ -34,6 +35,10 @@
     @QuerySqlField(index = true)
     private String secondName;
 
+    /** Birthday. */
+    @QuerySqlField
+    private Date birthday;
+
     /**
      * @param firstName First name.
      * @param secondName Second name.
@@ -41,6 +46,7 @@
     public Person(String firstName, String secondName) {
         this.firstName = firstName;
         this.secondName = secondName;
+        birthday = new Date();
     }
 
     /**
@@ -76,6 +82,7 @@
         return "Person{" +
             "firstName='" + firstName + '\'' +
             ", secondName='" + secondName + '\'' +
+            ", birthday='" + birthday + '\'' +
             '}';
     }
 
@@ -90,11 +97,12 @@
         Person person = (Person)o;
 
         return Objects.equals(firstName, person.firstName) &&
-            Objects.equals(secondName, person.secondName);
+            Objects.equals(secondName, person.secondName) &&
+            Objects.equals(birthday, person.birthday);
     }
 
     /** {@inheritDoc} */
     @Override public int hashCode() {
-        return Objects.hash(firstName, secondName);
+        return Objects.hash(firstName, secondName, birthday);
     }
 }
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
index c50a2d0..7ea699b 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
@@ -145,4 +145,12 @@
     /** Update using @Query but with errors on the query */
     @Query("UPDATE Person SET secondName = ? WHERE firstName = ? AND ERRORS = 'ERRORS'")
     public int setWrongFixedSecondName(String secondName, String firstName);
+
+    /** Produces a list of domain entity classes whose fields are obtained from the query result row. */
+    @Query(value = "SELECT firstName, secondName, birthday, _key, _val, NULL as one FROM Person", forceFieldsQuery = true)
+    public List<Person> queryWithRowToEntityConversion();
+
+    /** Produces a list of domain entity classes whose fields are obtained from the query result row. */
+    @Query(value = "SELECT firstName, birthday FROM Person", forceFieldsQuery = true)
+    public List<Person> queryWithIncompleteRowToEntityConversion();
 }
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java
index 4efc48b..6c26078 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.testsuites;
 
+import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest;
 import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest;
 import org.apache.ignite.springdata.IgniteSpringDataCrudSelfExpressionTest;
 import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest;
@@ -32,7 +35,10 @@
     IgniteSpringDataCrudSelfTest.class,
     IgniteSpringDataQueriesSelfTest.class,
     IgniteSpringDataCrudSelfExpressionTest.class,
-    IgniteSpringDataCompoundKeyTest.class
+    IgniteSpringDataCompoundKeyTest.class,
+    IgniteClientSpringDataCrudSelfTest.class,
+    IgniteClientSpringDataQueriesSelfTest.class,
+    IgniteClientSpringDataCompoundKeyTest.class
 })
 public class IgniteSpringData2TestSuite {
 }
diff --git a/modules/spring-data-2.2-ext/pom.xml b/modules/spring-data-2.2-ext/pom.xml
index b0c00ba..6ac1b21 100644
--- a/modules/spring-data-2.2-ext/pom.xml
+++ b/modules/spring-data-2.2-ext/pom.xml
@@ -149,6 +149,12 @@
             <version>${ignite.version}</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring-data-commons</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/Query.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/Query.java
index d1af467..1ea4bf3 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/Query.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/Query.java
@@ -21,6 +21,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
 
 /**
  * Annotation to provide a user defined query for a method.
@@ -36,7 +38,10 @@
     String value() default "";
 
     /**
-     * Whether annotated repository method must use TextQuery search.
+     * Whether annotated repository method must use TextQuery search. Note that text queries are not supported if
+     * {@link IgniteClient} is used for accessing the Ignite cluster, use {@link Ignite} node instance instead.
+     *
+     * @see RepositoryConfig#igniteInstance()
      */
     boolean textQuery() default false;
 
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/RepositoryConfig.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/RepositoryConfig.java
index 6699ed6..e890052 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/RepositoryConfig.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/RepositoryConfig.java
@@ -25,6 +25,8 @@
 import java.lang.annotation.Target;
 
 import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 
 /**
@@ -46,16 +48,14 @@
     String cacheName() default "";
 
     /**
-     * Ignite instance string. Default "igniteInstance".
-     *
-     * @return {@link Ignite} instance spring bean name
+     * Name of the Spring Bean that must provide {@link Ignite} or {@link IgniteClient} instance for accessing the
+     * Ignite cluster.
      */
     String igniteInstance() default "igniteInstance";
 
     /**
-     * Ignite cfg string. Default "igniteCfg".
-     *
-     * @return {@link IgniteConfiguration} spring bean name
+     * Name of the Spring Bean that must provide {@link IgniteConfiguration} or {@link ClientConfiguration} that is used
+     * for instantination of Ignite node or Ignite thin client respectively for accessing the Ignite cluster.
      */
     String igniteCfg() default "igniteCfg";
 
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/query/IgniteRepositoryQuery.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/query/IgniteRepositoryQuery.java
index 9ab9606..56e5c4c 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/query/IgniteRepositoryQuery.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/query/IgniteRepositoryQuery.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.springdata22.repository.query;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -32,27 +35,21 @@
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.cache.Cache;
 import org.apache.commons.lang.ArrayUtils;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.binary.BinaryObjectBuilder;
-import org.apache.ignite.binary.BinaryType;
+import org.apache.commons.lang.reflect.FieldUtils;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.cache.query.Query;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.SqlQuery;
 import org.apache.ignite.cache.query.TextQuery;
-import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.binary.BinaryUtils;
-import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
-import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
-import org.apache.ignite.internal.processors.cache.binary.IgniteBinaryImpl;
-import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
-import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
-import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteCacheClientProxy;
+import org.apache.ignite.internal.util.GridUnsafe;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.springdata22.repository.config.DynamicQueryConfig;
 import org.apache.ignite.springdata22.repository.query.StringQuery.ParameterBinding;
@@ -75,6 +72,8 @@
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.util.StringUtils;
 
+import static org.apache.ignite.internal.processors.query.QueryUtils.KEY_FIELD_NAME;
+import static org.apache.ignite.internal.processors.query.QueryUtils.VAL_FIELD_NAME;
 import static org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactory.isFieldQuery;
 
 /**
@@ -160,9 +159,6 @@
  */
 @SuppressWarnings("unchecked")
 public class IgniteRepositoryQuery implements RepositoryQuery {
-    /** */
-    private static final TreeMap<String, Class<?>> binaryFieldClass = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
     /**
      * Defines the way how to process query result
      */
@@ -201,20 +197,14 @@
     /** */
     private final Class<?> type;
 
+    /** Domain entitiy field descriptors mapped to their names in lower case. */
+    private final Map<String, Field> domainEntitiyFields;
+
     /** */
     private final IgniteQuery staticQuery;
 
     /** */
-    private final IgniteCache cache;
-
-    /** */
-    private final Ignite ignite;
-
-    /** Required by qryStr field query type for binary manipulation */
-    private IgniteBinaryImpl igniteBinary;
-
-    /** */
-    private BinaryType igniteBinType;
+    private final IgniteCacheProxy<?, ?> cache;
 
     /** */
     private final Method mtd;
@@ -258,7 +248,6 @@
     /**
      * Instantiates a new Ignite repository query.
      *
-     * @param ignite                               the ignite
      * @param metadata                             Metadata.
      * @param staticQuery                          Query.
      * @param mtd                                  Method.
@@ -267,12 +256,12 @@
      * @param staticQueryConfiguration             the query configuration
      * @param queryMethodEvaluationContextProvider the query method evaluation context provider
      */
-    public IgniteRepositoryQuery(Ignite ignite,
+    public IgniteRepositoryQuery(
         RepositoryMetadata metadata,
         @Nullable IgniteQuery staticQuery,
         Method mtd,
         ProjectionFactory factory,
-        IgniteCache cache,
+        IgniteCacheProxy<? ,?> cache,
         @Nullable DynamicQueryConfig staticQueryConfiguration,
         QueryMethodEvaluationContextProvider queryMethodEvaluationContextProvider) {
         this.metadata = metadata;
@@ -280,8 +269,10 @@
         this.factory = factory;
         type = metadata.getDomainType();
 
+        domainEntitiyFields = Arrays.stream(type.getDeclaredFields())
+            .collect(Collectors.toMap(field -> field.getName().toLowerCase(), field -> field));
+
         this.cache = cache;
-        this.ignite = ignite;
 
         this.staticQueryConfiguration = staticQueryConfiguration;
         this.staticQuery = staticQuery;
@@ -313,8 +304,6 @@
                 "When passing dynamicQuery = true via org.apache.ignite.springdata.repository.config.Query "
                     + "annotation, you must provide a non null method parameter of type DynamicQueryConfig");
         }
-        // ensure domain class is registered on marshaller to transform row to entity
-        registerClassOnMarshaller(((IgniteEx)ignite).context(), type);
     }
 
     /**
@@ -351,7 +340,20 @@
 
         Query iQry = prepareQuery(qry, config, returnStgy, parameters);
 
-        QueryCursor qryCursor = cache.query(iQry);
+        QueryCursor qryCursor;
+
+        try {
+            qryCursor = cache.query(iQry);
+        }
+        catch (IllegalArgumentException e) {
+            if (cache instanceof IgniteCacheClientProxy) {
+                throw new IllegalStateException(String.format("Query of type %s is not supported by thin client." +
+                    " Check %s#%s method configuration or use Ignite node instance to connect to the Ignite cluster.",
+                    iQry.getClass().getSimpleName(), mtd.getDeclaringClass().getName(), mtd.getName()), e);
+            }
+
+            throw e;
+        }
 
         return transformQueryCursor(qry, returnStgy, parameters, qryCursor);
     }
@@ -384,22 +386,6 @@
         return index;
     }
 
-    /** */
-    private synchronized IgniteBinaryImpl binary() {
-        if (igniteBinary == null)
-            igniteBinary = (IgniteBinaryImpl)ignite.binary();
-
-        return igniteBinary;
-    }
-
-    /** */
-    private synchronized BinaryType binType() {
-        if (igniteBinType == null)
-            igniteBinType = binary().type(type);
-
-        return igniteBinType;
-    }
-
     /**
      * @param mtd Method.
      * @param isFieldQry Is field query.
@@ -561,15 +547,12 @@
             // take control over single primite result from queries, i.e. DELETE, SELECT COUNT, UPDATE ...
             boolean singlePrimitiveResult = isPrimitiveOrWrapper(returnClass);
 
-            final List<GridQueryFieldMetadata> meta = ((QueryCursorEx)qryCursor).fieldsMeta();
+            FieldsQueryCursor<?> fieldQryCur = (FieldsQueryCursor<?>)qryCursor;
 
             Function<List<?>, ?> cWrapperTransformFunction = null;
 
-            if (type.equals(returnClass)) {
-                IgniteBinaryImpl binary = binary();
-                BinaryType binType = binType();
-                cWrapperTransformFunction = row -> rowToEntity(binary, binType, row, meta);
-            }
+            if (type.equals(returnClass))
+                cWrapperTransformFunction = row -> rowToEntity(row, fieldQryCur);
             else {
                 if (hasProjection || singlePrimitiveResult) {
                     if (singlePrimitiveResult)
@@ -577,11 +560,11 @@
                     else {
                         // Map row -> projection class
                         cWrapperTransformFunction = row -> factory
-                            .createProjection(returnClass, rowToMap(row, meta));
+                            .createProjection(returnClass, rowToMap(row, fieldQryCur));
                     }
                 }
                 else
-                    cWrapperTransformFunction = row -> rowToMap(row, meta);
+                    cWrapperTransformFunction = row -> rowToMap(row, fieldQryCur);
             }
 
             QueryCursorWrapper<?, ?> cWrapper = new QueryCursorWrapper<>((QueryCursor<List<?>>)qryCursor,
@@ -613,17 +596,15 @@
             }
         }
         else {
-            Iterable<CacheEntryImpl> qryIter = (Iterable<CacheEntryImpl>)qryCursor;
-
-            Function<CacheEntryImpl, ?> cWrapperTransformFunction;
+            Function<Cache.Entry<?, ?>, ?> cWrapperTransformFunction;
 
             if (hasProjection && !type.equals(returnClass))
                 cWrapperTransformFunction = row -> factory.createProjection(returnClass, row.getValue());
             else
-                cWrapperTransformFunction = row -> row.getValue();
+                cWrapperTransformFunction = Cache.Entry::getValue;
 
-            QueryCursorWrapper<?, ?> cWrapper = new QueryCursorWrapper<>((QueryCursor<CacheEntryImpl>)qryCursor,
-                cWrapperTransformFunction);
+            QueryCursorWrapper<Cache.Entry<?, ?>, ?> cWrapper = new QueryCursorWrapper<>(
+                (QueryCursor<Cache.Entry<?, ?>>)qryCursor, cWrapperTransformFunction);
 
             switch (returnStgy) {
                 case PAGE_OF_VALUES:
@@ -641,7 +622,7 @@
                     }
                     return null;
                 case CACHE_ENTRY:
-                    Iterator<?> iter2 = qryIter.iterator();
+                    Iterator<?> iter2 = qryCursor.iterator();
                     if (iter2.hasNext()) {
                         Object resp2 = iter2.next();
                         U.closeQuiet(qryCursor);
@@ -838,98 +819,59 @@
         return query;
     }
 
-    /** */
-    private static Map<String, Object> rowToMap(final List<?> row, final List<GridQueryFieldMetadata> meta) {
+    /**
+     * @param row SQL result row.
+     * @param cursor SQL query result cursor through which {@param row} was obtained.
+     * @return SQL result row values mapped to corresponding column names.
+     */
+    private static Map<String, Object> rowToMap(final List<?> row, FieldsQueryCursor<?> cursor) {
         // use treemap with case insensitive property name
         final TreeMap<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        for (int i = 0; i < meta.size(); i++) {
+
+        for (int i = 0; i < row.size(); i++) {
             // don't want key or val columns
-            final String metaField = meta.get(i).fieldName().toLowerCase();
-            if (!metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME) && !metaField.equalsIgnoreCase(
-                QueryUtils.VAL_FIELD_NAME))
-                map.put(metaField, row.get(i));
+            final String fieldName = cursor.getFieldName(i).toLowerCase();
+
+            if (!KEY_FIELD_NAME.equalsIgnoreCase(fieldName) && !VAL_FIELD_NAME.equalsIgnoreCase(fieldName))
+                map.put(fieldName, row.get(i));
         }
+
         return map;
     }
 
     /**
      * convert row ( with list of field values) into domain entity
+     *
+     * @param row SQL query result row.
+     * @param cursor SQL query result cursor through which {@param row} was obtained.
+     * @return Entitiy instance.
      */
-    private <V> V rowToEntity(final IgniteBinaryImpl binary,
-        final BinaryType binType,
-        final List<?> row,
-        final List<GridQueryFieldMetadata> meta) {
-        // additional data returned by query not present on domain object type
-        final TreeMap<String, Object> metadata = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        final BinaryObjectBuilder bldr = binary.builder(binType.typeName());
+    private <V> V rowToEntity(List<?> row, FieldsQueryCursor<?> cursor) {
+        Constructor<?> ctor;
 
-        for (int i = 0; i < row.size(); i++) {
-            final GridQueryFieldMetadata fMeta = meta.get(i);
-            final String metaField = fMeta.fieldName();
-            // add existing entity fields to binary object
-            if (binType.field(fMeta.fieldName()) != null && !metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME)
-                && !metaField.equalsIgnoreCase(QueryUtils.VAL_FIELD_NAME)) {
-                final Object fieldValue = row.get(i);
-                if (fieldValue != null) {
-                    final Class<?> clazz = getClassForBinaryField(binary, binType, fMeta);
-                    // null values must not be set into binary objects
-                    bldr.setField(metaField, fixExpectedType(fieldValue, clazz));
-                }
-            }
-            else {
-                // don't want key or val column... but wants null values
-                if (!metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME) && !metaField.equalsIgnoreCase(
-                    QueryUtils.VAL_FIELD_NAME))
-                    metadata.put(metaField, row.get(i));
-            }
-        }
-        return bldr.build().deserialize();
-    }
-
-    /**
-     * Obtains real field class from resultset metadata field whether it's available
-     */
-    private Class<?> getClassForBinaryField(final IgniteBinaryImpl binary,
-        final BinaryType binType,
-        final GridQueryFieldMetadata fieldMeta) {
         try {
-            final String fieldId = fieldMeta.schemaName() + "." + fieldMeta.typeName() + "." + fieldMeta.fieldName();
+            ctor = type.getDeclaredConstructor();
 
-            if (binaryFieldClass.containsKey(fieldId))
-                return binaryFieldClass.get(fieldId);
+            ctor.setAccessible(true);
+        }
+        catch (NoSuchMethodException | SecurityException ignored) {
+            ctor = null;
+        }
 
-            Class<?> clazz = null;
+        try {
+            Object res = ctor == null ? GridUnsafe.allocateInstance(type) : ctor.newInstance();
 
-            synchronized (binaryFieldClass) {
+            for (int i = 0; i < row.size(); i++) {
+                Field entityField = domainEntitiyFields.get(cursor.getFieldName(i).toLowerCase());
 
-                if (binaryFieldClass.containsKey(fieldId))
-                    return binaryFieldClass.get(fieldId);
-
-                String fieldName = null;
-
-                // search field name on binary type (query returns case insensitive
-                // field name) but BinaryType is not case insensitive
-                for (final String fname : binType.fieldNames()) {
-                    if (fname.equalsIgnoreCase(fieldMeta.fieldName())) {
-                        fieldName = fname;
-                        break;
-                    }
-                }
-
-                final CacheObjectBinaryProcessorImpl proc = (CacheObjectBinaryProcessorImpl)binary.processor();
-
-                // search for class by typeId, if not found use
-                // fieldMeta.fieldTypeName() class
-                clazz = BinaryUtils.resolveClass(proc.binaryContext(), binary.typeId(binType.fieldTypeName(fieldName)),
-                    fieldMeta.fieldTypeName(), ignite.configuration().getClassLoader(), true);
-
-                binaryFieldClass.put(fieldId, clazz);
+                if (entityField != null)
+                    FieldUtils.writeField(entityField, res, fixExpectedType(row.get(i), entityField.getType()), true);
             }
 
-            return clazz;
+            return (V)res;
         }
-        catch (final Exception e) {
-            return null;
+        catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+            throw new IgniteException("Unable to allocate instance of domain entity class " + type.getName(), e);
         }
     }
 
@@ -954,21 +896,6 @@
     }
 
     /**
-     * @param ctx   Context.
-     * @param clazz Clazz.
-     */
-    private static void registerClassOnMarshaller(final GridKernalContext ctx, final Class<?> clazz) {
-        try {
-            // ensure class registration for marshaller on cluster...
-            if (!U.isJdk(clazz))
-                U.marshal(ctx, clazz.newInstance());
-        }
-        catch (final Exception ignored) {
-            // silent
-        }
-    }
-
-    /**
      * Ignite QueryCursor wrapper.
      * <p>
      * Ensures closing underline cursor when there is no data.
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java
index 97d31e9..a9c7ad2 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java
@@ -20,10 +20,15 @@
 import java.util.Map;
 import java.util.Optional;
 import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteClientProxy;
 import org.apache.ignite.springdata22.repository.IgniteRepository;
 import org.apache.ignite.springdata22.repository.config.DynamicQueryConfig;
 import org.apache.ignite.springdata22.repository.config.Query;
@@ -73,7 +78,7 @@
     private final Map<Class<?>, String> repoToCache = new HashMap<>();
 
     /** Mapping of a repository to a ignite instance. */
-    private final Map<Class<?>, Ignite> repoToIgnite = new HashMap<>();
+    private final Map<Class<?>, IgniteProxy> repoToIgnite = new HashMap<>();
 
     /**
      * Creates the factory with initialized {@link Ignite} instance.
@@ -89,35 +94,51 @@
     }
 
     /** */
-    private Ignite igniteForRepoConfig(RepositoryConfig config) {
+    private IgniteProxy igniteForRepoConfig(RepositoryConfig config) {
         try {
-            String igniteInstanceName = evaluateExpression(config.igniteInstance());
-            return (Ignite)ctx.getBean(igniteInstanceName);
+            Object igniteInstanceBean = ctx.getBean(evaluateExpression(config.igniteInstance()));
+
+            if (igniteInstanceBean instanceof Ignite)
+                return new IgniteProxyImpl((Ignite)igniteInstanceBean);
+            else if (igniteInstanceBean instanceof IgniteClient)
+                return new IgniteClientProxy((IgniteClient)igniteInstanceBean);
+
+            throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" +
+                " \"igniteInstance\" property of repository configuration must be one of the following types: " +
+                Ignite.class.getName() + ", " + IgniteClient.class.getName());
         }
         catch (BeansException ex) {
             try {
-                String igniteConfigName = evaluateExpression(config.igniteCfg());
-                IgniteConfiguration cfg = (IgniteConfiguration)ctx.getBean(igniteConfigName);
-                try {
-                    // first try to attach to existing ignite instance
-                    return Ignition.ignite(cfg.getIgniteInstanceName());
+                Object igniteCfgBean = ctx.getBean(evaluateExpression(config.igniteCfg()));
+
+                if (igniteCfgBean instanceof IgniteConfiguration) {
+                    try {
+                        // first try to attach to existing ignite instance
+                        return new IgniteProxyImpl(Ignition.ignite(((IgniteConfiguration)igniteCfgBean).getIgniteInstanceName()));
+                    }
+                    catch (Exception ignored) {
+                        // nop
+                    }
+                    return new IgniteProxyImpl(Ignition.start((IgniteConfiguration)igniteCfgBean));
                 }
-                catch (Exception ignored) {
-                    // nop
-                }
-                return Ignition.start(cfg);
+                else if (igniteCfgBean instanceof ClientConfiguration)
+                    return new IgniteClientProxy(Ignition.startClient((ClientConfiguration)igniteCfgBean));
+
+                throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" +
+                    " the \"igniteCfg\" property of repository configuration must be one of the following types: [" +
+                    IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']');
+
             }
             catch (BeansException ex2) {
                 try {
                     String igniteSpringCfgPath = evaluateExpression(config.igniteSpringCfgPath());
                     String path = (String)ctx.getBean(igniteSpringCfgPath);
-                    return Ignition.start(path);
+                    return new IgniteProxyImpl(Ignition.start(path));
                 }
                 catch (BeansException ex3) {
-                    throw new IgniteException("Failed to initialize Ignite repository factory. Ignite instance or"
-                        + " IgniteConfiguration or a path to Ignite's spring XML "
-                        + "configuration must be defined in the"
-                        + " application configuration");
+                    throw new IgniteException("Failed to initialize Ignite repository factory. No beans required for" +
+                        " repository configuration were found. Check \"igniteInstance\", \"igniteCfg\"," +
+                        " \"igniteSpringCfgPath\" parameters of " + RepositoryConfig.class.getName() + "class.");
                 }
             }
         }
@@ -176,14 +197,14 @@
     }
 
     /** Control underlying cache creation to avoid cache creation by mistake */
-    private IgniteCache getRepositoryCache(Class<?> repoIf) {
-        Ignite ignite = repoToIgnite.get(repoIf);
+    private IgniteCacheProxy<?, ?> getRepositoryCache(Class<?> repoIf) {
+        IgniteProxy ignite = repoToIgnite.get(repoIf);
 
         RepositoryConfig config = repoIf.getAnnotation(RepositoryConfig.class);
 
         String cacheName = repoToCache.get(repoIf);
 
-        IgniteCache c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName);
+        IgniteCacheProxy<?, ?> c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName);
 
         if (c == null) {
             throw new IllegalStateException(
@@ -198,7 +219,7 @@
 
     /** {@inheritDoc} */
     @Override protected Object getTargetRepository(RepositoryInformation metadata) {
-        Ignite ignite = repoToIgnite.get(metadata.getRepositoryInterface());
+        IgniteProxy ignite = repoToIgnite.get(metadata.getRepositoryInterface());
 
         return getTargetRepositoryViaReflection(metadata, ignite,
             getRepositoryCache(metadata.getRepositoryInterface()));
@@ -209,8 +230,6 @@
         QueryMethodEvaluationContextProvider evaluationContextProvider) {
         return Optional.of((mtd, metadata, factory, namedQueries) -> {
             final Query annotation = mtd.getAnnotation(Query.class);
-            final Ignite ignite = repoToIgnite.get(metadata.getRepositoryInterface());
-
             if (annotation != null && (StringUtils.hasText(annotation.value()) || annotation.textQuery() || annotation
                 .dynamicQuery())) {
 
@@ -224,7 +243,7 @@
                     annotation.textQuery(), false, IgniteQueryGenerator.getOptions(mtd)) : null;
 
                 if (key != QueryLookupStrategy.Key.CREATE) {
-                    return new IgniteRepositoryQuery(ignite, metadata, query, mtd, factory,
+                    return new IgniteRepositoryQuery(metadata, query, mtd, factory,
                         getRepositoryCache(metadata.getRepositoryInterface()),
                         annotatedIgniteQuery ? DynamicQueryConfig.fromQueryAnnotation(annotation) : null,
                         evaluationContextProvider);
@@ -237,7 +256,7 @@
                     + ".config.Query annotation.");
             }
 
-            return new IgniteRepositoryQuery(ignite, metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd,
+            return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd,
                 factory, getRepositoryCache(metadata.getRepositoryInterface()),
                 DynamicQueryConfig.fromQueryAnnotation(annotation), evaluationContextProvider);
         });
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java
index c4d9834..de0573b 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java
@@ -29,7 +29,12 @@
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
 import org.apache.ignite.springdata22.repository.IgniteRepository;
+import org.apache.ignite.springdata22.repository.config.RepositoryConfig;
 import org.jetbrains.annotations.Nullable;
 import org.springframework.context.annotation.Conditional;
 
@@ -44,15 +49,19 @@
  */
 @Conditional(ConditionFalse.class)
 public class IgniteRepositoryImpl<V, K extends Serializable> implements IgniteRepository<V, K> {
+    /** Error message indicating that operation is spported only if {@link Ignite} instance is used to access the cluster. */
+    private static final String UNSUPPORTED_ERR_MSG = "Current operation is supported only if Ignite node instance is" +
+        " used to access the Ignite cluster. See " + RepositoryConfig.class.getName() + "#igniteInstance.";
+
     /**
      * Ignite Cache bound to the repository
      */
-    private final IgniteCache<K, V> cache;
+    private final IgniteCacheProxy<K, V> cache;
 
     /**
      * Ignite instance bound to the repository
      */
-    private final Ignite ignite;
+    private final IgniteProxy ignite;
 
     /**
      * Repository constructor.
@@ -60,19 +69,25 @@
      * @param ignite the ignite
      * @param cache  Initialized cache instance.
      */
-    public IgniteRepositoryImpl(Ignite ignite, IgniteCache<K, V> cache) {
+    public IgniteRepositoryImpl(IgniteProxy ignite, IgniteCacheProxy<K, V> cache) {
         this.cache = cache;
         this.ignite = ignite;
     }
 
     /** {@inheritDoc} */
     @Override public IgniteCache<K, V> cache() {
-        return cache;
+        if (cache instanceof IgniteCacheProxyImpl)
+            return ((IgniteCacheProxyImpl<K, V>)cache).delegate();
+
+        throw new UnsupportedOperationException(UNSUPPORTED_ERR_MSG);
     }
 
     /** {@inheritDoc} */
     @Override public Ignite ignite() {
-        return ignite;
+        if (ignite instanceof IgniteProxyImpl)
+            return ((IgniteProxyImpl)ignite).delegate();
+
+        throw new UnsupportedOperationException(UNSUPPORTED_ERR_MSG);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
new file mode 100644
index 0000000..6dcadb8
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.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.springdata;
+
+import org.apache.ignite.springdata.compoundkey.CityRepository;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data operation with compound key when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCompoundKeyTest extends IgniteSpringDataCompoundKeyTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(CityRepository.class);
+    }
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
new file mode 100644
index 0000000..d1597d9
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.springdata;
+
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.IgniteClientConfigRepository;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactory;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelfTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(PersonRepository.class);
+    }
+
+    /** Text queries are not supported when {@link IgniteClient} is used for acessing the Ignite cluster. */
+    @Override public void testUpdateQueryMixedCaseProjectionIndexedParameterLuceneTextQuery() {
+        GridTestUtils.assertThrows(log,
+            () -> repo.textQueryByFirstNameWithProjectionNamedParameter("person"), IllegalStateException.class,
+            "Query of type TextQuery is not supported by thin client. Check" +
+                " org.apache.ignite.springdata.misc.PersonRepository#textQueryByFirstNameWithProjectionNamedParameter" +
+                " method configuration or use Ignite node instance to connect to the Ignite cluster.");
+    }
+
+    /**
+     * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster.
+     */
+    @Test
+    public void testRepositoryWithClientConfiguration() {
+        IgniteRepositoryFactory factory = new IgniteRepositoryFactory(ctx);
+
+        IgniteClientConfigRepository repo = factory.getRepository(IgniteClientConfigRepository.class);
+
+        assertTrue(repo.count() > 0);
+    }
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
new file mode 100644
index 0000000..27d3645
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.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.springdata;
+
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.Person;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.PersonRepositoryOtherIgniteInstance;
+import org.apache.ignite.springdata.misc.PersonSecondRepository;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data query operations when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataQueriesSelfTest extends IgniteSpringDataQueriesSelfTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(PersonRepository.class);
+        repo2 = ctx.getBean(PersonSecondRepository.class);
+        repoTWO = ctx.getBean(PersonRepositoryOtherIgniteInstance.class);
+
+        for (int i = 0; i < CACHE_SIZE; i++) {
+            repo.save(i, new Person("person" + Integer.toHexString(i),
+                "lastName" + Integer.toHexString((i + 16) % 256)));
+            repoTWO.save(i, new Person("TWOperson" + Integer.toHexString(i),
+                "lastName" + Integer.toHexString((i + 16) % 256)));
+        }
+    }
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
index c4f9c0e..06e0c28 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
@@ -29,15 +29,17 @@
 import org.junit.Test;
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+
 /**
  * Test with using conpoud key in spring-data
  * */
 public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest {
     /** Application context */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /** City repository */
-    private static CityRepository repo;
+    protected static CityRepository repo;
 
     /** Cache name */
     private static final String CACHE_NAME = "City";
@@ -101,7 +103,7 @@
         if (ignite.cacheNames().contains(CACHE_NAME))
             ignite.destroyCache(CACHE_NAME);
 
-        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:" + CompoundKeyApplicationConfiguration.CLI_CONN_PORT + '/')) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:" + CLI_CONN_PORT + '/')) {
             Statement st = conn.createStatement();
 
             st.execute("DROP TABLE IF EXISTS City");
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
index 38785ef..a543044 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
@@ -37,10 +37,10 @@
  */
 public class IgniteSpringDataCrudSelfTest extends GridCommonAbstractTest {
     /** Repository. */
-    private static PersonRepository repo;
+    protected static PersonRepository repo;
 
     /** Context. */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /** Number of entries to store */
     private static int CACHE_SIZE = 1000;
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
index d35c781..19a9f63 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
@@ -20,6 +20,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import javax.cache.Cache;
 import org.apache.ignite.springdata.misc.ApplicationConfiguration;
 import org.apache.ignite.springdata.misc.Person;
@@ -39,23 +40,23 @@
  */
 public class IgniteSpringDataQueriesSelfTest extends GridCommonAbstractTest {
     /** Repository. */
-    private static PersonRepository repo;
+    protected static PersonRepository repo;
 
     /** Repository 2. */
-    private static PersonSecondRepository repo2;
+    protected static PersonSecondRepository repo2;
 
     /**
      * Repository Ignite Instance cluster TWO.
      */
-    private static PersonRepositoryOtherIgniteInstance repoTWO;
+    protected static PersonRepositoryOtherIgniteInstance repoTWO;
 
     /** Context. */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /**
      * Number of entries to store
      */
-    private static int CACHE_SIZE = 1000;
+    protected static int CACHE_SIZE = 1000;
 
     /**
      * Performs context initialization before tests.
@@ -406,4 +407,35 @@
             assertTrue(list.get(0) instanceof Integer);
         }
     }
+
+    /** Tests conversion of SQL select query result to domain entity objects. */
+    @Test
+    public void testRowToEntityConversion() {
+        Set<Person> res = new HashSet<>(repo.queryWithRowToEntityConversion());
+
+        Set<Person> exp = new HashSet<>();
+
+        repo.findAll().forEach(exp::add);
+
+        assertEquals(exp, res);
+    }
+
+    /**
+     * Tests conversion of SQL select query result to domain entity objects if result rows don't contain all fields
+     * of domain entity class.
+     */
+    @Test
+    public void testIncompleteRowToEntityConversion() {
+        Set<Person> res = new HashSet<>(repo.queryWithIncompleteRowToEntityConversion());
+
+        Set<Person> exp = new HashSet<>();
+
+        repo.findAll().forEach(p -> {
+            p.setSecondName(null);
+
+            exp.add(p);
+        });
+
+        assertEquals(exp, res);
+    }
 }
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
new file mode 100644
index 0000000..c5d159a
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
@@ -0,0 +1,105 @@
+/*
+ * 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.springdata.misc;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.springdata.misc.SampleEvaluationContextExtension.SamplePassParamExtension;
+import org.apache.ignite.springdata22.repository.config.EnableIgniteRepositories;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.spel.spi.EvaluationContextExtension;
+
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+import static org.apache.ignite.springdata.misc.ApplicationConfiguration.IGNITE_INSTANCE_ONE;
+import static org.apache.ignite.springdata.misc.ApplicationConfiguration.IGNITE_INSTANCE_TWO;
+
+/** Spring Application configuration for repository testing in case thin client is used for accessing the cluster. */
+@Configuration
+@EnableIgniteRepositories({"org.apache.ignite.springdata.compoundkey", "org.apache.ignite.springdata.misc"})
+public class IgniteClientApplicationConfiguration {
+    /** Test cache name. */
+    public static final String CACHE_NAME = "PersonCache";
+
+    /** */
+    @Bean
+    public CacheNamesBean cacheNames() {
+        CacheNamesBean bean = new CacheNamesBean();
+
+        bean.setPersonCacheName(CACHE_NAME);
+
+        return bean;
+    }
+
+    /** */
+    @Bean
+    public EvaluationContextExtension sampleSpELExtension() {
+        return new SampleEvaluationContextExtension();
+    }
+
+    /** */
+    @Bean(value = "sampleExtensionBean")
+    public SamplePassParamExtension sampleExtensionBean() {
+        return new SamplePassParamExtension();
+    }
+
+    /** */
+    @Bean
+    public Ignite igniteServerNode() {
+        return Ignition.start(igniteConfiguration(IGNITE_INSTANCE_ONE, CLI_CONN_PORT));
+    }
+
+    /** Ignite client instance bean with default name. */
+    @Bean
+    public IgniteClient igniteInstance() {
+        return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT)
+            .setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false)));
+    }
+
+    /** Ignite client instance bean with non-default name. */
+    @Bean
+    public IgniteClient igniteInstanceTWO() {
+        Ignition.start(igniteConfiguration(IGNITE_INSTANCE_TWO, 10801));
+
+        return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:10801"));
+    }
+
+    /** Ignite client configuraition bean. */
+    @Bean
+    public ClientConfiguration clientConfiguration() {
+        return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT);
+    }
+
+    /** Ingite configuration for server node. */
+    private static IgniteConfiguration igniteConfiguration(String igniteInstanceName, int cliConnPort) {
+        return new IgniteConfiguration()
+            .setIgniteInstanceName(igniteInstanceName)
+            .setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(new TcpDiscoveryVmIpFinder(true)))
+            .setCacheConfiguration(new CacheConfiguration<>(CACHE_NAME).setIndexedTypes(Integer.class, Person.class))
+            .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(cliConnPort))
+            .setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false));
+    }
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java
new file mode 100644
index 0000000..23a9d8e
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java
@@ -0,0 +1,29 @@
+/*
+ * 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.springdata.misc;
+
+import java.io.Serializable;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata22.repository.IgniteRepository;
+import org.apache.ignite.springdata22.repository.config.RepositoryConfig;
+
+/** Repository for testing repository configurion approach through {@link ClientConfiguration}. */
+@RepositoryConfig(cacheName = "PersonCache", igniteCfg = "clientConfiguration")
+public interface IgniteClientConfigRepository extends IgniteRepository<Object, Serializable> {
+    // No-op.
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
index 531c67f..62c3054 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.springdata.misc;
 
+import java.util.Date;
 import java.util.Objects;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
 import org.apache.ignite.cache.query.annotations.QueryTextField;
@@ -34,6 +35,10 @@
     @QuerySqlField(index = true)
     private String secondName;
 
+    /** Birthday. */
+    @QuerySqlField
+    private Date birthday;
+
     /**
      * @param firstName First name.
      * @param secondName Second name.
@@ -41,6 +46,7 @@
     public Person(String firstName, String secondName) {
         this.firstName = firstName;
         this.secondName = secondName;
+        birthday = new Date();
     }
 
     /**
@@ -76,6 +82,7 @@
         return "Person{" +
             "firstName='" + firstName + '\'' +
             ", secondName='" + secondName + '\'' +
+            ", birthday='" + birthday + '\'' +
             '}';
     }
 
@@ -90,11 +97,12 @@
         Person person = (Person)o;
 
         return Objects.equals(firstName, person.firstName) &&
-            Objects.equals(secondName, person.secondName);
+            Objects.equals(secondName, person.secondName) &&
+            Objects.equals(birthday, person.birthday);
     }
 
     /** {@inheritDoc} */
     @Override public int hashCode() {
-        return Objects.hash(firstName, secondName);
+        return Objects.hash(firstName, secondName, birthday);
     }
 }
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
index 0a5826f..4fd3e07 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
@@ -148,4 +148,12 @@
     /** Update using @Query but with errors on the query */
     @Query("UPDATE Person SET secondName = ? WHERE firstName = ? AND ERRORS = 'ERRORS'")
     public int setWrongFixedSecondName(String secondName, String firstName);
+
+    /** Produces a list of domain entity classes whose fields are obtained from the query result row. */
+    @Query(value = "SELECT firstName, secondName, birthday, _key, _val, NULL as one FROM Person", forceFieldsQuery = true)
+    public List<Person> queryWithRowToEntityConversion();
+
+    /** Produces a list of domain entity classes whose fields are obtained from the query result row. */
+    @Query(value = "SELECT firstName, birthday FROM Person", forceFieldsQuery = true)
+    public List<Person> queryWithIncompleteRowToEntityConversion();
 }
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java
index 872fe9f..4363f74 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.testsuites;
 
+import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest;
 import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest;
 import org.apache.ignite.springdata.IgniteSpringDataCrudSelfExpressionTest;
 import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest;
@@ -32,7 +35,10 @@
     IgniteSpringDataCrudSelfTest.class,
     IgniteSpringDataQueriesSelfTest.class,
     IgniteSpringDataCrudSelfExpressionTest.class,
-    IgniteSpringDataCompoundKeyTest.class
+    IgniteSpringDataCompoundKeyTest.class,
+    IgniteClientSpringDataCrudSelfTest.class,
+    IgniteClientSpringDataQueriesSelfTest.class,
+    IgniteClientSpringDataCompoundKeyTest.class
 })
 public class IgniteSpringData22TestSuite {
 }
diff --git a/modules/spring-data-commons/licenses/apache-2.0.txt b/modules/spring-data-commons/licenses/apache-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/modules/spring-data-commons/licenses/apache-2.0.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/modules/spring-data-commons/pom.xml b/modules/spring-data-commons/pom.xml
new file mode 100644
index 0000000..5e9be93
--- /dev/null
+++ b/modules/spring-data-commons/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<!--
+    POM file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.ignite</groupId>
+        <artifactId>ignite-extensions-parent</artifactId>
+        <version>1</version>
+        <relativePath>../../parent</relativePath>
+    </parent>
+
+    <artifactId>ignite-spring-data-commons</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <url>http://ignite.apache.org</url>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${ignite.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheClientProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheClientProxy.java
new file mode 100644
index 0000000..10fef14
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheClientProxy.java
@@ -0,0 +1,102 @@
+/*
+ * 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.springdata.proxy;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.cache.Cache;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.query.Query;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.client.ClientCache;
+import org.apache.ignite.client.ClientException;
+import org.jetbrains.annotations.NotNull;
+
+/** Implementation of {@link IgniteCacheProxy} that provides access to Ignite cache through {@link ClientCache} instance. */
+public class IgniteCacheClientProxy<K, V> implements IgniteCacheProxy<K, V> {
+    /** {@link ClientCache} instance to which operations are delegated. */
+    private final ClientCache<K, V> cache;
+
+    /** */
+    public IgniteCacheClientProxy(ClientCache<K, V> cache) {
+        this.cache = cache;
+    }
+
+    /** {@inheritDoc} */
+    @Override public V get(K key) throws ClientException {
+        return cache.get(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void put(K key, V val) throws ClientException {
+        cache.put(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size(CachePeekMode... peekModes) throws ClientException {
+        return cache.size(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Map<K, V> getAll(Set<? extends K> keys) throws ClientException {
+        return cache.getAll(keys);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void putAll(Map<? extends K, ? extends V> map) throws ClientException {
+        cache.putAll(map);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean remove(K key) throws ClientException {
+        return cache.remove(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeAll(Set<? extends K> keys) throws ClientException {
+        cache.removeAll(keys);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() throws ClientException {
+        cache.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteCacheProxy<K, V> withExpiryPolicy(ExpiryPolicy expirePlc) {
+        return new IgniteCacheClientProxy<>(cache.withExpirePolicy(expirePlc));
+    }
+
+    /** {@inheritDoc} */
+    @Override public <R> QueryCursor<R> query(Query<R> qry) {
+        return cache.query(qry);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsKey(K key) {
+        return cache.containsKey(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public @NotNull Iterator<Cache.Entry<K, V>> iterator() {
+        return cache.<Cache.Entry<K, V>>query(new ScanQuery<>()).getAll().iterator();
+    }
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java
new file mode 100644
index 0000000..ac29eb4
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java
@@ -0,0 +1,111 @@
+/*
+ * 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.springdata.proxy;
+
+import java.util.Map;
+import java.util.Set;
+import javax.cache.Cache;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.query.Query;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.client.ClientCache;
+
+/** Reperesents Ignite cache operations required by Spring Data. */
+public interface IgniteCacheProxy<K, V> extends Iterable<Cache.Entry<K, V>> {
+    /**
+     * Gets an entry from the cache.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the element, or null, if it does not exist.
+     */
+    public V get(K key);
+
+    /**
+     * Associates the specified value with the specified key in the cache.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param val value to be associated with the specified key.
+     */
+    public void put(K key, V val);
+
+    /**
+     * Gets the number of all entries cached across all nodes.
+     *
+     * @param peekModes Optional peek modes. If not provided, then total cache size is returned.
+     */
+    public int size(CachePeekMode... peekModes);
+
+    /**
+     * Gets a collection of entries from the Ignite cache, returning them as
+     * {@link Map} of the values associated with the set of keys requested.
+     *
+     * @param keys The keys whose associated values are to be returned.
+     * @return A map of entries that were found for the given keys.
+     */
+    public Map<K, V> getAll(Set<? extends K> keys);
+
+    /**
+     * Copies all of the entries from the specified map to the Ignite cache.
+     *
+     * @param map Mappings to be stored in this cache.
+     */
+    public void putAll(Map<? extends K, ? extends V> map);
+
+    /**
+     * Removes the mapping for a key from this cache if it is present.
+     *
+     * @param key Key whose mapping is to be removed from the cache.
+     * @return <tt>false</tt> if there was no matching key.
+     */
+    public boolean remove(K key);
+
+    /**
+     * Removes entries for the specified keys.
+     *
+     * @param keys The keys to remove.
+     */
+    public void removeAll(Set<? extends K> keys);
+
+    /** Clears the contents of the cache. */
+    public void clear();
+
+    /**
+     * Returns cache with the specified expired policy set. This policy will be used for each operation invoked on
+     * the returned cache.
+     *
+     * @return Cache instance with the specified expiry policy set.
+     */
+    public IgniteCacheProxy<K, V> withExpiryPolicy(ExpiryPolicy expirePlc);
+
+    /**
+     * Execute SQL query and get cursor to iterate over results.
+     *
+     * @param qry SQL query.
+     * @return Query result cursor.
+     */
+    public <R> QueryCursor<R> query(Query<R> qry) ;
+
+    /**
+     * Determines if the {@link ClientCache} contains an entry for the specified key.
+     *
+     * @param key key whose presence in this cache is to be tested.
+     * @return <tt>true</tt> if this map contains a mapping for the specified key.
+     */
+    public boolean containsKey(K key);
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxyImpl.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxyImpl.java
new file mode 100644
index 0000000..7a4c716
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxyImpl.java
@@ -0,0 +1,107 @@
+/*
+ * 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.springdata.proxy;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.cache.Cache;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.query.Query;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.client.ClientException;
+import org.jetbrains.annotations.NotNull;
+
+/** Implementation of {@link IgniteCacheProxy} that provides access to Ignite cache through {@link IgniteCache} instance. */
+public class IgniteCacheProxyImpl<K, V> implements IgniteCacheProxy<K, V> {
+    /** {@link IgniteCache} instance to which operations are delegated. */
+    private final IgniteCache<K, V> cache;
+
+    /** */
+    public IgniteCacheProxyImpl(IgniteCache<K, V> cache) {
+        this.cache = cache;
+    }
+
+    /** {@inheritDoc} */
+    @Override public V get(K key) throws ClientException {
+        return cache.get(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void put(K key, V val) throws ClientException {
+        cache.put(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size(CachePeekMode... peekModes) throws ClientException {
+        return cache.size(peekModes);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Map<K, V> getAll(Set<? extends K> keys) throws ClientException {
+        return cache.getAll(keys);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void putAll(Map<? extends K, ? extends V> map) throws ClientException {
+        cache.putAll(map);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean remove(K key) throws ClientException {
+        return cache.remove(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeAll(Set<? extends K> keys) throws ClientException {
+        cache.removeAll(keys);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() throws ClientException {
+        cache.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public IgniteCacheProxy<K, V> withExpiryPolicy(ExpiryPolicy expirePlc) {
+        return new IgniteCacheProxyImpl<>(cache.withExpiryPolicy(expirePlc));
+    }
+
+    /** {@inheritDoc} */
+    @Override public <R> QueryCursor<R> query(Query<R> qry) {
+        return cache.query(qry);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsKey(K key) {
+        return cache.containsKey(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public @NotNull Iterator<Cache.Entry<K, V>> iterator() {
+        return cache.<Cache.Entry<K, V>>query(new ScanQuery<>()).getAll().iterator();
+    }
+
+    /** @return {@link IgniteCache} instance to which operations are delegated. */
+    public IgniteCache<K, V> delegate() {
+        return cache;
+    }
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java
new file mode 100644
index 0000000..ae50823
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java
@@ -0,0 +1,41 @@
+/*
+ * 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.springdata.proxy;
+
+import org.apache.ignite.client.IgniteClient;
+
+/** Implementation of {@link IgniteProxy} that provides access to Ignite cluster through {@link IgniteClient} instance. */
+public class IgniteClientProxy implements IgniteProxy {
+    /** {@link IgniteClient} instance to which operations are delegated.  */
+    private final IgniteClient cli;
+
+    /** */
+    public IgniteClientProxy(IgniteClient cli) {
+        this.cli = cli;
+    }
+
+    /** {@inheritDoc} */
+    @Override public <K, V> IgniteCacheProxy<K, V> getOrCreateCache(String name) {
+        return new IgniteCacheClientProxy<>(cli.getOrCreateCache(name));
+    }
+
+    /** {@inheritDoc} */
+    @Override public <K, V> IgniteCacheProxy<K, V> cache(String name) {
+        return new IgniteCacheClientProxy<>(cli.cache(name));
+    }
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java
new file mode 100644
index 0000000..e798274
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java
@@ -0,0 +1,37 @@
+/*
+ * 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.springdata.proxy;
+
+/** Reperesents Ignite cluster operations required by Spring Data. */
+public interface IgniteProxy {
+    /**
+     * Gets existing cache with the given name or creates new one.
+     *
+     * @param name Cache name.
+     * @return Cache proxy that provides access to cache with given name.
+     */
+    public <K, V> IgniteCacheProxy<K, V> getOrCreateCache(String name);
+
+    /**
+     * Gets cache with the given name.
+     *
+     * @param name Cache name.
+     * @return Cache proxy that provides access to cache with specified name or {@code null} if it doesn't exist.
+     */
+    public <K, V> IgniteCacheProxy<K, V> cache(String name);
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java
new file mode 100644
index 0000000..96b46a7
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java
@@ -0,0 +1,46 @@
+/*
+ * 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.springdata.proxy;
+
+import org.apache.ignite.Ignite;
+
+/** Implementation of {@link IgniteProxy} that provides access to Ignite cluster through {@link Ignite} instance. */
+public class IgniteProxyImpl implements IgniteProxy {
+    /** {@link Ignite} instance to which operations are delegated. */
+    private final Ignite ignite;
+
+    /** */
+    public IgniteProxyImpl(Ignite ignite) {
+        this.ignite = ignite;
+    }
+
+    /** {@inheritDoc} */
+    @Override public <K, V> IgniteCacheProxy<K, V> getOrCreateCache(String name) {
+        return new IgniteCacheProxyImpl<>(ignite.getOrCreateCache(name));
+    }
+
+    /** {@inheritDoc} */
+    @Override public <K, V> IgniteCacheProxy<K, V> cache(String name) {
+        return new IgniteCacheProxyImpl<>(ignite.cache(name));
+    }
+
+    /** @return {@link Ignite} instance to which operations are delegated. */
+    public Ignite delegate() {
+        return ignite;
+    }
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/package-info.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/package-info.java
new file mode 100644
index 0000000..cbb8373
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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 description. -->
+ * Contains classes that provide client-independent access to the Ignite operations required by Spring Data.
+ */
+package org.apache.ignite.springdata.proxy;
diff --git a/modules/spring-data-ext/pom.xml b/modules/spring-data-ext/pom.xml
index b7547f1..3015102 100644
--- a/modules/spring-data-ext/pom.xml
+++ b/modules/spring-data-ext/pom.xml
@@ -95,6 +95,12 @@
             <version>${ignite.version}</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring-data-commons</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/query/IgniteRepositoryQuery.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/query/IgniteRepositoryQuery.java
index e351c61..79ce6c0 100644
--- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/query/IgniteRepositoryQuery.java
+++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/query/IgniteRepositoryQuery.java
@@ -25,12 +25,11 @@
 import java.util.Iterator;
 import java.util.List;
 import javax.cache.Cache;
-import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.query.Query;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.SqlQuery;
-import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.springframework.data.domain.Pageable;
@@ -84,7 +83,7 @@
     private final IgniteQuery qry;
 
     /** Cache. */
-    private final IgniteCache cache;
+    private final IgniteCacheProxy<?, ?> cache;
 
     /** Method. */
     private final Method mtd;
@@ -106,7 +105,7 @@
      * @param cache Cache.
      */
     public IgniteRepositoryQuery(RepositoryMetadata metadata, IgniteQuery qry,
-        Method mtd, ProjectionFactory factory, IgniteCache cache) {
+        Method mtd, ProjectionFactory factory, IgniteCacheProxy<?, ?> cache) {
         type = metadata.getDomainType();
         this.qry = qry;
         this.cache = cache;
@@ -238,19 +237,19 @@
             }
         }
         else {
-            Iterable<CacheEntryImpl> qryIter = (Iterable<CacheEntryImpl>)qryCursor;
+            Iterable<Cache.Entry<?, ?>> qryIter = (Iterable<Cache.Entry<?, ?>>)qryCursor;
 
             switch (returnStgy) {
                 case LIST_OF_VALUES:
                     List list = new ArrayList<>();
 
-                    for (CacheEntryImpl entry : qryIter)
+                    for (Cache.Entry<?, ?> entry : qryIter)
                         list.add(entry.getValue());
 
                     return list;
 
                 case ONE_VALUE:
-                    Iterator<CacheEntryImpl> iter1 = qryIter.iterator();
+                    Iterator<Cache.Entry<?, ?>> iter1 = qryIter.iterator();
 
                     if (iter1.hasNext())
                         return iter1.next().getValue();
@@ -258,7 +257,7 @@
                     return null;
 
                 case CACHE_ENTRY:
-                    Iterator<CacheEntryImpl> iter2 = qryIter.iterator();
+                    Iterator<Cache.Entry<?, ?>> iter2 = qryIter.iterator();
 
                     if (iter2.hasNext())
                         return iter2.next();
@@ -268,7 +267,7 @@
                 case SLICE_OF_VALUES:
                     List content = new ArrayList<>();
 
-                    for (CacheEntryImpl entry : qryIter)
+                    for (Cache.Entry<?, ?> entry : qryIter)
                         content.add(entry.getValue());
 
                     return new SliceImpl(content, (Pageable)prmtrs[prmtrs.length - 1], true);
diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java
index de2549f..5fc0ba2 100644
--- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java
+++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java
@@ -22,7 +22,12 @@
 import java.util.Map;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteClientProxy;
 import org.apache.ignite.springdata.repository.IgniteRepository;
 import org.apache.ignite.springdata.repository.config.Query;
 import org.apache.ignite.springdata.repository.config.RepositoryConfig;
@@ -47,7 +52,7 @@
  */
 public class IgniteRepositoryFactory extends RepositoryFactorySupport {
     /** Ignite instance */
-    private Ignite ignite;
+    private IgniteProxy ignite;
 
     /** Mapping of a repository to a cache. */
     private final Map<Class<?>, String> repoToCache = new HashMap<>();
@@ -58,7 +63,12 @@
      * @param ignite
      */
     public IgniteRepositoryFactory(Ignite ignite) {
-        this.ignite = ignite;
+        this.ignite = new IgniteProxyImpl(ignite);
+    }
+
+    /** Creates the factory with initialized {@link IgniteClient} instance. */
+    public IgniteRepositoryFactory(IgniteClient cli) {
+        ignite = new IgniteClientProxy(cli);
     }
 
     /**
@@ -68,7 +78,15 @@
      * @param cfg Ignite configuration.
      */
     public IgniteRepositoryFactory(IgniteConfiguration cfg) {
-        this.ignite = Ignition.start(cfg);
+        this.ignite = new IgniteProxyImpl(Ignition.start(cfg));
+    }
+
+    /**
+     * Initializes the factory with provided {@link ClientConfiguration} that is used to start up an underlying
+     * {@link IgniteClient} instance.
+     */
+    public IgniteRepositoryFactory(ClientConfiguration cfg) {
+        this.ignite = new IgniteClientProxy(Ignition.startClient(cfg));
     }
 
     /**
@@ -78,7 +96,7 @@
      * @param springCfgPath A path to Ignite configuration.
      */
     public IgniteRepositoryFactory(String springCfgPath) {
-        this.ignite = Ignition.start(springCfgPath);
+        this.ignite = new IgniteProxyImpl(Ignition.start(springCfgPath));
     }
 
     /** {@inheritDoc} */
diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java
index f87dce5..71200c2 100644
--- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java
+++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java
@@ -20,6 +20,8 @@
 import java.io.Serializable;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.springdata.repository.IgniteRepository;
 import org.springframework.beans.BeansException;
@@ -35,8 +37,8 @@
  * The repository requires to define one of the parameters below in your Spring application configuration in order
  * to get an access to Apache Ignite cluster:
  * <ul>
- * <li>{@link Ignite} instance bean named "igniteInstance"</li>
- * <li>{@link IgniteConfiguration} bean named "igniteCfg"</li>
+ * <li>{@link Ignite} or {@link IgniteClient} instance bean named "igniteInstance"</li>
+ * <li>{@link IgniteConfiguration} or {@link ClientConfiguration} bean named "igniteCfg"</li>
  * <li>A path to Ignite's Spring XML configuration named "igniteSpringCfgPath"</li>
  * <ul/>
  *
@@ -64,15 +66,29 @@
     /** {@inheritDoc} */
     @Override protected RepositoryFactorySupport createRepositoryFactory() {
         try {
-            Ignite ignite = (Ignite)ctx.getBean("igniteInstance");
+            Object igniteInstanceBean = ctx.getBean("igniteInstance");
 
-            return new IgniteRepositoryFactory(ignite);
+            if (igniteInstanceBean instanceof Ignite)
+                return new IgniteRepositoryFactory((Ignite)igniteInstanceBean);
+            else if (igniteInstanceBean instanceof IgniteClient)
+                return new IgniteRepositoryFactory((IgniteClient)igniteInstanceBean);
+
+            throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" +
+                " \"igniteInstance\" property of repository configuration must be one of the following types: " +
+                Ignite.class.getName() + ", " + IgniteClient.class.getName());
         }
         catch (BeansException ex) {
             try {
-                IgniteConfiguration cfg = (IgniteConfiguration)ctx.getBean("igniteCfg");
+                Object igniteCfgBean = ctx.getBean("igniteCfg");
 
-                return new IgniteRepositoryFactory(cfg);
+                if (igniteCfgBean instanceof IgniteConfiguration)
+                    return new IgniteRepositoryFactory((IgniteConfiguration)igniteCfgBean);
+                else if (igniteCfgBean instanceof ClientConfiguration)
+                    return new IgniteRepositoryFactory((ClientConfiguration)igniteCfgBean);
+
+                throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" +
+                    " the \"igniteCfg\" property of repository configuration must be one of the following types: [" +
+                    IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']');
             }
             catch (BeansException ex2) {
                 try {
@@ -81,9 +97,9 @@
                     return new IgniteRepositoryFactory(path);
                 }
                 catch (BeansException ex3) {
-                    throw new IgniteException("Failed to initialize Ignite repository factory. Ignite instance or" +
-                        " IgniteConfiguration or a path to Ignite's spring XML configuration must be defined in the" +
-                        " application configuration");
+                    throw new IgniteException("Failed to initialize Ignite repository factory. One of the following" +
+                        " beans must be defined in application configuration: \"igniteInstance\", \"igniteCfg\"," +
+                        " \"igniteSpringCfgPath\".");
                 }
             }
         }
diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryImpl.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryImpl.java
index 95d933b..e8b8824 100644
--- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryImpl.java
+++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryImpl.java
@@ -23,8 +23,8 @@
 import java.util.Set;
 import java.util.TreeSet;
 import javax.cache.Cache;
-import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
 import org.apache.ignite.springdata.repository.IgniteRepository;
 
 import static java.util.Collections.emptySet;
@@ -34,14 +34,14 @@
  */
 public class IgniteRepositoryImpl<T, ID extends Serializable> implements IgniteRepository<T, ID> {
     /** Ignite Cache bound to the repository */
-    private final IgniteCache<ID, T> cache;
+    private final IgniteCacheProxy<ID, T> cache;
 
     /**
      * Repository constructor.
      *
      * @param cache Initialized cache instance.
      */
-    public IgniteRepositoryImpl(IgniteCache<ID, T> cache) {
+    public IgniteRepositoryImpl(IgniteCacheProxy<ID, T> cache) {
         this.cache = cache;
     }
 
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
new file mode 100644
index 0000000..6dcadb8
--- /dev/null
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.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.springdata;
+
+import org.apache.ignite.springdata.compoundkey.CityRepository;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data operation with compound key when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCompoundKeyTest extends IgniteSpringDataCompoundKeyTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(CityRepository.class);
+    }
+}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
new file mode 100644
index 0000000..a887ad8
--- /dev/null
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.springdata;
+
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.PersonRepositoryWithCompoundKey;
+import org.apache.ignite.springdata.repository.config.EnableIgniteRepositories;
+import org.junit.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+
+/** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelfTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(PersonRepository.class);
+        repoWithCompoundKey = ctx.getBean(PersonRepositoryWithCompoundKey.class);
+        ignite = ctx.getBean(IgniteEx.class);
+    }
+
+    /**
+     * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster.
+     */
+    @Test
+    public void testRepositoryWithClientConfiguration() {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(TestApplicationConfiguration.class);
+        ctx.refresh();
+
+        assertTrue(ctx.getBean(PersonRepository.class).count() > 0);
+    }
+
+    /** */
+    @Configuration
+    @EnableIgniteRepositories("org.apache.ignite.springdata.misc")
+    static class TestApplicationConfiguration {
+        /** Ignite client configuration bean. */
+        @Bean
+        public ClientConfiguration igniteCfg() {
+           return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT);
+        }
+    }
+}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
new file mode 100644
index 0000000..0410e20
--- /dev/null
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.springdata;
+
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.Person;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.PersonSecondRepository;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data query operations when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataQueriesSelfTest extends IgniteSpringDataQueriesSelfTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        ctx = new AnnotationConfigApplicationContext();
+
+        ctx.register(IgniteClientApplicationConfiguration.class);
+        ctx.refresh();
+
+        repo = ctx.getBean(PersonRepository.class);
+        repo2 = ctx.getBean(PersonSecondRepository.class);
+
+        for (int i = 0; i < CACHE_SIZE; i++) {
+            repo.save(i, new Person("person" + Integer.toHexString(i),
+                "lastName" + Integer.toHexString((i + 16) % 256)));
+        }
+    }
+}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
index 62ff8c5..655ef40 100644
--- a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
@@ -34,10 +34,10 @@
  * */
 public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest {
     /** Application context */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /** City repository */
-    private static CityRepository repo;
+    protected static CityRepository repo;
 
     /** Cache name */
     private static final String CACHE_NAME = "City";
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
index 65f9239..3f8cecc 100644
--- a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
@@ -41,23 +41,23 @@
  */
 public class IgniteSpringDataCrudSelfTest extends GridCommonAbstractTest {
     /** Number of entries to store. */
-    private static final int CACHE_SIZE = 1000;
+    protected static final int CACHE_SIZE = 1000;
 
     /** Context. */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /** Repository. */
-    private static PersonRepository repo;
+    protected static PersonRepository repo;
 
     /** Repository. */
-    private static PersonRepositoryWithCompoundKey repoWithCompoundKey;
+    protected static PersonRepositoryWithCompoundKey repoWithCompoundKey;
 
     /** */
     @Rule
     public final ExpectedException expected = ExpectedException.none();
 
     /** */
-    private static IgniteEx ignite;
+    protected static IgniteEx ignite;
 
     /** {@inheritDoc} */
     @Override protected void beforeTestsStarted() throws Exception {
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
index 51492c1..39568f9 100644
--- a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
@@ -37,16 +37,16 @@
  */
 public class IgniteSpringDataQueriesSelfTest extends GridCommonAbstractTest {
     /** Repository. */
-    private static PersonRepository repo;
+    protected static PersonRepository repo;
 
     /** Repository 2. */
-    private static PersonSecondRepository repo2;
+    protected static PersonSecondRepository repo2;
 
     /** Context. */
-    private static AnnotationConfigApplicationContext ctx;
+    protected static AnnotationConfigApplicationContext ctx;
 
     /** Number of entries to store */
-    private static int CACHE_SIZE = 1000;
+    protected static int CACHE_SIZE = 1000;
 
     @Override protected void beforeTestsStarted() throws Exception {
         super.beforeTestsStarted();
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
new file mode 100644
index 0000000..66379f7
--- /dev/null
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
@@ -0,0 +1,54 @@
+/*
+ * 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.springdata.misc;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.springdata.repository.config.EnableIgniteRepositories;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+
+/** Spring Application configuration for repository testing in case thin client is used for accessing the cluster. */
+@Configuration
+@EnableIgniteRepositories({"org.apache.ignite.springdata.compoundkey", "org.apache.ignite.springdata.misc"})
+public class IgniteClientApplicationConfiguration {
+    /** @return {@link Ignite} node instance. */
+    @Bean
+    public IgniteEx igniteServerNode() {
+        IgniteConfiguration cfg = new IgniteConfiguration()
+            .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(CLI_CONN_PORT))
+            .setCacheConfiguration(new CacheConfiguration<Integer, Person>("PersonCache")
+                .setIndexedTypes(Integer.class, Person.class));
+
+        return (IgniteEx)Ignition.start(cfg);
+    }
+
+    /** Ignite client instance bean with default name. */
+    @Bean
+    public IgniteClient igniteInstance() {
+        return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT));
+    }
+}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java
index cc063a6..cb6e34c 100644
--- a/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.testsuites;
 
+import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest;
 import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest;
 import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest;
 import org.apache.ignite.springdata.IgniteSpringDataQueriesSelfTest;
@@ -30,7 +33,10 @@
 @Suite.SuiteClasses({
     IgniteSpringDataCrudSelfTest.class,
     IgniteSpringDataQueriesSelfTest.class,
-    IgniteSpringDataCompoundKeyTest.class
+    IgniteSpringDataCompoundKeyTest.class,
+    IgniteClientSpringDataCrudSelfTest.class,
+    IgniteClientSpringDataQueriesSelfTest.class,
+    IgniteClientSpringDataCompoundKeyTest.class
 })
 public class IgniteSpringDataTestSuite {
 }
diff --git a/pom.xml b/pom.xml
index a3232d1..957acfb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,6 +58,7 @@
         <module>modules/spring-data-ext</module>
         <module>modules/spring-data-2.0-ext</module>
         <module>modules/spring-data-2.2-ext</module>
+        <module>modules/spring-data-commons</module>
     </modules>
 
     <profiles>