This is an automated email from the ASF dual-hosted git repository. dmagda pushed a commit to branch IGNITE-7595 in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/IGNITE-7595 by this push: new 7f82c5d ported the Spring-related docs 7f82c5d is described below commit 7f82c5dab5abd7db0323163d13d023d60305cbdc Author: Denis Magda <dma...@gridgain.com> AuthorDate: Thu Sep 24 16:03:19 2020 -0700 ported the Spring-related docs --- docs/_data/toc.yaml | 8 + .../spring/spring-boot.adoc | 196 ++++++++++++++++++ .../spring/spring-caching.adoc | 218 +++++++++++++++++++++ .../spring/spring-data.adoc | 214 ++++++++++++++++++++ 4 files changed, 636 insertions(+) diff --git a/docs/_data/toc.yaml b/docs/_data/toc.yaml index 9489c96..1d32928 100644 --- a/docs/_data/toc.yaml +++ b/docs/_data/toc.yaml @@ -373,6 +373,14 @@ url: sqlline - title: Extensions and Integrations items: + - title: Spring + items: + - title: Spring Boot + url: extensions-and-integrations/spring/spring-boot + - title: Spring Data + url: extensions-and-integrations/spring/spring-data + - title: Spring Caching + url: extensions-and-integrations/spring/spring-caching - title: Ignite for Spark items: - title: Overview diff --git a/docs/_docs/extensions-and-integrations/spring/spring-boot.adoc b/docs/_docs/extensions-and-integrations/spring/spring-boot.adoc new file mode 100644 index 0000000..ee7207f --- /dev/null +++ b/docs/_docs/extensions-and-integrations/spring/spring-boot.adoc @@ -0,0 +1,196 @@ += Apache Ignite With Spring Boot + +== Overview + +https://spring.io/projects/spring-boot[Spring Boot, window="_blank"] is a widely used Java framework that makes it easy +to create stand-alone Spring-based applications. + +Apache Ignite provides two extensions that automate Ignite configuration withing the Spring Boot environment: + +* `ignite-spring-boot-autoconfigure-ext` - autoconfigures ignite server and client nodes within Spring Boot. +* `ignite-spring-boot-thin-client-autoconfigure-ext` - autoconfigures link:thin-clients/java-thin-client[Ignite Thin Client] with Spring Boot. + +== Autoconfiguration of Apache Ignite Servers and Clients + +You need to use `ignite-spring-boot-autoconfigure-ext` extension to autoconfigure Ignite servers or clients (aka. thick clients) with Spring Boot. + +The extension can be added with Maven as follows: + +[tabs] +-- +tab:pom.xml[] +[source,xml] +---- +<dependency> + <groupId>org.apache.ignite</groupId> + <artifactId>ignite-spring-boot-autoconfigure-ext</artifactId> + <version>1.0.0</version> +</dependency> +---- +-- + +Once added, Spring will create an Ignite instance on start-up automatically. + +=== Set Ignite Up Via Spring Boot Configuration + +You can use a regular Spring Boot configuration to set Ignite-specific settings. Use `ignite` as a prefix: + +[tabs] +-- +tab:application.yml[] +[source,yaml] +---- +ignite: + igniteInstanceName: properties-instance-name + communicationSpi: + localPort: 5555 + dataStorageConfiguration: + defaultDataRegionConfiguration: + initialSize: 10485760 #10MB + dataRegionConfigurations: + - name: my-dataregion + initialSize: 104857600 #100MB + cacheConfiguration: + - name: accounts + queryEntities: + - tableName: ACCOUNTS + keyFieldName: ID + keyType: java.lang.Long + valueType: java.lang.Object + fields: + ID: java.lang.Long + amount: java.lang.Double + updateDate: java.util.Date + - name: my-cache2 +---- +-- + +=== Set Ignite Up Programmatically + +There are two ways to configure Ignite programmatically. + +**1. Create IgniteConfiguration Bean** + +Just create a method that returns `IgniteConfiguration` bean that will be used to initialize an Ignite node with the settings you set: + +[tabs] +-- +tab:Java[] +[source,java] +---- +@Bean + public IgniteConfiguration igniteConfiguration() { + // If you provide a whole ClientConfiguration bean then configuration properties will not be used. + IgniteConfiguration cfg = new IgniteConfiguration(); + cfg.setIgniteInstanceName("my-ignite"); + return cfg; + } +---- +-- + +**2. Customize IgniteConfiguration Created With Spring Boot Configuration** + +If you want to customize `IgniteConfiguration` that was initially created with Spring Boot configuration file, then +provide an implementation of `IgniteConfigurer` interface for your application context. + +First, `IgniteConfiguration` will be loaded from the Spring Boot configuration and then that instance will be passed to the configurer for extra settings. + +[tabs] +-- +tab:Java[] +[source,java] +---- +@Bean + public IgniteConfigurer nodeConfigurer() { + return cfg -> { + //Setting some property. + //Other will come from `application.yml` + cfg.setIgniteInstanceName("my-ignite"); + }; + } +---- +-- + +== Autoconfiguration of Apache Ignite Thin Client + +You need to use `ignite-spring-boot-thin-client-autoconfigure-ext` extension to autoconfigure Ignite Thin Client with Spring Boot. + +[tabs] +-- +tab:pom.xml[] +[source,xml] +---- +<dependency> + <groupId>org.apache.ignite</groupId> + <artifactId>ignite-spring-boot-thin-client-autoconfigure-ext</artifactId> + <version>1.0.0</version> +</dependency> +---- +-- + +Once added, Spring will create an instance of Ignite Thin client connection on start-up automatically. + +=== Set Thin Client Up Via Spring Boot Configuration + +You can use a regular Spring Boot configuration to configure `IgniteClient` object. Use `ignite-client` for Ignite-specific settings: + +[tabs] +-- +tab:application.yml[] +[source,yaml] +---- +ignite-client: + addresses: 127.0.0.1:10800 # this is mandatory property! + timeout: 10000 + tcpNoDelay: false +---- +-- + +=== Set Thin Client Up Programmatically + +You can use two ways to configure `IgniteClient` object programmatically. + +**1. Create ClientConfiguration bean** + +Just create a method that returns `ClientConfiguration` bean. `IgniteClient` object will use that bean upon startup: + +[tabs] +-- +tab:Java[] +[source,java] +---- +@Bean + public ClientConfiguration clientConfiguration() { + // If you provide a whole ClientConfiguration bean then configuration properties will not be used. + ClientConfiguration cfg = new ClientConfiguration(); + cfg.setAddresses("127.0.0.1:10800"); + return cfg; + } +---- +-- + +**2. Customize ClientConfiguration Created With Spring Boot Configuration** + +If you want to customize `ClientConfiguration` bean created from the Spring Boot configuration file, then provide an +implementation of `IgniteClientConfigurer` interface in your application context. + +First, `ClientConfiguration` will be loaded from the Spring Boot configuration and then an instance will be passed to the configurer. + +[tabs] +-- +tab:Java[] +[source,java] +---- +@Bean + IgniteClientConfigurer configurer() { + //Setting some property. + //Other will come from `application.yml` + return cfg -> cfg.setSendBufferSize(64*1024); + } +---- +-- + +== Examples + +Refer to several available https://github.com/apache/ignite-extensions/tree/master/modules/spring-boot-autoconfigure-ext/examples/main[examples, windows="_blank"] +for more details. diff --git a/docs/_docs/extensions-and-integrations/spring/spring-caching.adoc b/docs/_docs/extensions-and-integrations/spring/spring-caching.adoc new file mode 100644 index 0000000..292aa16 --- /dev/null +++ b/docs/_docs/extensions-and-integrations/spring/spring-caching.adoc @@ -0,0 +1,218 @@ += Using Spring Cache With Apache Ignite + +== Overview + +Ignite is shipped with `SpringCacheManager` - an implementation of http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html[Spring Cache Abstraction, window=_blank]. +It provides an annotation-based way to enable caching for Java methods so that the result of a method execution is stored +in an Ignite cache. Later, if the same method is called with the same set of parameter values, the result will be retrieved +from the cache instead of actually executing the method. + +== Enabling Ignite for Spring Caching + +Only two simple steps are required to plug in an Ignite cache into your Spring-based application: + +* Start an Ignite node with proper configuration in embedded mode (i.e., in the same JVM where the application is running). It can already have predefined caches, but it's not required - caches will be created automatically on first access if needed. +* Configure `SpringCacheManager` as the cache manager in the Spring application context. + +The embedded node can be started by `SpringCacheManager` itself. In this case you will need to provide a path to either +the Ignite configuration XML file or `IgniteConfiguration` bean via `configurationPath` or `configuration` +properties respectively (see examples below). Note that setting both is illegal and results in `IllegalArgumentException`. + +[tabs] +-- +tab:configuration path[] +[source,xml] +---- +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:cache="http://www.springframework.org/schema/cache" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/cache + http://www.springframework.org/schema/cache/spring-cache.xsd"> + <!-- Provide configuration file path. --> + <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager"> + <property name="configurationPath" value="examples/config/spring-cache.xml"/> + </bean> + + <!-- Enable annotation-driven caching. --> + <cache:annotation-driven/> +</beans> +---- +tab:configuration bean[] +[source,xml] +---- +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:cache="http://www.springframework.org/schema/cache" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/cache + http://www.springframework.org/schema/cache/spring-cache.xsd"> + <-- Provide configuration bean. --> + <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager"> + <property name="configuration"> + <bean class="org.apache.ignite.configuration.IgniteConfiguration"> + ... + </bean> + </property> + </bean> + + <-- Enable annotation-driven caching. --> + <cache:annotation-driven/> +</beans> +---- + +-- + +It's possible that you already have an Ignite node running when the cache manager is initialized (e.g., it was started using +`ServletContextListenerStartup`). In this case you should simply provide the grid name via `gridName` property. +Note that if you don't set the grid name as well, the cache manager will try to use the default Ignite instance +(the one with the `null` name). Here is an example: + +[tabs] +-- +tab:Using an already started Ignite instance[] +[source,xml] +---- +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:cache="http://www.springframework.org/schema/cache" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/cache + http://www.springframework.org/schema/cache/spring-cache.xsd"> + <!-- Provide grid name. --> + <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager"> + <property name="gridName" value="myGrid"/> + </bean> + + <!-- Enable annotation-driven caching. --> + <cache:annotation-driven/> +</beans> +---- +-- + +[NOTE] +==== +[discrete] +Keep in mind that the node started inside your application is an entry point to the whole topology it connects to. +You can start as many remote standalone nodes as you need and all these nodes will participate in caching the data. +==== + +== Dynamic Caches + +While you can have all required caches predefined in Ignite configuration, it's not required. If Spring wants to use a +cache that doesn't exist, the `SpringCacheManager` will automatically create it. + +If otherwise not specified, a new cache will be created will all defaults. To customize it, you can provide a configuration +template via `dynamicCacheConfiguration` property. For example, if you want to use `REPLICATED` caches instead of +`PARTITIONED`, you should configure `SpringCacheManager` like this: + +[tabs] +-- +tab:Dynamic cache configuration[] +[source,xml] +---- +<bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager"> + ... + + <property name="dynamicCacheConfiguration"> + <bean class="org.apache.ignite.configuration.CacheConfiguration"> + <property name="cacheMode" value="REPLICATED"/> + </bean> + </property> +</bean> +---- +-- + +You can also utilize near caches on client side. To achieve this, simply provide near cache configuration via the +`dynamicNearCacheConfiguration` property. By default, near cache is not created. Here is an example: + +[tabs] +-- +tab:Dynamic near cache configuration[] +[source,xml] +---- +<bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager"> + ... + + <property name="dynamicNearCacheConfiguration"> + <bean class="org.apache.ignite.configuration.NearCacheConfiguration"> + <property name="nearStartSize" value="1000"/> + </bean> + </property> +</bean> +---- +-- + +== Example + +Once you have added `SpringCacheManager` to your Spring application context, you can enable caching for any Java method by simply attaching an annotation to it. + +Usually, you would use caching for heavy operations, like database access. For example, let's assume you have a DAO class with +`averageSalary(...)` method that calculates the average salary of all employees in an organization. You can use `@Cacheable` +annotation to enable caching for this method: + +[tabs] +-- +tab:Java[] +[source,java] +---- +private JdbcTemplate jdbc; + +@Cacheable("averageSalary") +public long averageSalary(int organizationId) { + String sql = + "SELECT AVG(e.salary) " + + "FROM Employee e " + + "WHERE e.organizationId = ?"; + + return jdbc.queryForObject(sql, Long.class, organizationId); +} +---- +-- + +When this method is called for the first time, `SpringCacheManager` will automatically create a `averageSalary` cache. +It will also lookup the pre-calculated average value in this cache and return it right away if it's there. If the average +for this organization is not calculated yet, the method will be called and the result will be stored in cache. So next +time you request the average salary for this organization, you will not need to query the database. + +If the salary of one of the employees is changed, you may want to remove the average value for the organization this +employee belongs to, because otherwise the `averageSalary(...)` method will return obsolete cached result. This can be +achieved by attaching `@CacheEvict` annotation to a method that updates employee's salary: + +[tabs] +-- +tab:Java[] +[source,java] +---- +private JdbcTemplate jdbc; + +@CacheEvict(value = "averageSalary", key = "#e.organizationId") +public void updateSalary(Employee e) { + String sql = + "UPDATE Employee " + + "SET salary = ? " + + "WHERE id = ?"; + + jdbc.update(sql, e.getSalary(), e.getId()); +} +---- +-- + +After this method is called, average value for the provided employee's organization will be evicted from the `averageSalary` cache. +This will force `averageSalary(...)` to recalculate the value next time it's called. + +[NOTE] +==== +[discrete] +Note that this method receives employee as a parameter, while average values are saved in cache by `organizationID`. +To explicitly specify what is used as a cache key, we used key parameter of the annotation and Spring Expression Language. + +The `#e.organizationId` expression means that we need to extract the value of `organizationId` property from `e` variable. +Essentially, `getOrganizationId()` method will be called on provided employee object and the returned value will be used as the cache key. +==== diff --git a/docs/_docs/extensions-and-integrations/spring/spring-data.adoc b/docs/_docs/extensions-and-integrations/spring/spring-data.adoc new file mode 100644 index 0000000..3b1a106 --- /dev/null +++ b/docs/_docs/extensions-and-integrations/spring/spring-data.adoc @@ -0,0 +1,214 @@ += 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</artifactId> + <version>{ignite.version}</version> +</dependency> +---- +-- + +== 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:{githubUrl}/examples/src/main/java/org/apache/ignite/examples/springdata[GitHub, window=_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]. +