// 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.
= Apache Ignite With Spring Data

== Overview

Spring Data Framework provides a unified and widely used API that allows abstracting an underlying data storage from the
application layer. Spring Data helps you avoid locking to a specific database vendor, making it easy to switch from one
database to another with minimal efforts. Apache Ignite integrates with Spring Data by implementing Spring Data `CrudRepository` interface.

== Maven Configuration

The easiest way to start working with Apache Ignite's Spring Data repository is by adding the following Maven dependency
to an application's `pom.xml` file:

[tabs]
--
tab:pom.xml[]
[source,xml]
----
<dependency>
    <groupId>org.apache.ignite</groupId>
    <artifactId>ignite-spring-data_2.2</artifactId>
    <version>{ignite.version}</version>
</dependency>
----
--

[NOTE]
====
If your Spring Data version is earlier than Spring Data 2.2 then set `ignite-spring-data_2.0`
or `ignite-spring-data` as an `artifactId` in the pom.xml configuration.
====

== Apache Ignite Repository

Apache Ignite introduces a special `IgniteRepository` interface that extends default `CrudRepository`. This interface
should be extended by all custom Spring Data repositories that wish to store and query data located in an Apache Ignite cluster.

For instance, let's create the first custom repository named `PersonRepository`:

[tabs]
--
tab:Java[]
[source,java]
----
@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);
}
----
--

* `@RepositoryConfig` annotation should be specified to map a repository to a distributed cache. In the above example, `PersonRepository` is mapped to `PersonCache`.
* Signatures of custom methods like `findByFirstName(name)` and `findTopByLastNameLike(name)` will be automatically processed and turned
into SQL queries when methods get executed. In addition, `@Query(queryString)` annotation can be used if a concrete​ SQL
query needs to be executed as a result of a method call.


[CAUTION]
====
[discrete]
=== Unsupported CRUD Operations

Some operations of CrudRepository interface are not currently supported. These are the operations that do not require providing the key as a parameter:

* save(S entity)
* save(Iterable<S> entities)
* delete(T entity)
* delete(Iterable<? extends T> entities)

Instead of these operations you can use Ignite specific counterparts available via `IgniteRepository` interface:

* save(ID key, S entity)
* save(Map<ID, S> entities)
* deleteAll(Iterable<ID> ids)

====

== Spring Data and Apache Ignite Configuration


To enable Apache Ignite backed repositories in Spring Data, mark an application configuration with `@EnableIgniteRepositories`
annotation, as shown below:

[tabs]
--
tab:Java[]
[source,java]
----
@Configuration
@EnableIgniteRepositories
public class SpringAppCfg {
    /**
     * Creating Apache Ignite instance bean. A bean will be passed
     * to IgniteRepositoryFactoryBean to initialize all Ignite based Spring Data repositories and connect to a cluster.
     */
    @Bean
    public Ignite igniteInstance() {
        IgniteConfiguration cfg = new IgniteConfiguration();

        // Setting some custom name for the node.
        cfg.setIgniteInstanceName("springDataNode");

        // Enabling peer-class loading feature.
        cfg.setPeerClassLoadingEnabled(true);

        // Defining and creating a new cache to be used by Ignite Spring Data
        // repository.
        CacheConfiguration ccfg = new CacheConfiguration("PersonCache");

        // Setting SQL schema for the cache.
        ccfg.setIndexedTypes(Long.class, Person.class);

        cfg.setCacheConfiguration(ccfg);

        return Ignition.start(cfg);
    }
}
----
--

The configuration has to instantiate Apache Ignite bean (node) that will be passed to `IgniteRepositoryFactoryBean`
and will be used by all the Apache Ignite repositories in order to connect to the cluster.

In the example above, the bean is initialized directly by the application and is named `igniteInstance`.
Alternatively, the following beans can be registered in your configuration and an Apache Ignite node will be started automatically:

* `IgniteConfiguration` object named as `igniteCfg` bean.
* A path to Apache Ignite's Spring XML configuration named `igniteSpringCfgPath`.

== Using Apache Ignite Repositories

Once all the configurations and repositories are ready to be used, you can register the configuration in an application context and get a reference to the repository.
The following example shows how to register `SpringAppCfg` - our sample configuration from the section above - in an application context and get a reference to `PersonRepository`:

[tabs]
--
tab:Java[]
[source,java]
----
ctx = new AnnotationConfigApplicationContext();

// Explicitly registering Spring configuration.
ctx.register(SpringAppCfg.class);

ctx.refresh();

// Getting a reference to PersonRepository.
repo = ctx.getBean(PersonRepository.class);
----
--

Now, you can put data in Ignite using Spring Data API:

[tabs]
--
tab:Java[]
[source,java]
----
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"));

// Adding data into the repository.
repo.save(persons);
----
--

To query the data, we can use basic CRUD operations or methods that will be automatically turned into Apache Ignite SQL queries:

[tabs]
--
tab:Java[]
[source,java]
----
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());
----
--

== Example

The complete example is available on link: https://github.com/apache/ignite-extensions/tree/master/modules/spring-data-2.0-ext/examples/main[GitHub, windows="_blank"]

== Tutorial

Follow the tutorial that shows how to build a https://www.gridgain.com/docs/tutorials/spring/spring-ignite-tutorial[RESTful web service with Apache Ignite and Spring Data, window=_blank].

