Spring认证中国教育管理中心-Apache Geode的Spring数据教程二十三
发布于 2 年前 作者 qzhou 2600 次浏览 来自 分享

原标题:Spring认证中国教育管理中心-Apache Geode 的 Spring 数据教程二十三(Spring中国教育管理中心)

Apache Geode 的 Spring 数据教程二十三

  1. Apache Geode 存储库的 Spring 数据
    Spring Data for Apache Geode 支持使用 Spring Data Repository 抽象来轻松地将实体持久化到 Apache Geode 中,同时执行查询。此处提供了对存储库编程模型的一般介绍。

10.1.Spring XML 配置
要引导 Spring Data Repositories,请使用<repositories/>Spring Data for Apache Geode Data 命名空间中的元素,如以下示例所示:

示例 7. 使用 XML 为 Apache Geode 存储库启动 Spring 数据

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe-data="https://www.springframework.org/schema/data/geode"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=“
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/data/geode https://www.springframework.org/schema/data/geode/spring-data-geode.xsd
”>

<gfe-data:repositories base-package=“com.example.acme.repository”/>

</beans>
前面的配置片段在配置的基本包下查找接口,并为由SimpleGemFireRepository.

除非您将应用程序域类正确映射到配置的区域,否则引导过程将失败。

10.2.基于 Spring Java 的配置
或者,许多开发人员更喜欢使用 Spring 的基于 Java 的容器配置。

使用这种方法,您可以使用 SDG@EnableGemfireRepositories 注释引导 Spring Data Repositories ,如以下示例所示:

示例 8. 使用 Apache Geode 存储库引导 Spring 数据 @EnableGemfireRepositories

@SpringBootApplication
@EnableGemfireRepositories(basePackages = “com.example.acme.repository”)
class SpringDataApplication {

}
而不是使用basePackages属性,你可能更愿意使用类型安全的basePackageClasses属性来代替。将basePackageClasses让你指定只指定您的应用程序库的接口类型中的一种包含了所有的应用程序库类的包。考虑在每个包中创建一个特殊的无操作标记类或接口,除了标识此属性引用的应用程序存储库的位置外,没有其他用途。

除了basePackages and basePackageClasses属性,如 Spring 的 @ComponentScan注解,@EnableGemfireRepositories注解提供了包含和排除过滤器,基于 Spring 的 ComponentScan.Filter类型。您可以使用该filterType属性按不同方面进行过滤,例如应用程序 Repository 类型是否使用特定的注解进行注解或扩展特定的类类型等等。有关 更多详细信息,请参阅 FilterTypeJavadoc。

@EnableGemfireRepositories注解也让你指定一个名为OQL查询,这驻留在一个Java的位置Properties文件,通过使用namedQueriesLocation属性。属性名称必须与 Repository 查询方法的名称匹配,并且属性值是调用 Repository 查询方法时要执行的 OQL 查询。

如果您的应用程序需要一个或多个自定义存储库实现,
repositoryImplementationPostfix则可以将该属性设置为替代值(默认为Impl)。此功能通常用于扩展 Spring Data Repository 基础架构以实现数据存储未提供的功能(例如 SDG)。

Apache Geode 需要自定义存储库实现的一个示例是执行连接时。SDG 存储库不支持联接。对于 Apache Geode PARTITIONRegion,连接必须在并置的PARTITIONRegion上执行,因为 Apache Geode 不支持“分布式”连接。此外,Equi-Join OQL 查询必须在 Apache Geode 函数内执行。有关 Apache Geode Equi-Join Queries 的更多详细信息,请参见此处。

SDG 存储库基础设施扩展的许多其他方面也可以进行定制。有关@EnableGemfireRepositories 所有配置设置的更多详细信息,请参阅 Javadoc。

10.3.执行 OQL 查询
Spring Data for Apache Geode Repositories 支持定义查询方法,以便针对托管实体映射到的区域轻松执行 Apache Geode OQL 查询,如下例所示:

示例 9. 示例存储库

@Region(“People”)
public class Person { … }
public interface PersonRepository extends CrudRepository<Person, Long> {

Person findByEmailAddress(String emailAddress);

Collection<Person> findByFirstname(String firstname);

@Query(“SELECT * FROM /People p WHERE p.firstname = $1”)
Collection<Person> findByFirstnameAnnotated(String firstname);

@Query(“SELECT * FROM /People p WHERE p.firstname IN SET $1”)
Collection<Person> findByFirstnamesAnnotated(Collection<String> firstnames);
}
前面示例中列出的第一个查询方法导致派生以下 OQL 查询: SELECT x FROM /People x WHERE x.emailAddress = $1. 第二个查询方法的工作方式相同,只是它返回找到的所有实体,而第一个查询方法期望找到单个结果。

如果支持的关键字不足以声明和表达您的 OQL 查询,或者方法名称变得过于冗长,那么您可以使用@Query如第三和第四种方法所示的注释查询方法。

下表提供了可在查询方法中使用的受支持关键字的简要示例:

Apache Geode 的 Spring 数据教程二十三
10.4.使用注解的 OQL 查询扩展
许多查询语言,例如 Apache Geode 的 OQL(对象查询语言),具有 Spring Data Commons 的 Repository 基础架构不直接支持的扩展。

Spring Data Commons 的 Repository 基础设施目标之一是充当最低公分母,以保持对当今应用程序开发中可用和使用的最广泛数据存储的支持和可移植性。从技术上讲,这意味着开发人员可以通过重用现有的特定于应用程序的 Repository 接口(一种方便且强大的抽象),在其应用程序中访问 Spring Data Commons 支持的多个不同数据存储。

为了支持 Apache Geode 的 OQL 查询语言扩展并保持跨不同数据存储的可移植性,Spring Data for Apache Geode 通过使用 Java 注释添加了对 OQL 查询扩展的支持。这些注解会被其他没有类似查询语言特性的 Spring Data Repository 实现(例如 Spring Data JPA 或 Spring Data Redis)忽略。

例如,许多数据存储很可能没有实现 Apache Geode 的 OQLIMPORT关键字。实施IMPORT 作为注释(即,@Import),而不是作为查询方法签名的一部分(具体而言,该方法“姓名”)不与所述解析基础设施评估查询方法名构造另一个数据存储器语言适当的查询时干扰。

目前,Spring Data for Apache Geode 支持的 Apache Geode OQL 查询语言扩展集包括:

例如,假设您有一个Customers应用程序域类和相应的 Apache Geode Region 以及一个按姓氏CustomerRepository查找的查询方法Customers,如下所示:

示例 10. 示例客户存储库

package …;

import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;

@Region(“Customers”)
public class Customer … {

@Id
private Long id;


}
package …;

import org.springframework.data.gemfire.repository.GemfireRepository;

public interface CustomerRepository extends GemfireRepository<Customer, Long> {

@Trace
@Limit(10)
@Hint(“LastNameIdx”)
@Import(“org.example.app.domain.Customer”)
List<Customer> findByLastName(String lastName);


}
Apache Geode 的 Spring 数据教程二十三
前面的示例产生以下 OQL 查询:

<TRACE> <HINT ‘LastNameIdx’> IMPORT org.example.app.domain.Customer; SELECT * FROM /Customers x WHERE x.lastName = $1 LIMIT 10

当 OQL 注释扩展与@Query注释结合使用时,Apache Geode 的 Repository 扩展的 Spring Data 小心不要创建冲突的声明。

再举一个例子,假设你@Query在你的 中定义了一个原始的带注释的查询方法CustomerRepository,如下所示:

示例 11. CustomerRepository

public interface CustomerRepository extends GemfireRepository<Customer, Long> {

@Trace
@Limit(10)
@Hint(“CustomerIdx”)
@Import(“org.example.app.domain.Customer”)
@Query("<TRACE> <HINT ‘ReputationIdx’> SELECT DISTINCT * FROM /Customers c WHERE c.reputation > $1 ORDER BY c.reputation DESC LIMIT 5")
List<Customer> findDistinctCustomersByReputationGreaterThanOrderByReputationDesc(Integer reputation);

}
上述查询方法产生以下 OQL 查询:

IMPORT org.example.app.domain.Customer; <TRACE> <HINT ‘ReputationIdx’> SELECT DISTINCT * FROM /Customers x WHERE x.reputation > $1 ORDER BY c.reputation DESC LIMIT 5

@Limit(10)注释不会覆盖LIMIT在原始查询中明确定义。此外,@Hint(“CustomerIdx”)注释不会覆盖HINT原始查询中明确定义的。最后,@Trace注释是多余的,没有额外的作用。

ReputationIdx考虑到可能对其声誉具有相同价值的客户数量,该指数可能不是最明智的指数,这会降低该指数的有效性。请明智地选择索引和其他优化,因为由于维护索引的开销,不正确或选择不当的索引会对您的性能产​生相反的影响。该ReputationIdx只用来为例子的目的。

10.5.查询后处理
由于使用了 Spring Data Repository 抽象,用于定义数据存储特定查询(例如 OQL)的查询方法约定变得简单方便。但是,有时仍希望检查甚至可能修改从 Repository 查询方法生成的查询。

从 2.0.x 开始,Apache Geode 的 Spring Data 包含
o.s.d.gemfire.repository.query.QueryPostProcessor功能接口。接口的定义松散如下:

示例 12. QueryPostProcessor

package org.springframework.data.gemfire.repository.query;

import org.springframework.core.Ordered;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.QueryMethod;
import …;

@FunctionalInterface
interface QueryPostProcessor<T extends Repository, QUERY> extends Ordered {

QUERY postProcess(QueryMethod queryMethod, QUERY query, Object… arguments);

}
提供了额外的默认方法,让您可以组合QueryPostProcessor类似于
java.util.function.Function.andThen(:Function) 和java.util.function.Function.compose(:Function) 工作方式的实例。

此外,该QueryPostProcessor接口实现了该
org.springframework.core.Ordered接口,当QueryPostProcessors在 Spring 容器中声明和注册多个并用于为一组生成的查询方法查询创建处理管道时,该接口 很有用。

最后,QueryPostProcessor接受分别对应于类型参数T和的类型参数QUERY。类型T扩展了 Spring Data Commons 标记接口,
org.springframework.data.repository.Repository. 我们将在本节后面进一步讨论这一点。QUERYApache Geode 案例的 Spring Data 中的所有类型参数参数都是 java.lang.String.

将查询定义为 type 很有用QUERY,因为这个QueryPostProcessor接口可以移植到 Spring Data Commons,因此必须处理不同数据存储(例如 JPA、MongoDB 或 Redis)的所有形式的查询。

您可以实现此接口,以通过调用该Repository方法时从应用程序接口方法生成的查询接收回调 。

例如,您可能希望记录来自所有应用程序存储库接口定义的所有查询。您可以通过使用以下QueryPostProcessor实现来做到这一点:

示例 13. LoggingQueryPostProcessor

package example;

import …;

class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {

private Logger logger = Logger.getLogger(“someLoggerName”);

@Override
public String postProcess(QueryMethod queryMethod, String query, Object… arguments) {

  String message = String.format("Executing query [%s] with arguments [%s]", query, Arrays.toString(arguments));

  this.logger.info(message);

}
}
将LoggingQueryPostProcessor已键入了Spring数据
org.springframework.data.repository.Repository 标记接口,并且因此,记录所有应用程序库接口查询方法生成的查询。

您可以将此日志记录的范围限制为仅来自某些类型的应用程序存储库接口的查询,例如 a CustomerRepository,如以下示例所示:

示例 14. CustomerRepository

interface CustomerRepository extends CrudRepository<Customer, Long> {

Customer findByAccountNumber(String accountNumber);

List<Customer> findByLastNameLike(String lastName);

}
然后,您可以LoggingQueryPostProcessor专门在 中键入CustomerRepository,如下所示:

示例 15.
CustomerLoggingQueryPostProcessor

class LoggingQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> { … }
因此,只会记录CustomerRepository界面中定义的查询,例如findByAccountNumber。

您可能希望为QueryPostProcessor存储库查询方法定义的特定查询创建一个。例如,假设您希望将
CustomerRepository.findByLastNameLike(:String)查询方法生成的 OQL 查询限制为仅返回五个结果,并按升序对CustomersbyfirstName进行排序。为此,您可以定义一个 custom QueryPostProcessor,如以下示例所示:

示例 16.
OrderedLimitedCustomerByLastNameQueryPostProcessor

class OrderedLimitedCustomerByLastNameQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {

private final int limit;

public OrderedLimitedCustomerByLastNameQueryPostProcessor(int limit) {
this.limit = limit;
}

@Override
public String postProcess(QueryMethod queryMethod, String query, Object… arguments) {

return "findByLastNameLike".equals(queryMethod.getName())
  ? query.trim()
      .replace("SELECT", "SELECT DISTINCT")
      .concat(" ORDER BY firstName ASC")
      .concat(String.format(" LIMIT %d", this.limit))
  : query;

}
}
虽然前面的示例有效,但您可以通过使用 Spring Data for Apache Geode 提供的 Spring Data Repository 约定来实现相同的效果。例如,相同的查询可以定义如下:

示例 17. CustomerRepository 使用约定

interface CustomerRepository extends CrudRepository<Customer, Long> {

@Limit(5)
List<Customer> findDistinctByLastNameLikeOrderByFirstNameDesc(String lastName);

}
但是,如果您无法控制应用程序CustomerRepository接口定义,那么QueryPostProcessor(即
OrderedLimitedCustomerByLastNameQueryPostProcessor)很方便。

如果要确保LoggingQueryPostProcessor始终在其他应用程序定义的 QueryPostProcessors可能已在 Spring 中声明和注册的 bean 之后ApplicationContext,则可以order通过覆盖该o.s.core.Ordered.getOrder()方法来设置该属性,如下例所示:

示例 18. 定义order属性

class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {

@Override
int getOrder() {
return 1;
}
}

class CustomerQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {

@Override
int getOrder() {
return 0;
}
}
这确保您始终可以QueryPostProcessors 在LoggingQueryPostProcessor记录查询之前看到其他应用的后期处理的效果。

您可以根据需要QueryPostProcessors在 Spring 中定义ApplicationContext任意数量,并以任意顺序将它们应用于所有或特定应用程序存储库接口,并通过使用提供的postProcess(…)方法回调参数来尽可能细化。

回到顶部