[关闭]
@liyuj 2018-01-27T16:48:54.000000Z 字数 9079 阅读 4561

Apache-Ignite-2.3.0-中文开发手册

5.Spring

5.1.Spring缓存

5.1.1.摘要

Ignite提供了一个SpringCacheManager-一个Spring缓存抽象的实现。他提供了基于注解的方式来启用Java方法的缓存,这样方法的执行结果就会存储在Ignite缓存中。如果之后同一个方法通过同样的参数集被调用,结果会直接从缓存中获得而不是实际执行这个方法。

Spring缓存抽象文档
关于如何使用Spring缓存抽象的更多信息,包括可用的注解,可以参照这个文档页面:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html.

5.1.2.如何启用缓存

只需要两个简单的步骤就可以将Ignite缓存嵌入基于Spring的应用:

嵌入式节点可以通过SpringCacheManager自己启动,这种情况下需要分别通过configurationPath或者configuration属性提供一个Ignite配置文件的路径或者IgniteConfigurationBean(看下面的示例)。注意同时设置两个属性是非法的,会抛出IllegalArgumentException
配置路径:

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:cache="http://www.springframework.org/schema/cache"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/cache
  8. http://www.springframework.org/schema/cache/spring-cache.xsd">
  9. <!-- Provide configuration file path. -->
  10. <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
  11. <property name="configurationPath" value="examples/config/spring-cache.xml"/>
  12. </bean>
  13. <!-- Enable annotation-driven caching. -->
  14. <cache:annotation-driven/>
  15. </beans>

配置Bean:

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:cache="http://www.springframework.org/schema/cache"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/cache
  8. http://www.springframework.org/schema/cache/spring-cache.xsd">
  9. <-- Provide configuration bean. -->
  10. <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
  11. <property name="configuration">
  12. <bean class="org.apache.ignite.configuration.IgniteConfiguration">
  13. ...
  14. </bean>
  15. </property>
  16. </bean>
  17. <-- Enable annotation-driven caching. -->
  18. <cache:annotation-driven/>
  19. </beans>

当缓存管理器初始化时也可能已经有一个Ignite节点正在运行(比如已经通过ServletContextListenerStartup启动了)。这时只需要简单地通过gridName属性提供网格名字就可以了。注意如果不设置网格名字,缓存管理器会试图使用默认的Ignite实例(名字为null的),下面是一个示例:
使用已启动的Ignite实例:

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:cache="http://www.springframework.org/schema/cache"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/cache
  8. http://www.springframework.org/schema/cache/spring-cache.xsd">
  9. <!-- Provide grid name. -->
  10. <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
  11. <property name="gridName" value="myGrid"/>
  12. </bean>
  13. <!-- Enable annotation-driven caching. -->
  14. <cache:annotation-driven/>
  15. </beans>

远程节点
注意应用内部启动的节点只是希望连接的网络的一个入口,可以根据需要通过Ignite发行版提供的bin/ignite.{sh|bat}脚本启动尽可能多的远程独立节点,所有这些节点都会参与缓存数据。

5.1.3.动态缓存

虽然通过Ignite配置文件可以获得所有必要的缓存,但是这不是必要的。如果Spring要使用一个不存在的缓存时,SpringCacheManager会自动创建它。
如果不指定,会使用默认值创建一个新的缓存。要定制的话,可以通过dynamicCacheConfiguration属性提供一个配置模板,比如,如果希望使用复制缓存而不是分区缓存,可以像下面这样配置SpringCacheManager:

  1. <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
  2. ...
  3. <property name="dynamicCacheConfiguration">
  4. <bean class="org.apache.ignite.configuration.CacheConfiguration">
  5. <property name="cacheMode" value="REPLICATED"/>
  6. </bean>
  7. </property>
  8. </bean>

也可以在客户端侧使用近缓存,要做到这一点只需要简单地通过dynamicNearCacheConfiguration属性提供一个近缓存配置即可。近缓存默认是不启用的,下面是一个例子:

  1. <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
  2. ...
  3. <property name="dynamicNearCacheConfiguration">
  4. <bean class="org.apache.ignite.configuration.NearCacheConfiguration">
  5. <property name="nearStartSize" value="1000"/>
  6. </bean>
  7. </property>
  8. </bean>

5.1.4.示例

如果在Spring应用上下文中已经加入了SpringCacheManager,就可以通过简单地加上注解为任意的java方法启用缓存。
通常为很重的操作使用缓存,比如数据库访问。比如,假设有个Dao类有一个averageSalary(...)方法,他计算一个组织内的所有雇员的平均工资,那么可以通过@Cacheable注解来开启这个方法的缓存。

  1. private JdbcTemplate jdbc;
  2. @Cacheable("averageSalary")
  3. public long averageSalary(int organizationId) {
  4. String sql =
  5. "SELECT AVG(e.salary) " +
  6. "FROM Employee e " +
  7. "WHERE e.organizationId = ?";
  8. return jdbc.queryForObject(sql, Long.class, organizationId);
  9. }

当这个方法第一次被调用时,SpringCacheManager会自动创建一个averageSalary缓存,他也会在缓存中查找事先计算好的平均值然后如果存在的话就会直接返回,如果这个组织的平均值还没有被计算过,那么这个方法就会被调用然后将结果保存在缓存中,因此下一次请求这个组织的平均值,就不需要访问数据库了。

缓存键
因为organizationId是唯一的方法参数,所以他会自动作为缓存键。

如果一个雇员的工资发生变化,可能希望从缓存中删除这个雇员所属组织的平均值,否则averageSalary(...)方法会返回过时的缓存结果。这个可以通过将@CacheEvict注解加到一个方法上来更新雇员的工资:

  1. private JdbcTemplate jdbc;
  2. @CacheEvict(value = "averageSalary", key = "#e.organizationId")
  3. public void updateSalary(Employee e) {
  4. String sql =
  5. "UPDATE Employee " +
  6. "SET salary = ? " +
  7. "WHERE id = ?";
  8. jdbc.update(sql, e.getSalary(), e.getId());
  9. }

在这个方法被调用之后,该雇员所属组织的平均值就会被从averageSalary缓存中踢出,这会强迫averageSalary(...)方法在下次调用时重新计算。

Spring表达式语言(SpEL)
注意这个方法是以雇员为参数的,而平均值是通过组织的Id将平均值存储在缓存中的。为了明确地指定什么作为缓存键,可以使用注解的key参数和Spring表达式语言
#e.organizationId表达式的意思是从e变量中获取organizationId属性的值。本质上会在提供的雇员对象上调用getOrganizationId()方法,以及将返回的值作为缓存键。

5.2.Spring Data

5.2.1.摘要

Spring Data框架提供了一套统一并且广泛使用的API,它从应用层抽象了底层的数据存储,Spring Data有助于避免锁定到特定的数据库厂商,通过很小的代价就可以从一个数据库切换到另一个。
Ignite实现了Spring Data的CrudRepository接口,它不仅仅支持基本的CRUD操作,还支持通过统一的Spring Data API访问Ignite的SQL网格。

5.2.2.Maven配置

开始使用Ignite的Spring Data库的最简单方式就是将下面的Maven依赖加入应用的pom.xml文件:

  1. <dependency>
  2. <groupId>org.apache.ignite</groupId>
  3. <artifactId>ignite-spring-data</artifactId>
  4. <version>{ignite.version}</version>
  5. </dependency>

Ignite版本
Ignite从2.0版本开始支持Spring Data,因此需要使用2.0.0及之后的版本。

5.2.3.IgniteRepository

Ignite引入了一个特定的IgniteRepository接口,扩展了默认的CrudRepository,这个接口可以被所有希望从Ignite集群中存储和查询数据的自定义Spring Data Repository继承。
比如,创建一个名为PersonRepository的自定义Repository:

  1. @RepositoryConfig(cacheName = "PersonCache")
  2. public interface PersonRepository extends IgniteRepository<Person, Long> {
  3. /**
  4. * Gets all the persons with the given name.
  5. * @param name Person name.
  6. * @return A list of Persons with the given first name.
  7. */
  8. public List<Person> findByFirstName(String name);
  9. /**
  10. * Returns top Person with the specified surname.
  11. * @param name Person surname.
  12. * @return Person that satisfy the query.
  13. */
  14. public Cache.Entry<Long, Person> findTopByLastNameLike(String name);
  15. /**
  16. * Getting ids of all the Person satisfying the custom query from {@link Query} annotation.
  17. *
  18. * @param orgId Query parameter.
  19. * @param pageable Pageable interface.
  20. * @return A list of Persons' ids.
  21. */
  22. @Query("SELECT id FROM Person WHERE orgId > ?")
  23. public List<Long> selectId(long orgId, Pageable pageable);
  24. }

@RepositoryConfig注解需要指定,他会将Repository映射到一个分布式缓存,在上面的示例中,PersonRepository映射到了PersonCache
自定义方法(比如findByFirstName(name)以及findTopByLastNameLike(name))的签名会被自动处理,在该方法被调用时会被转成对应的SQL查询。另外,如果需要执行明确的SQL查询作为方法调用的结果,也可以使用@Query(queryString)注解。

不支持的CRUD操作
CrudRepository接口的部分操作目前还不支持。这些操作是不需要提供键作为参数的:
1.save(S entity)
2.save(Iterable<S> entities)
3.delete(T entity)
4.delete(Iterable<? extends T> entities)
这些操作可以使用IgniteRepository接口中提供的功能相当的函数就行替代:
1.save(ID key, S entity)
2.save(Map<ID, S> entities)
3.deleteAll(Iterable<ID> ids)

5.2.4.Spring Data和Ignite配置

要在Spring Data中启用面向Ignite的Repository,需要在应用的配置上添加@EnableIgniteRepositories注解,如下所示:

  1. @Configuration
  2. @EnableIgniteRepositories
  3. public class SpringAppCfg {
  4. /**
  5. * Creating Apache Ignite instance bean. A bean will be passed
  6. * to IgniteRepositoryFactoryBean to initialize all Ignite based Spring Data * repositories and connect to a cluster.
  7. */
  8. @Bean
  9. public Ignite igniteInstance() {
  10. IgniteConfiguration cfg = new IgniteConfiguration();
  11. // Setting some custom name for the node.
  12. cfg.setIgniteInstanceName("springDataNode");
  13. // Enabling peer-class loading feature.
  14. cfg.setPeerClassLoadingEnabled(true);
  15. // Defining and creating a new cache to be used by Ignite Spring Data
  16. // repository.
  17. CacheConfiguration ccfg = new CacheConfiguration("PersonCache");
  18. // Setting SQL schema for the cache.
  19. ccfg.setIndexedTypes(Long.class, Person.class);
  20. cfg.setCacheConfiguration(ccfg);
  21. return Ignition.start(cfg);
  22. }
  23. }

这个配置会实例化传入IgniteRepositoryFactoryBean的Ignite bean(节点),然后用于所有需要接入Ignite集群的Ignite Repository。
在上例中,应用会直接实例化该bean,然后命名为igniteInstance,另外,配置也可以注册下面的bean,然后自动地启动一个Ignite节点。

5.2.5.使用IgniteRepository

所有的配置和Repository准备好之后,就可以在应用的上下文中注册配置以及获取Repository的引用。
下面的示例代码就会展示如何在应用的上下文中注册SpringAppCfg(上面章节的示例配置),然后获取PersonRepository的引用:

  1. ctx = new AnnotationConfigApplicationContext();
  2. // Explicitly registering Spring configuration.
  3. ctx.register(SpringAppCfg.class);
  4. ctx.refresh();
  5. // Getting a reference to PersonRepository.
  6. repo = ctx.getBean(PersonRepository.class);

下面,就可以使用Spring Data的API将数据加入分布式缓存:

  1. TreeMap<Long, Person> persons = new TreeMap<>();
  2. persons.put(1L, new Person(1L, 2000L, "John", "Smith", 15000, "Worked for Apple"));
  3. persons.put(2L, new Person(2L, 2000L, "Brad", "Pitt", 16000, "Worked for Oracle"));
  4. persons.put(3L, new Person(3L, 1000L, "Mark", "Tomson", 10000, "Worked for Sun"));
  5. // Adding data into the repository.
  6. repo.save(persons);

如果要查询数据,可以使用基本的CRUD操作或者方法,它们会自动地转换为Ignite的SQL查询。

  1. List<Person> persons = repo.findByFirstName("John");
  2. for (Person person: persons)
  3. System.out.println(" >>> " + person);
  4. Cache.Entry<Long, Person> topPerson = repo.findTopByLastNameLike("Smith");
  5. System.out.println("\n>>> Top Person with surname 'Smith': " +
  6. topPerson.getValue());

5.2.6.示例

GitHub上有完整的示例,也可以在Ignite发行版的examples文件夹中找到。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注