IGNITE-14767 Add examples to spring-data-2.2 module. (#62)

diff --git a/modules/spring-data-2.2-ext/examples/config/example-default.xml b/modules/spring-data-2.2-ext/examples/config/example-default.xml
new file mode 100644
index 0000000..e6c359d
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/config/example-default.xml
@@ -0,0 +1,76 @@
+<?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.
+-->
+
+<!--
+    Ignite configuration with all defaults and enabled p2p deployment and enabled events.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <bean abstract="true" id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <!-- Set to true to enable distributed class loading for examples, default is false. -->
+        <property name="peerClassLoadingEnabled" value="true"/>
+
+        <!-- Enable task execution events for examples. -->
+        <property name="includeEventTypes">
+            <list>
+                <!--Task execution events-->
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_STARTED"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FINISHED"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FAILED"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_TIMEDOUT"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_SESSION_ATTR_SET"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_REDUCED"/>
+
+                <!--Cache events-->
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_PUT"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_READ"/>
+                <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_REMOVED"/>
+            </list>
+        </property>
+
+        <!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. -->
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <!--
+                        Ignite provides several options for automatic discovery that can be used
+                        instead os static IP based discovery. For information on all options refer
+                        to our documentation: http://apacheignite.readme.io/docs/cluster-config
+                    -->
+                    <!-- Uncomment static IP finder to enable static-based discovery of initial nodes. -->
+                    <!--<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">-->
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <!-- In distributed environment, replace with actual host IP address. -->
+                                <value>127.0.0.1:47500..47509</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/spring-data-2.2-ext/examples/config/example-spring-data.xml b/modules/spring-data-2.2-ext/examples/config/example-spring-data.xml
new file mode 100644
index 0000000..9f7d674
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/config/example-spring-data.xml
@@ -0,0 +1,64 @@
+<?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.
+-->
+
+<!--
+    Ignite Spring configuration file to startup Ignite cache.
+
+    This file demonstrates how to configure cache using Spring. Provided cache
+    will be created on node startup.
+
+    Use this configuration file when running Spring Data examples.
+
+    When starting a standalone node, you need to execute the following command:
+    {IGNITE_HOME}/bin/ignite.{bat|sh} modules/spring-data-2.2/examples/config/example-spring-data.xml
+
+    When starting Ignite from Java IDE, pass path to this file to Ignition:
+    Ignition.start("modules/spring-data-2.2/examples/config/example-spring-data.xml");
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+    <!-- Imports default Ignite configuration -->
+    <import resource="example-default.xml"/>
+
+    <bean parent="ignite.cfg">
+        <property name="igniteInstanceName" value="springDataNode" />
+
+        <property name="cacheConfiguration">
+            <list>
+                <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                    <!--
+                        Apache Ignite uses an IgniteRepository extension which inherits from Spring Data's CrudRepository.
+                        The SQL grid is also enabled to aceess Spring Data repository. The @RepositoryConfig annotation
+                        maps the PersonRepository to an Ignite's cache named "PersonCache".
+                    -->
+                    <property name="name" value="PersonCache"/>
+                    <property name="indexedTypes">
+                        <list>
+                            <value>java.lang.Long</value>
+                            <value>org.apache.ignite.springdata22.examples.model.Person</value>
+                        </list>
+                    </property>
+                </bean>
+            </list>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/IgniteClientSpringApplicationConfiguration.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/IgniteClientSpringApplicationConfiguration.java
new file mode 100644
index 0000000..72d8eaa
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/IgniteClientSpringApplicationConfiguration.java
@@ -0,0 +1,53 @@
+/*
+ * 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.springdata22.examples;
+
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata22.repository.config.EnableIgniteRepositories;
+import org.apache.ignite.springdata22.repository.config.RepositoryConfig;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.apache.ignite.configuration.ClientConnectorConfiguration.DFLT_PORT;
+
+/**
+ * Example of Spring application configuration that represents beans required to configure Spring Data repository access
+ * to an Ignite cluster through the thin client.
+ *
+ * Note that both Ignite thin client and Ignite node approaches of Ignite cluster access configuration uses the same API.
+ * Ignite Spring Data integration automatically recognizes the type of provided bean and use the appropriate
+ * cluster connection.
+ *
+ * @see SpringApplicationConfiguration
+ */
+@Configuration
+@EnableIgniteRepositories
+public class IgniteClientSpringApplicationConfiguration {
+    /**
+     * Creates Apache Ignite thin client instance bean which will be used for accessing the Ignite cluster.
+     * Note, that the name of the current bean must match value of {@link RepositoryConfig#igniteInstance}
+     * property that {@link PersonRepository} is marked with. In this particular case, the default value of
+     * {@link RepositoryConfig#igniteInstance} property is used.
+     */
+    @Bean
+    public IgniteClient igniteInstance() {
+        return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:" + DFLT_PORT));
+    }
+}
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/PersonRepository.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/PersonRepository.java
new file mode 100644
index 0000000..24b8bdb
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/PersonRepository.java
@@ -0,0 +1,59 @@
+/*
+ * 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.springdata22.examples;
+
+import java.util.List;
+import javax.cache.Cache;
+import org.apache.ignite.springdata22.examples.model.Person;
+import org.apache.ignite.springdata22.repository.IgniteRepository;
+import org.apache.ignite.springdata22.repository.config.Query;
+import org.apache.ignite.springdata22.repository.config.RepositoryConfig;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * Apache Ignite Spring Data repository backed by Ignite Person's cache.
+ * </p>
+ * To link the repository with an Ignite cache use {@link RepositoryConfig#cacheName()} annotation's parameter.
+ */
+@RepositoryConfig(cacheName = "PersonCache")
+public interface PersonRepository extends IgniteRepository<Person, Long> {
+    /**
+     * Gets all the persons with the given name.
+     * @param name Person name.
+     * @return A list of Persons with the given first name.
+     */
+    public List<Person> findByFirstName(String name);
+
+    /**
+     * Returns top Person with the specified surname.
+     * @param name Person surname.
+     * @return Person that satisfy the query.
+     */
+    public Cache.Entry<Long, Person> findTopByLastNameLike(String name);
+
+    /**
+     * Getting ids of all the Person satisfying the custom query from {@link Query} annotation.
+     *
+     * @param orgId Query parameter.
+     * @param pageable Pageable interface.
+     * @return A list of Persons' ids.
+     */
+    @Query("SELECT id FROM Person WHERE orgId > ?")
+    public List<Long> selectId(long orgId, Pageable pageable);
+}
+
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/SpringApplicationConfiguration.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/SpringApplicationConfiguration.java
new file mode 100644
index 0000000..d3ca61e
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/SpringApplicationConfiguration.java
@@ -0,0 +1,51 @@
+/*
+ * 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.springdata22.examples;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.springdata22.repository.IgniteRepository;
+import org.apache.ignite.springdata22.repository.config.EnableIgniteRepositories;
+import org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactoryBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Every {@link IgniteRepository} is bound to a specific Apache Ignite that it communicates to in order to mutate and
+ * read data via Spring Data API. To pass an instance of Apache Ignite cache to an {@link IgniteRepository} it's
+ * required to initialize {@link IgniteRepositoryFactoryBean} with one of the following:
+ * <ul>
+ * <li>{@link Ignite} instance bean</li>
+ * <li>{@link IgniteConfiguration} bean</li>
+ * <li>A path to Ignite's Spring XML configuration named "igniteSpringCfgPath"</li>
+ * <ul/>
+ * In this example the first approach is utilized.
+ */
+@Configuration
+@EnableIgniteRepositories
+public class SpringApplicationConfiguration {
+    /**
+     * Creating Apache Ignite instance bean. A bean will be passed to {@link IgniteRepositoryFactoryBean} to initialize
+     * all Ignite based Spring Data repositories and connect to a cluster.
+     */
+    @Bean
+    public Ignite igniteInstance() {
+        return Ignition.start("modules/spring-data-2.2-ext/examples/config/example-spring-data.xml");
+    }
+}
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/SpringDataExample.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/SpringDataExample.java
new file mode 100644
index 0000000..f585673
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/SpringDataExample.java
@@ -0,0 +1,181 @@
+/*
+ * 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.springdata22.examples;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+import javax.cache.Cache;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.springdata22.examples.model.Person;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.data.domain.PageRequest;
+
+/**
+ * The example demonstrates how to interact with an Apache Ignite cluster by means of Spring Data API.
+ *
+ * Additional cluster nodes can be started with special configuration file which
+ * enables P2P class loading: {@code 'ignite.{sh|bat} modules/spring-data-2.2/examples/config/example-spring-data.xml'}.
+ */
+public class SpringDataExample {
+    /** Spring Application Context. */
+    private static AnnotationConfigApplicationContext ctx;
+
+    /** Ignite Spring Data repository. */
+    private static PersonRepository repo;
+
+    /**
+     * Execute examples involving both approaches to configure Spring Data repository access to an Ignite cluster:
+     *      through Ignite thin client and through Ignite node.
+     * @param args Command line arguments, none required.
+     */
+    public static void main(String[] args) {
+        try (Ignite ignored = startIgniteNode()) {
+            // Ignite node instance is used to configure access to the Ignite cluster.
+            doSpringDataExample(SpringApplicationConfiguration.class);
+
+            // Ignite thin client instance is used to configure access to the Ignite cluster.
+            doSpringDataExample(IgniteClientSpringApplicationConfiguration.class);
+        }
+    }
+
+    /** Starts an Ignite node that simulates an Ignite cluster to which Spring Data repository will perform access. */
+    private static Ignite startIgniteNode() {
+        IgniteConfiguration cfg = new IgniteConfiguration()
+            .setPeerClassLoadingEnabled(true)
+            .setCacheConfiguration(new CacheConfiguration<Long, Person>("PersonCache")
+                .setIndexedTypes(Long.class, Person.class));
+
+        return Ignition.start(cfg);
+    }
+
+    /**
+     * Performs basic Spring Data repository operation.
+     *
+     * @param springAppCfg Class of Spring application configuration that will be used for Spring context initialization.
+     */
+    private static void doSpringDataExample(Class<?> springAppCfg) {
+        igniteSpringDataInit(springAppCfg);
+
+        populateRepository();
+
+        findPersons();
+
+        queryRepository();
+
+        System.out.println("\n>>> Cleaning out the repository...");
+
+        repo.deleteAll();
+
+        System.out.println("\n>>> Repository size: " + repo.count());
+
+        // Destroying the context.
+        ctx.destroy();
+    }
+
+    /**
+     * Initializes Spring Data and Ignite repositories.
+     *
+     * @param springAppCfg Class of Spring application configuration that will be used for Spring context initialization.
+     */
+    private static void igniteSpringDataInit(Class<?> springAppCfg) {
+        ctx = new AnnotationConfigApplicationContext();
+
+        // Explicitly registering Spring configuration.
+        ctx.register(springAppCfg);
+
+        ctx.refresh();
+
+        // Getting a reference to PersonRepository.
+        repo = ctx.getBean(PersonRepository.class);
+    }
+
+    /**
+     * Fills the repository in with sample data.
+     */
+    private static void populateRepository() {
+        TreeMap<Long, Person> persons = new TreeMap<>();
+
+        persons.put(1L, new Person(1L, 2000L, "John", "Smith", 15000, "Worked for Apple"));
+        persons.put(2L, new Person(2L, 2000L, "Brad", "Pitt", 16000, "Worked for Oracle"));
+        persons.put(3L, new Person(3L, 1000L, "Mark", "Tomson", 10000, "Worked for Sun"));
+        persons.put(4L, new Person(4L, 2000L, "Erick", "Smith", 13000, "Worked for Apple"));
+        persons.put(5L, new Person(5L, 1000L, "John", "Rozenberg", 25000, "Worked for RedHat"));
+        persons.put(6L, new Person(6L, 2000L, "Denis", "Won", 35000, "Worked for CBS"));
+        persons.put(7L, new Person(7L, 1000L, "Abdula", "Adis", 45000, "Worked for NBC"));
+        persons.put(8L, new Person(8L, 2000L, "Roman", "Ive", 15000, "Worked for Sun"));
+
+        // Adding data into the repository.
+        repo.save(persons);
+
+        System.out.println("\n>>> Added " + repo.count() + " Persons into the repository.");
+    }
+
+    /**
+     * Gets a list of Persons using standard read operations.
+     */
+    private static void findPersons() {
+        // Getting Person with specific ID.
+        Person person = repo.findById(2L).orElse(null);
+
+        System.out.println("\n>>> Found Person [id=" + 2L + ", val=" + person + "]");
+
+        // Getting a list of Persons.
+
+        ArrayList<Long> ids = new ArrayList<>();
+
+        for (long i = 0; i < 5; i++)
+            ids.add(i);
+
+        Iterator<Person> persons = repo.findAllById(ids).iterator();
+
+        System.out.println("\n>>> Persons list for specific ids: ");
+
+        while (persons.hasNext())
+            System.out.println("   >>>   " + persons.next());
+    }
+
+    /**
+     * Execute advanced queries over the repository.
+     */
+    private static void queryRepository() {
+        System.out.println("\n>>> Persons with name 'John':");
+
+        List<Person> persons = repo.findByFirstName("John");
+
+        for (Person person: persons)
+            System.out.println("   >>>   " + person);
+
+
+        Cache.Entry<Long, Person> topPerson = repo.findTopByLastNameLike("Smith");
+
+        System.out.println("\n>>> Top Person with surname 'Smith': " + topPerson.getValue());
+
+
+        List<Long> ids = repo.selectId(1000L, PageRequest.of(0, 4));
+
+        System.out.println("\n>>> Persons working for organization with ID > 1000: ");
+
+        for (Long id: ids)
+            System.out.println("   >>>   [id=" + id + "]");
+    }
+}
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Address.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Address.java
new file mode 100644
index 0000000..6e00981
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Address.java
@@ -0,0 +1,72 @@
+/*
+ * 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.springdata22.examples.model;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+
+/**
+ * Employee address.
+ * <p>
+ * This class implements {@link Binarylizable} only for example purposes,
+ * in order to show how to customize serialization and deserialization of
+ * binary objects.
+ */
+public class Address implements Binarylizable {
+    /** Street. */
+    private String street;
+
+    /** ZIP code. */
+    private int zip;
+
+    /**
+     * Required for binary deserialization.
+     */
+    public Address() {
+        // No-op.
+    }
+
+    /**
+     * @param street Street.
+     * @param zip ZIP code.
+     */
+    public Address(String street, int zip) {
+        this.street = street;
+        this.zip = zip;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBinary(BinaryWriter writer) throws BinaryObjectException {
+        writer.writeString("street", street);
+        writer.writeInt("zip", zip);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readBinary(BinaryReader reader) throws BinaryObjectException {
+        street = reader.readString("street");
+        zip = reader.readInt("zip");
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Address [street=" + street +
+            ", zip=" + zip + ']';
+    }
+}
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Employee.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Employee.java
new file mode 100644
index 0000000..12a99fa
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Employee.java
@@ -0,0 +1,93 @@
+/*
+ * 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.springdata22.examples.model;
+
+import java.util.Collection;
+
+/**
+ * This class represents employee object.
+ */
+public class Employee {
+    /** Name. */
+    private String name;
+
+    /** Salary. */
+    private long salary;
+
+    /** Address. */
+    private Address addr;
+
+    /** Departments. */
+    private Collection<String> departments;
+
+    /**
+     * Required for binary deserialization.
+     */
+    public Employee() {
+        // No-op.
+    }
+
+    /**
+     * @param name Name.
+     * @param salary Salary.
+     * @param addr Address.
+     * @param departments Departments.
+     */
+    public Employee(String name, long salary,Address addr, Collection<String> departments) {
+        this.name = name;
+        this.salary = salary;
+        this.addr = addr;
+        this.departments = departments;
+    }
+
+    /**
+     * @return Name.
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * @return Salary.
+     */
+    public long salary() {
+        return salary;
+    }
+
+    /**
+     * @return Address.
+     */
+    public Address address() {
+        return addr;
+    }
+
+    /**
+     * @return Departments.
+     */
+    public Collection<String> departments() {
+        return departments;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Employee [name=" + name +
+            ", salary=" + salary +
+            ", address=" + addr +
+            ", departments=" + departments + ']';
+    }
+}
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/EmployeeKey.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/EmployeeKey.java
new file mode 100644
index 0000000..2127eaf
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/EmployeeKey.java
@@ -0,0 +1,93 @@
+/*
+ * 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.springdata22.examples.model;
+
+import org.apache.ignite.cache.affinity.AffinityKeyMapped;
+
+/**
+ * This class represents key for employee object.
+ * <p>
+ * Used in query example to collocate employees
+ * with their organizations.
+ */
+public class EmployeeKey {
+    /** ID. */
+    private int id;
+
+    /** Organization ID. */
+    @AffinityKeyMapped
+    private int organizationId;
+
+    /**
+     * Required for binary deserialization.
+     */
+    public EmployeeKey() {
+        // No-op.
+    }
+
+    /**
+     * @param id ID.
+     * @param organizationId Organization ID.
+     */
+    public EmployeeKey(int id, int organizationId) {
+        this.id = id;
+        this.organizationId = organizationId;
+    }
+
+    /**
+     * @return ID.
+     */
+    public int id() {
+        return id;
+    }
+
+    /**
+     * @return Organization ID.
+     */
+    public int organizationId() {
+        return organizationId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        EmployeeKey key = (EmployeeKey)o;
+
+        return id == key.id && organizationId == key.organizationId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        int res = id;
+
+        res = 31 * res + organizationId;
+
+        return res;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "EmployeeKey [id=" + id +
+            ", organizationId=" + organizationId + ']';
+    }
+}
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Organization.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Organization.java
new file mode 100644
index 0000000..a6ba676
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Organization.java
@@ -0,0 +1,132 @@
+/*
+ * 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.springdata22.examples.model;
+
+import java.sql.Timestamp;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+
+/**
+ * This class represents organization object.
+ */
+public class Organization {
+    /** */
+    private static final AtomicLong ID_GEN = new AtomicLong();
+
+    /** Organization ID (indexed). */
+    @QuerySqlField(index = true)
+    private Long id;
+
+    /** Organization name (indexed). */
+    @QuerySqlField(index = true)
+    private String name;
+
+    /** Address. */
+    private Address addr;
+
+    /** Type. */
+    private OrganizationType type;
+
+    /** Last update time. */
+    private Timestamp lastUpdated;
+
+    /**
+     * Required for binary deserialization.
+     */
+    public Organization() {
+        // No-op.
+    }
+
+    /**
+     * @param name Organization name.
+     */
+    public Organization(String name) {
+        id = ID_GEN.incrementAndGet();
+
+        this.name = name;
+    }
+
+    /**
+     * @param id Organization ID.
+     * @param name Organization name.
+     */
+    public Organization(long id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    /**
+     * @param name Name.
+     * @param addr Address.
+     * @param type Type.
+     * @param lastUpdated Last update time.
+     */
+    public Organization(String name, Address addr, OrganizationType type, Timestamp lastUpdated) {
+        id = ID_GEN.incrementAndGet();
+
+        this.name = name;
+        this.addr = addr;
+        this.type = type;
+
+        this.lastUpdated = lastUpdated;
+    }
+
+    /**
+     * @return Organization ID.
+     */
+    public Long id() {
+        return id;
+    }
+
+    /**
+     * @return Name.
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * @return Address.
+     */
+    public Address address() {
+        return addr;
+    }
+
+    /**
+     * @return Type.
+     */
+    public OrganizationType type() {
+        return type;
+    }
+
+    /**
+     * @return Last update time.
+     */
+    public Timestamp lastUpdated() {
+        return lastUpdated;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Organization [id=" + id +
+            ", name=" + name +
+            ", address=" + addr +
+            ", type=" + type +
+            ", lastUpdated=" + lastUpdated + ']';
+    }
+}
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/OrganizationType.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/OrganizationType.java
new file mode 100644
index 0000000..140ff4e
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/OrganizationType.java
@@ -0,0 +1,32 @@
+/*
+ * 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.springdata22.examples.model;
+
+/**
+ * Organization type enum.
+ */
+public enum OrganizationType {
+    /** Non-profit organization. */
+    NON_PROFIT,
+
+    /** Private organization. */
+    PRIVATE,
+
+    /** Government organization. */
+    GOVERNMENT
+}
diff --git a/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Person.java b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Person.java
new file mode 100644
index 0000000..585c458
--- /dev/null
+++ b/modules/spring-data-2.2-ext/examples/main/java/org/apache/ignite/springdata22/examples/model/Person.java
@@ -0,0 +1,145 @@
+/*
+ * 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.springdata22.examples.model;
+
+import java.io.Serializable;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.cache.affinity.AffinityKey;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.cache.query.annotations.QueryTextField;
+
+/**
+ * Person class.
+ */
+public class Person implements Serializable {
+    /** */
+    private static final AtomicLong ID_GEN = new AtomicLong();
+
+    /** Person ID (indexed). */
+    @QuerySqlField(index = true)
+    public Long id;
+
+    /** Organization ID (indexed). */
+    @QuerySqlField(index = true)
+    public Long orgId;
+
+    /** First name (not-indexed). */
+    @QuerySqlField
+    public String firstName;
+
+    /** Last name (not indexed). */
+    @QuerySqlField
+    public String lastName;
+
+    /** Resume text (create LUCENE-based TEXT index for this field). */
+    @QueryTextField
+    public String resume;
+
+    /** Salary (indexed). */
+    @QuerySqlField(index = true)
+    public double salary;
+
+    /** Custom cache key to guarantee that person is always collocated with its organization. */
+    private transient AffinityKey<Long> key;
+
+    /**
+     * Default constructor.
+     */
+    public Person() {
+        // No-op.
+    }
+
+    /**
+     * Constructs person record.
+     *
+     * @param org       Organization.
+     * @param firstName First name.
+     * @param lastName  Last name.
+     * @param salary    Salary.
+     * @param resume    Resume text.
+     */
+    public Person(Organization org, String firstName, String lastName, double salary, String resume) {
+        // Generate unique ID for this person.
+        id = ID_GEN.incrementAndGet();
+
+        orgId = org.id();
+
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.salary = salary;
+        this.resume = resume;
+    }
+
+    /**
+     * Constructs person record.
+     *
+     * @param id Person ID.
+     * @param orgId Organization ID.
+     * @param firstName First name.
+     * @param lastName Last name.
+     * @param salary    Salary.
+     * @param resume    Resume text.
+     */
+    public Person(Long id, Long orgId, String firstName, String lastName, double salary, String resume) {
+        this.id = id;
+        this.orgId = orgId;
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.salary = salary;
+        this.resume = resume;
+    }
+
+    /**
+     * Constructs person record.
+     *
+     * @param id Person ID.
+     * @param firstName First name.
+     * @param lastName Last name.
+     */
+    public Person(Long id, String firstName, String lastName) {
+        this.id = id;
+
+        this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    /**
+     * Gets cache affinity key. Since in some examples person needs to be collocated with organization, we create
+     * custom affinity key to guarantee this collocation.
+     *
+     * @return Custom affinity key to guarantee that person is always collocated with organization.
+     */
+    public AffinityKey<Long> key() {
+        if (key == null)
+            key = new AffinityKey<>(id, orgId);
+
+        return key;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override public String toString() {
+        return "Person [id=" + id +
+                ", orgId=" + orgId +
+                ", lastName=" + lastName +
+                ", firstName=" + firstName +
+                ", salary=" + salary +
+                ", resume=" + resume + ']';
+    }
+}
diff --git a/modules/spring-data-2.2-ext/pom.xml b/modules/spring-data-2.2-ext/pom.xml
index a7d027e..ba45cd4 100644
--- a/modules/spring-data-2.2-ext/pom.xml
+++ b/modules/spring-data-2.2-ext/pom.xml
@@ -161,4 +161,57 @@
         </dependency>
     </dependencies>
 
+    <profiles>
+        <profile>
+            <id>examples</id>
+
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>build-helper-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <phase>generate-sources</phase>
+                                <goals>
+                                    <goal>add-source</goal>
+                                </goals>
+                                <configuration>
+                                    <sources>
+                                        <source>examples/main/java</source>
+                                    </sources>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+
+            <dependencies>
+                <dependency>
+                    <groupId>org.apache.ignite</groupId>
+                    <artifactId>ignite-core</artifactId>
+                    <version>${ignite.version}</version>
+                </dependency>
+
+                <dependency>
+                    <groupId>org.apache.ignite</groupId>
+                    <artifactId>ignite-indexing</artifactId>
+                    <version>${ignite.version}</version>
+                </dependency>
+
+                <dependency>
+                    <groupId>org.apache.ignite</groupId>
+                    <artifactId>ignite-spring</artifactId>
+                    <version>${ignite.version}</version>
+                </dependency>
+
+                <dependency>
+                    <groupId>org.springframework.data</groupId>
+                    <artifactId>spring-data-commons</artifactId>
+                    <version>${spring.data-2.2.version}</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
 </project>