领先一步
VMware 提供培训和认证,助您加速进步。
了解更多12个项目修复了超过300个问题,这使得跟踪自上次发布以来发生的变化变得相当困难。因此,这里更详细地摘录了我们在上次迭代中开发的一些新功能。
自 Dijkstra 版本系列以来,我们已经能够在 JPA 支持的仓库中通过实体上声明的 @EntityGraph
注解引用命名实体图。在下面的示例中,这会强制急切加载 firstname 和 lastname,而其他所有内容则保持延迟加载。
@Entity
@NamedEntityGraphs(
@NamedEntityGraph(name = "with-tags",
attributeNodes = { @NamedAttributeNode("tags") }))
class Product {
@ManyToMany
Set<Tag> tags;
// other properties omitted
}
interface ProductRepository extends Repository<Customer, Long> {
@EntityGraph("with-tags")
Product findOneById(Long id);
}
Gosling 版本现在将我们的 JPA 2.1 支持向前推进了一步,将其扩展到即时 fetch graph 定义。通过在查询方法上使用 @EntityGraph(attributePaths = …)
显式指定属性,您无需在实体上使用 NamedEntityGraph
注解。
@Entity
class Product {
@ManyToMany
Set<Tag> tags;
// other properties omitted
}
interface ProductRepository extends Repository<Customer, Long> {
@EntityGraph(attributePaths = {"tags"})
Product findOneById(Long id);
}
Spring Data 的 Web 支持已经允许您在控制器处理方法中声明 Pageable
类型的参数。新引入的 Querydsl 集成扩展了这一功能,允许您直接从 HTTP 请求的查询字符串中获取可用的 Predicate
。当配置了 @EnableSpringDataWebSupport
并且类路径中存在 Querydsl 时,此功能会自动启用。
如果在没有进一步配置的情况下使用 Predicate
,我们将尝试从方法的返回类型中解析 Predicate
解析的根类型,尽管在大多数情况下,最好通过 @QuerydslPredicate(root = …)
显式声明所需的类型引用。有了这个,查询字符串属性将绑定到类型的匹配属性,从而生成例如
QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
来自 ?firstname=Dave&lastname=Matthews
,使用默认的依赖于属性类型的绑定。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Controller
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
static class UserController {
private final UserRepository repository;
@RequestMapping(value = "/", method = RequestMethod.GET)
String index(Model model,
@QuerydslPredicate(root = User.class) Predicate predicate,
Pageable pageable) {
model.addAttribute("users", repository.findAll(predicate, pageable));
return "index";
}
}
}
现在,使用默认的(等于)绑定并不总是合适的,而是应该为每个属性或特定类型提供专门的绑定。要实现这一点,只需通过提供一个 QuerydslBinderCustomizer
来覆盖默认设置,该自定义器可以通过 @QuerydslPredicate(bindings = …)
注册,或者直接由仓库实现。
interface UserRepository extends CrudRepository<User, String>,
QueryDslPredicateExecutor<User>,
QuerydslBinderCustomizer<QUser> {
// Query methods go here
@Override
default public void customize(QuerydslBindings bindings, QUser user) {
bindings.bind(user.nationality).first(
(path, value) -> path.equalsIgnoreCase(value)); // 1
bindings.bind(String.class).first(
(StringPath path, String value) -> path.containsIgnoreCase(value)); // 2
bindings.excluding(user.password);
}
}
如您所见,我们利用 Java 8 Lambda 和 Querydsl 的类型安全属性引用来定义特定属性(1)或给定类型的所有属性(2)的绑定。使用 QuerydslBindings.excluding
可以将路径排除在可查询范围之外。
在 Spring Data 示例仓库 中找到一个完整的可运行示例,并查看 参考文档 获取详细信息。
Spring Data Commons 中引入的 Querydsl 支持(参见 上文)已集成到 Spring Data REST 中。这意味着您可以通过在请求 URI 中附加简单的属性查询参数来过滤您的集合资源。
在公开星巴克门店位置的 Spring Data REST 示例中,请注意 StoreRepository 如何实现 QueryDslPredicateExecutor
和 QuerydslBinderCustomizer<QStore>
,正如上文所述。
Spring Data REST 公开的门店集合资源将允许您发出以下请求
$ http :8080/api/stores?address.city=York
{
"_embedded": {
"stores": [
{
"_links": {
…
},
"address": {
"city": "New York",
"location": { "x": -73.938421, "y": 40.851 },
"street": "803 W 181st St",
"zip": "10033-4516"
},
"name": "Washington Hgts/181st St"
},
{
"_links": {
…
},
"address": {
"city": "New York",
"location": { "x": -73.939822, "y": 40.84135 },
"street": "4001 Broadway",
"zip": "10032-1508"
},
"name": "168th & Broadway"
},
…
]
},
"_links": {
…
},
"page": {
"number": 0,
"size": 20,
"totalElements": 209,
"totalPages": 11
}
}
请注意,它只返回城市以“York”结尾的门店,这与 StoresRepository
中 QuerydslBinderCustomizer
的实现中定义的行为一致。
我们目前正在研究如何更明显地宣传这种查询机制的选项,例如使用模板变量,甚至提供高级映射功能来自定义要使用的请求参数名称。
Spring Data REST 的 Gosling 版本附带了一个附加模块,它封装了 Mike Kelly 的 HAL 浏览器,并进行了一些定制,以利用我们公开的 API 元数据。要在您的应用程序中使用该浏览器,只需将 spring-data-rest-hal-browser
模块添加到您的项目中,您的 API 根路径就会为接受 text/html
的请求提供浏览器服务。当然,标准的 HAL 响应仍然默认提供,或者如果您使用基于 JSON 的 Accept
头。
图 1. - HAL 浏览器(点击放大)
虽然 Spring Data REST 模块使得将浏览器添加到您的应用程序中变得很容易,但它也稍微调整了浏览器。当您点击按钮触发非 GET
请求时,浏览器通常会打开一个模态对话框,该对话框需要一些原始 JSON 输入。虽然如果您知道自己在做什么,这当然很棒,但这有点容易出错且不太方便,因为您必须了解服务器期望的数据结构。
Spring Data REST 利用 profile
链接关系公开系统中暴露类型的 JSON Schema 文档,这使得 Schema 可以通用地发现,而无需将发现逻辑绑定到 Spring Data REST 本身。我们随附的浏览器实例将查找该 Schema 元数据,如果找到,则将其交给 JSON Editor,用完全基于 JSON Schema 生成的表单替换默认对话框。
图 2. - 基于 JSON Editor 的 POST 表单(点击放大)
请看表单如何允许添加明细项,因为 Schema 将其暴露为一个数组。价格和订单日期字段被标记为只读,位置字段允许从具有国际化值的枚举中选择值。
示例项目可以在 GitHub 上找到。
正如您在上面的截图中看到的那样,restbucks:orders
链接伴随有一个人类可读的描述。这些描述从可选的资源包 rest-messages
中提取,使用 _links.$rel.title
键来定义可读的值。示例使用 rest-messages.properties
作为备用资源包,但也包含一个 rest-messages_de.properties
,用于向发送设置为 de
的 Accept-Language
头的客户端返回德语标签。
同样的资源包可以用于枚举值的国际化,以便它们可以在客户端以人类可读的方式使用。为了不破坏现有应用程序,必须通过 RepositoryRestConfiguration.setEnableEnumTranslation(…)
显式激活此功能。有关翻译的详细信息可以在 EnumTranslationConfiguration
上配置。
Spring Data GemFire 1.7 最值得注意的新增功能是对 Pivotal GemFire 8.1 和 Apache Geode 的支持。Pivotal GemFire 于今年早些时候提交到 Apache Incubator,Spring Data 团队迅速响应,在 Spring Data GemFire 中包含了 支持。
此外,还添加了其他几项功能,以简化使用 Spring 开发 GemFire 和 Apache Geode 应用程序。例如,开发人员现在可以使用注解定义特定于应用程序域对象的过期策略
@TimeToLiveExpiration(
timeout = "@expirationSettings['spel.defined.timeout']" action="DESTROY")
@IdleTimeoutExpiration(
timeout = "1800" action="${property.placeholder.defined.action}")
class ApplicationDomainObject { … }
基于过期的注解支持 SpEL 和 Spring 属性占位符 值。要启用基于注解的过期策略,您只需在您的 GemFire 区域上为 TTL、TTI 或两者配置 Spring Data GemFire 的 CustomExpiry 实现 AnnotationBasedExpiration。
<gfe:partitioned-region id="Example" persistent="false" …>
<gfe:custom-entry-ttl>
<bean class="….gemfire.support.AnnotationBasedExpiration" factory-method="forTimeToLive"/>
</gfe:custom-entry-ttl>
<gfe:custom-entry-tti ref="ttiExpiration"/>
</gfe:partitioned-region>
<bean id="ttiExpiration" class="….gemfire.support.AnnotationBasedExpiration" factory-method="forIdleTimeout">
<constructor-arg ref="defaultExpirationAttributes"/>
</bean>
<bean id="defaultExpirationAttributes" class="….ExpirationAttributes">
<constructor-arg value="600"/>
<constructor-arg value="#{T(….ExpirationAction).DESTROY}"/>
</bean>
请参阅参考指南以 了解更多。接下来,通过注解添加了对仓库查询方法 OQL 扩展的支持
interface CustomerRepository implements CrudRepository<Cutomer, Long> {
@Trace
@Limit(25)
@Import("org.exmple.Customer")
@Hint("CustomerLastNameIdx")
List<Customer> findByLastNameOrderByLastNameAsc(String lastName);
}
@Trace
启用单个 OQL 语句调试。@Limit
限制查询结果集中的结果数量,@Import
使应用程序能够区分名称相似的对象类型。例如,您的应用程序可能同时定义了 org.example.app.core.Customer
和 org.example.app.vendor.xyz.Customer
类型。有关详细信息,请参阅 GemFire 的 文档。@Hint
允许使用 OQL 提示来识别适用于查询的索引。在此处了解有关 OQL 扩展的更多信息 此处。
最后,Spring Data GemFire 使用 Spring Data GemFire XML 数据命名空间提供了对 GemFire 缓存和区域快照 的支持。
<gfe:partitioned-region id="Example" persistent="false" … />
<gfe-data:snapshot-service id="exampleRegionSnapshotService" region-ref="Example">
<gfe-data:snapshot-import location="/path/to/import/example.snapshot"/>
<gfe-data:snapshot-export locator="/path/to/export/example.snapshot"/>
</gfe-data:snapshot-service>
您可以在 此处 了解更多关于 Spring Data GemFire 如何支持导入 ZIP 文件、使用 Spring ApplicationEvents
触发导入和导出快照以及如何适当过滤导入和导出数据的信息。
自从我们被要求为 Spring Data 仓库提供一个非常简单的基于 Map
的实现,用于各种(主要是测试)目的以来,已经过去了相当长的时间。最终,这些请求促使我们以一种与以往略有不同的方式重振了 KeyValue 模块。
Spring Data KeyValue 现在包含一个基本的基于 Map
的仓库实现,该实现默认使用 Spring Expression Language (SpEL) 查询值,并基于其集合模块提供排序、分页和 Querydsl 集成。它还公开了专门的 API,允许键值存储在需要时利用特定于存储的存储、检索和最重要的是查询执行优化。
Spring Data KeyValue 仓库使用的默认查询机制基于 SpEL,允许您定义和运行复杂的查询。当在 COMPILED
模式下运行时,这种方法展现了其真正的威力,因为它有效地编译了要在值上执行的过滤表达式。另外,您也可以使用 Querydsl 表达式进行类型安全查询。
@Configuration
@EnableMapRepositories("com.acme.repositories")
class AppConfig {}
@KeySpace("user")
class User {
String @Id id;
String firstname;
}
interface UserRepository extends CrudRepository<User, String> {
List<String> findByFirstnameStartingWith(String firstname);
}
我们目前正在为 Ehcache、Hazelcast 和 Aerospike 开发该 API 的扩展,并期待评估整合 Redis 以及可能将一些 Gemfire API 移植到此处使用的选项。
下一步是 在华盛顿特区举行的 SpringOne2GX - 我们很高兴在那里见到您 - 这是与团队联系、了解新功能并度过愉快时光的最佳场所。同时,我们已经在为 Fowler 版本系列准备下一个服务版本,并开始为 Hopper 版本系列开发新功能(嘘......我们会在 SpringOne 的 “Spring Data 在 2015 年有哪些新功能?”演讲中偷偷透露 Hopper 的一些内容)。