原标题:Spring认证中国教育管理中心-Apache Geode 的 Spring 数据教程二十五(Spring中国教育管理中心)
12. Apache Lucene 集成
Apache Geode与Apache Lucene集成,让您可以使用 Lucene 查询索引和搜索存储在 Apache Geode 中的数据。基于搜索的查询还包括翻阅查询结果的能力。
此外,Spring Data for Apache Geode 添加了对基于 Spring Data Commons 投影基础设施的查询投影的支持。此功能使查询结果可以根据应用程序的需要投影到一流的应用程序域类型中。
Lucene的Index任何Lucene的基于搜索的查询可以运行之前必须创建。ALuceneIndex 可以在 Spring (Data for Apache Geode) XML 配置中创建,如下所示:
代码语言:javascript复制<gfe:lucene-index id="IndexOne" fields="fieldOne, fieldTwo" region-path="/Example"/>
此外,Apache Lucene 允许为 每个字段指定分析器,并且可以按照以下示例进行配置:
代码语言:javascript复制<gfe:lucene-index id="IndexTwo" lucene-service-ref="luceneService" region-path="/AnotherExample">
<gfe:field-analyzers>
<map>
<entry key="fieldOne">
<bean class="example.AnalyzerOne"/>
</entry>
<entry key="fieldTwo">
<bean class="example.AnalyzerTwo"/>
</entry>
</map>
</gfe:field-analyzers>
</gfe:lucene-index>
的Map可以被指定为一个顶级bean定义,并通过使用所引用的ref在嵌套属性<gfe:field-analyzers>元素,如下: <gfe-field-analyzers ref=" refToTopLevelMapBeanDefinition"/>。
Spring Data for Apache Geode 的LuceneIndexFactoryBeanAPI 和 SDG 的 XML 命名空间也允许 org.apache.geode.cache.lucene.LuceneSerializer 在创建LuceneIndex. 将LuceneSerializer允许您配置对象将转换为Lucene的文件进行索引当对象被索引的方式。
下面的示例演示如何将添加LuceneSerializer到LuceneIndex:
代码语言:javascript复制<bean id="MyLuceneSerializer" class="example.CustomLuceneSerializer"/>
<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
<gfe:lucene-serializer ref="MyLuceneSerializer">
</gfe:lucene-index>
您也可以将 指定LuceneSerializer为匿名的嵌套 bean 定义,如下所示:
代码语言:javascript复制<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
<gfe:lucene-serializer>
<bean class="example.CustomLuceneSerializer"/>
</gfe:lucene-serializer>
</gfe:lucene-index>
或者,您可以LuceneIndex在 Spring Java 配置中的@Configuration类中声明或定义一个,如以下示例所示:
代码语言:javascript复制@Bean(name = "Books")
@DependsOn("bookTitleIndex")
PartitionedRegionFactoryBean<Long, Book> booksRegion(GemFireCache gemfireCache) {
PartitionedRegionFactoryBean<Long, Book> peopleRegion =
new PartitionedRegionFactoryBean<>();
peopleRegion.setCache(gemfireCache);
peopleRegion.setClose(false);
peopleRegion.setPersistent(false);
return peopleRegion;
}
@Bean
LuceneIndexFactoryBean bookTitleIndex(GemFireCache gemFireCache,
LuceneSerializer luceneSerializer) {
LuceneIndexFactoryBean luceneIndex = new LuceneIndexFactoryBean();
luceneIndex.setCache(gemFireCache);
luceneIndex.setFields("title");
luceneIndex.setLuceneSerializer(luceneSerializer);
luceneIndex.setRegionPath("/Books");
return luceneIndex;
}
@Bean
CustomLuceneSerializer myLuceneSerialier() {
return new CustomeLuceneSerializer();
}
Apache Geode 的 Apache Lucene 集成和支持存在一些限制。
首先,LuceneIndex只能在 Apache Geode PARTITIONRegion上创建。
其次,所有LuceneIndexes必须在应用的区域之前创建LuceneIndex。
为了帮助确保LuceneIndexes在 Spring 容器中定义的所有声明都是在它们应用的区域之前创建的,SDG 包括 org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor. 您可以BeanFactoryPostProcessor 使用.xml在 XML 配置中注册这个 Spring <bean class="org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor"/>。在o.s.d.g.config.support.LuceneIndexRegionBeanFactoryPostProcessor使用SDG XML配置时,仅可使用。BeanFactoryPostProcessors可以在此处找到有关 Spring 的更多详细信息。
这些 Apache Geode 限制可能不适用于未来版本,这就是为什么 SDG LuceneIndexFactoryBeanAPI 也直接引用区域,而不仅仅是区域路径。
当您想LuceneIndex在应用程序生命周期的稍后时间点根据需求在现有区域上定义带有数据的区域时,这是更理想的选择。在可能的情况下,SDG 努力坚持强类型对象。但是,目前您必须使用该regionPath属性来指定应用的区域LuceneIndex。
此外,在前面的示例中,请注意 Spring@DependsOn对BooksRegion bean 定义的注释的存在。这会创建从BooksRegion bean 到bookTitleIndex LuceneIndexbean 定义的依赖关系,确保LuceneIndex在它应用的 Region 之前创建它。
既然有了LuceneIndex,我们就可以执行基于 Lucene 的数据访问操作,例如查询。
12.1.Lucene 模板数据访问器
Spring Data for Apache Geode 为 Lucene 数据访问操作提供了两个主要模板,具体取决于您的应用程序准备处理的级别有多低。
该LuceneOperations接口使用 Apache Geode Lucene types定义查询操作 ,其定义在以下接口定义中:
代码语言:javascript复制public interface LuceneOperations {
<K, V> List<LuceneResultStruct<K, V>> query(String query, String defaultField [, int resultLimit]
, String... projectionFields);
<K, V> PageableLuceneQueryResults<K, V> query(String query, String defaultField,
int resultLimit, int pageSize, String... projectionFields);
<K, V> List<LuceneResultStruct<K, V>> query(LuceneQueryProvider queryProvider [, int resultLimit]
, String... projectionFields);
<K, V> PageableLuceneQueryResults<K, V> query(LuceneQueryProvider queryProvider,
int resultLimit, int pageSize, String... projectionFields);
<K> Collection<K> queryForKeys(String query, String defaultField [, int resultLimit]);
<K> Collection<K> queryForKeys(LuceneQueryProvider queryProvider [, int resultLimit]);
<V> Collection<V> queryForValues(String query, String defaultField [, int resultLimit]);
<V> Collection<V> queryForValues(LuceneQueryProvider queryProvider [, int resultLimit]);
}
在操作LuceneOperations界面匹配由Apache的Geode的提供的操作 LuceneQuery接口。但是,SDG 具有将专有 Apache Geode 或 Apache LuceneExceptions 转换为 Spring 高度一致且富有表现力的 DAO 异常层次结构的附加价值,特别是当许多现代数据访问操作涉及多个存储或存储库时。
此外,SDG 的LuceneOperations接口可以保护您的应用程序免受底层 Apache Geode 或 Apache Lucene API 引入的接口破坏性更改的影响。
但是,提供仅使用 Apache Geode 和 Apache Lucene 数据类型(例如 Apache Geode 的LuceneResultStruct)的 Lucene 数据访问对象 (DAO) 将是可悲的。因此,SDG 为您提供了 ProjectingLuceneOperations解决这些重要应用问题的 接口。以下清单显示了ProjectingLuceneOperations接口定义:
代码语言:javascript复制public interface ProjectingLuceneOperations {
<T> List<T> query(String query, String defaultField [, int resultLimit], Class<T> projectionType);
<T> Page<T> query(String query, String defaultField, int resultLimit, int pageSize, Class<T> projectionType);
<T> List<T> query(LuceneQueryProvider queryProvider [, int resultLimit], Class<T> projectionType);
<T> Page<T> query(LuceneQueryProvider queryProvider, int resultLimit, int pageSize, Class<T> projectionType);
}
该 ProjectingLuceneOperations接口主要使用应用程序域对象类型,让您可以使用应用程序数据。该query方法的变体接受一个投影类型和模板可以使用Spring的数据共享基础设施的投影应用的查询结果给定的投影类型的实例。
此外,该模板将分页的 Lucene 查询结果包装在 Spring Data CommonsPage抽象的实例中 。相同的投影逻辑仍然可以应用于页面中的结果,并在访问集合中的每个页面时延迟投影。
举例来说,假设您有一个表示 a 的类Person,如下所示:
代码语言:javascript复制class Person {
Gender gender;
LocalDate birthDate;
String firstName;
String lastName;
...
String getName() {
return String.format("%1$s %2$s", getFirstName(), getLastName());
}
}
此外,Customers根据您的应用程序视图,您可能有一个单独的界面来将人员表示为,如下所示:
代码语言:javascript复制interface Customer {
String getName()
}
如果我定义以下内容LuceneIndex......
代码语言:javascript复制@Bean
LuceneIndexFactoryBean personLastNameIndex(GemFireCache gemfireCache) {
LuceneIndexFactoryBean personLastNameIndex =
new LuceneIndexFactoryBean();
personLastNameIndex.setCache(gemfireCache);
personLastNameIndex.setFields("lastName");
personLastNameIndex.setRegionPath("/People");
return personLastNameIndex;
}
然后您可以将人作为Person对象进行查询,如下所示:
代码语言:javascript复制List<Person> people = luceneTemplate.query("lastName: D*", "lastName", Person.class);
或者,您可以查询 a Pageof type Customer,如下所示:
代码语言:javascript复制Page<Customer> customers = luceneTemplate.query("lastName: D*", "lastName", 100, 20, Customer.class);
该Page则可以用来获取结果的单个页面,如下所示:
代码语言:javascript复制List<Customer> firstPage = customers.getContent();
方便的是,Spring Data CommonsPage接口还实现了java.lang.Iterable<T>,从而可以轻松地对内容进行迭代。
Spring Data Commons Projection 基础结构的唯一限制是投影类型必须是接口。但是,可以扩展提供的 SDC Projection 基础结构并提供ProjectionFactory 使用CGLIB生成代理类作为投影实体的自定义 。
您可以使用在 Lucene 模板上setProjectionFactory(:ProjectionFactory)设置自定义ProjectionFactory。
12.2.注释配置支持
最后,Spring Data for Apache Geode 为LuceneIndexes.
最终,SDG Lucene 支持将进入 Apache Geode 的 Repository 基础设施扩展,以便 Lucene 查询可以表示为应用程序Repository接口上的方法,与OQL 支持今天的工作方式大致相同。
但是,与此同时,如果您想方便地表达LuceneIndexes,您可以直接在您的应用程序域对象上进行,如下例所示:
代码语言:javascript复制@PartitionRegion("People")
class Person {
Gender gender;
@Index
LocalDate birthDate;
String firstName;
@LuceneIndex;
String lastName;
...
}
要启用此功能,您必须使用 SDG 的注解配置支持,特别是 @EnableEntityDefineRegions和@EnableIndexing注解,如下所示:
代码语言:javascript复制@PeerCacheApplication
@EnableEntityDefinedRegions
@EnableIndexing
class ApplicationConfiguration {
...
}
LuceneIndexes只能在 Apache Geode 服务器上创建,因为LuceneIndexes仅适用于PARTITION区域。
鉴于我们之前对Person类的定义,SDG 注释配置支持找到Person实体类定义并确定人员存储在PARTITION名为“人员”的区域中,并且Person具有 OQL IndexonbirthDate和LuceneIndexon lastName。
13. 在 Apache Geode 中引导 Spring ApplicationContext
通常,基于 Spring 的应用程序通过使用 Spring Data for Apache Geode 的功能来引导 Apache Geode。通过指定<gfe:cache/>使用 Spring Data for Apache Geode XML 命名空间的元素,Cache在与应用程序相同的 JVM 进程中使用默认设置创建和初始化单个嵌入式 Apache Geode 对等实例。
但是,有时需要(可能是您的 IT 组织强制要求)Apache Geode 由提供的 Apache Geode 工具套件完全管理和操作,可能使用 Gfsh。通过使用Gfsh,Apache Geode 引导您的 SpringApplicationContext而不是相反。Apache Geode 不是使用 Spring Boot 的应用程序服务器或 Java 主类,而是进行引导并托管您的应用程序。
Apache Geode 不是应用程序服务器。此外,在涉及 Apache Geode 缓存配置的情况下,使用这种方法存在限制。
13.1.使用 Apache Geode 从 Gfsh 开始引导 Spring 上下文
为了启动一个春天ApplicationContext开始使用的Apache服务器的Geode时在Apache中的Geode Gfsh,则必须使用Apache的Geode的 initalizer能力。初始化程序块可以声明在缓存由 Apache Geode 初始化后启动的应用程序回调。
一个初始化声明的内初始化通过使用Apache的Geode的原生的最小片段元素cache.xml。要引导 Spring ApplicationContext,cache.xml需要一个文件,这与引导ApplicationContext使用组件扫描配置的 Spring 所需的最小 Spring XML 配置片段非常相似(例如<context:component-scan base-packages="…"/>)。
幸运的是,框架已经方便地提供了这样的初始化程序: SpringContextBootstrappingInitializer.
以下示例显示了 Apache Geodecache.xml文件中此类的典型但最小的配置 :
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://geode.apache.org/schema/cache"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<initializer>
<class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
<parameter name="contextConfigLocations">
<string>classpath:application-context.xml</string>
</parameter>
</initializer>
</cache>
本 SpringContextBootstrappingInitializer类遵循类似于Spring的约定ContextLoaderListener 类,它是用来启动一个春天ApplicationContext的Web应用程序,其中里面ApplicationContext 的配置文件与指定的contextConfigLocationsservlet上下文参数。
此外, SpringContextBootstrappingInitializer该类还可以与basePackages参数一起使用,以指定包含适当注释的应用程序组件的基本包的逗号分隔列表。Spring 容器搜索这些组件以在类路径中查找并创建 Spring bean 和其他应用程序组件,如下例所示:
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://geode.apache.org/schema/cache"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<initializer>
<class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
<parameter name="basePackages">
<string>org.mycompany.myapp.services,org.mycompany.myapp.dao,...</string>
</parameter>
</initializer>
</cache>
然后,用适当的配置和构造CLASSPATH及cache.xml文件(前面示出)开始在阿帕奇的Geode服务器时指定为命令行选项Gfsh,命令行会是如下所示:
代码语言:javascript复制gfsh>start server --name=ExampleServer --log-level=config ...
--classpath="/path/to/application/classes.jar:/path/to/spring-data-geode-<major>.<minor>.<maint>.RELEASE.jar"
--cache-xml-file="/path/to/geode/cache.xml"
该application-context.xml可以是任何有效的Spring配置元数据,包括所有的SDG XML命名空间的元素。这种方法的唯一限制是无法使用 SDG XML 命名空间配置 Apache Geode 缓存。换言之,没有任何的<gfe:cache/>元素属性(如cache-xml-location,properties-ref,critical-heap-percentage,pdx-serializer-ref,lock-lease,和其它物质)可以被指定。如果使用,这些属性将被忽略。
这样做的原因是 Apache Geode 本身已经在调用初始化程序之前创建并初始化了缓存。因此,缓存已经存在,并且由于它是“单例”,因此无法重新初始化或增加其任何配置。
13.2.懒惰布线 Apache Geode 组件
Spring Data for Apache Geode 已经提供了对自动装配 Apache Geode 组件(例如CacheListeners、 CacheLoaders、CacheWriters等等)的支持,这些组件由 Apache Geodecache.xml使用 SDG 的WiringDeclarableSupport类声明和创建,如使用自动装配和注释的配置中所述。但是,这仅在 Spring 执行引导程序时(即,当 Spring 引导 Apache Geode 时)才有效。
当您的 SpringApplicationContext由 Apache Geode 引导时,这些 Apache Geode 应用程序组件ApplicationContext不会被注意到,因为 Spring尚不存在。ApplicationContext在 Apache Geode 调用初始化程序块之前不会创建Spring ,这仅在所有其他 Apache Geode 组件(缓存、区域等)都已经创建和初始化之后发生。
为了解决这个问题, LazyWiringDeclarableSupport引入了一个新类。这个新类知道 Spring ApplicationContext。这个抽象基类背后的意图是任何实现类都将自己注册为由 Spring 容器配置,一旦调用初始化程序,该容器最终由 Apache Geode 创建。从本质上讲,这使您的 Apache Geode 应用程序组件有机会使用 Spring 容器中定义的 Spring bean 进行配置和自动连接。
为了让您的 Apache Geode 应用程序组件由 Spring 容器自动连接,您应该创建一个应用程序类,该类扩展 LazyWiringDeclarableSupport并注释需要作为 Spring bean 依赖项提供的任何类成员,类似于以下示例:
代码语言:javascript复制public class UserDataSourceCacheLoader extends LazyWiringDeclarableSupport
implements CacheLoader<String, User> {
@Autowired
private DataSource userDataSource;
...
}
正如CacheLoader上面的示例所暗示的那样,您可能必须(尽管很少)CacheListener在 Apache Geode 中同时定义了 Region 和组件cache.xml。在CacheLoader可能需要访问应用程序存储库(或者一个JDBCDataSource在Spring中定义ApplicationContext)加载Users到阿帕奇的GeodeREPLICATE区上启动。
警告
以这种方式将 Apache Geode 和 Spring 容器的不同生命周期混合在一起时要小心。并非所有用例和场景都受支持。Apache Geodecache.xml配置类似于以下内容(来自 SDG 的测试套件):
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://geode.apache.org/schema/cache"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<region name="Users" refid="REPLICATE">
<region-attributes initial-capacity="101" load-factor="0.85">
<key-constraint>java.lang.String</key-constraint>
<value-constraint>org.springframework.data.gemfire.repository.sample.User</value-constraint>
<cache-loader>
<class-name>
org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTests$UserDataStoreCacheLoader
</class-name>
</cache-loader>
</region-attributes>
</region>
<initializer>
<class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
<parameter name="basePackages">
<string>org.springframework.data.gemfire.support.sample</string>
</parameter>
</initializer>
</cache>