此版本仍在开发中,尚未被视为稳定版本。如需使用最新的稳定版本,请使用 Spring Data Relational 4.0.4spring-doc.cadn.net.cn

持久化实体

保存一个聚合可以使用 CrudRepository.save(…) 方法。 如果该聚合是新的,则会首先对聚合根执行插入操作,然后对其所有直接或间接引用的实体依次执行插入语句。spring-doc.cadn.net.cn

如果聚合根不是新的,则所有引用的实体都会被删除,聚合根会被更新,并且所有引用的实体会再次被插入。 请注意,一个实例是否为新实例属于该实例状态的一部分。spring-doc.cadn.net.cn

这种方法存在一些明显的缺点。 如果被引用的实体中只有少数几个实际上发生了变更,那么删除和重新插入就是一种浪费。 尽管这一过程可以而且很可能会得到改进,但 Spring Data JDBC 的能力仍存在一定限制。 它无法获知聚合的先前状态。 因此,任何更新操作都必须以数据库中当前存在的数据为基础,并确保将其转换为传递给 save 方法的实体所表示的状态。

另请参阅实体状态检测以获取更多详细信息。spring-doc.cadn.net.cn

正在加载聚合对象

Spring Data JDBC 提供了两种加载聚合的方式:spring-doc.cadn.net.cn

  1. 传统方式(在 3.2 版本之前也是唯一的方式)非常简单: 每个查询都会加载聚合根,无论该查询是基于 CrudRepository 方法、派生查询还是带注解的查询。 如果聚合根引用了其他实体,则这些实体会通过单独的语句进行加载。spring-doc.cadn.net.cn

  2. Spring Data JDBC 3.2 支持使用单查询加载(Single Query Loading)。 通过这种方式,可以使用一条 SQL 查询完整加载任意数量的聚合根。 这应该会显著提升效率,特别是对于由多个实体组成的复杂聚合根。spring-doc.cadn.net.cn

    目前,单查询加载(Single Query Loading)在不同方面受到限制:spring-doc.cadn.net.cn

    1. 聚合对象不得包含嵌套的集合,这包括Map。 计划在未来移除这一限制。spring-doc.cadn.net.cn

    2. 聚合不得使用 AggregateReference 或嵌入式实体。 计划在未来移除这一限制。spring-doc.cadn.net.cn

    3. 数据库方言必须支持该功能。在 Spring Data JDBC 提供的所有方言中,除了 H2 和 HSQL 之外,其余都支持此功能。 H2 和 HSQL 不支持分析函数(也称为窗口函数)。spring-doc.cadn.net.cn

    4. 它仅适用于 CrudRepository 中的 find 方法,不适用于派生查询,也不适用于带注解的查询。 计划在未来移除这一限制。spring-doc.cadn.net.cn

    5. 必须通过调用 JdbcMappingContextsetSingleQueryLoadingEnabled(true) 中启用单查询加载(Single Query Loading)。spring-doc.cadn.net.cn

如果任何条件未满足,Spring Data JDBC 将回退到加载聚合的默认方法。spring-doc.cadn.net.cn

单查询加载(Single Query Loading)目前被视为实验性功能。 我们非常欢迎您提供关于其实际使用效果的反馈。
虽然单查询加载(Single Query Loading)可以缩写为 SQL,但我们强烈不建议这样做,因为几乎肯定会与结构化查询语言(Structured Query Language)产生混淆。

ID 生成

Spring Data 使用标识符属性来识别实体。 也就是说,查找这些实体或创建针对特定行的语句。 实体的 ID 必须使用 Spring Data 的 @Id 注解进行标注。spring-doc.cadn.net.cn

当您的数据库的 ID 列是自增列时,在将实体插入数据库后,生成的值会被设置到该实体中。spring-doc.cadn.net.cn

如果你在标识符属性上额外添加 @Sequence 注解,并且底层的 Dialect 支持序列(sequences),则将使用数据库序列来获取 ID 的值。spring-doc.cadn.net.cn

否则,当实体为新实体且标识符值为其初始默认值时,Spring Data 不会尝试向标识符列插入值。 对于基本数据类型,该初始值为 0;如果标识符属性使用了如 null 这样的数值包装类型,则初始值为 Longspring-doc.cadn.net.cn

实体状态检测详细解释了用于判断一个实体是新实体,还是预期已在数据库中存在的策略。spring-doc.cadn.net.cn

一个重要的约束是,在保存实体之后,该实体不能再处于“新建”状态。 请注意,实体是否为新建状态是实体自身状态的一部分。 对于自增列,这一过程会自动完成,因为 Spring Data 会使用 ID 列的值来设置实体的 ID。spring-doc.cadn.net.cn

模板 API

作为存储库的替代方案,Spring Data JDBC 提供了 JdbcAggregateTemplate,作为一种更直接的方式在关系型数据库中加载和持久化实体。 在很大程度上,存储库使用 JdbcAggregateTemplate 来实现其功能。spring-doc.cadn.net.cn

本节仅重点介绍 JdbcAggregateTemplate 中最有趣的部分。 如需更全面的概述,请参阅 JdbcAggregateTemplate 的 JavaDoc。spring-doc.cadn.net.cn

访问 JdbcAggregateTemplate

JdbcAggregateTemplate 旨在作为 Spring Bean 使用。 如果您已将应用程序配置为包含 Spring Data JDBC,则可以在任意 Spring Bean 中声明对 JdbcAggregateTemplate 的依赖,Spring 框架将注入一个正确配置的实例。spring-doc.cadn.net.cn

这包括您用于为 Spring Data 仓库实现自定义方法的片段,使您可以使用 JdbcAggregateTemplate 来定制和扩展您的仓库。spring-doc.cadn.net.cn

持久化

JdbcAggregateTemplate 提供了三种用于持久化实体的方法:saveinsertupdate。 每种方法都有两种形式: 一种作用于单个聚合对象,方法名与上述完全相同;另一种带有 All 后缀,作用于一个 Iterablespring-doc.cadn.net.cn

save 的作用与仓库中同名方法的作用相同。spring-doc.cadn.net.cn

insertupdate 在实体为新实体时会跳过测试,并根据其名称所指示的,假定该聚合是新建的或已存在的。spring-doc.cadn.net.cn

查询

JdbcAggregateTemplate 提供了大量用于查询聚合对象及其集合的方法。 其中有一类方法需要特别注意, 那就是接受 Query 作为参数的方法。 它们允许执行以编程方式构建的查询,如下所示:spring-doc.cadn.net.cn

template.findOne(query(where("name").is("Gandalf")), Person.class);

Queryquery 方法返回,用于定义要选择的列列表、where 子句(通过 CriteriaDefinition)以及 limit 和 offset 子句的规范。 有关 Query 类的详细信息,请参阅其 JavaDoc。spring-doc.cadn.net.cn

Criteria类(其中where是其静态成员)提供了org.springframework.data.relational.core.query.CriteriaDefinition[]的实现,这些实现代表查询的where子句。spring-doc.cadn.net.cn

Criteria 类的方法

Criteria 类提供了以下方法,所有这些方法都对应于 SQL 操作符:spring-doc.cadn.net.cn

  • Criteria (String column):向当前 Criteria 添加一个带有指定 property 的链式 Criteria,并返回新创建的 5spring-doc.cadn.net.cn

  • Criteria (String column):向当前 Criteria 添加一个带有指定 property 的链式 Criteria,并返回新创建的 5spring-doc.cadn.net.cn

  • Criteria greaterThan (Object o):使用 > 运算符创建一个条件。spring-doc.cadn.net.cn

  • Criteria greaterThanOrEquals (Object o):使用 >= 运算符创建一个条件。spring-doc.cadn.net.cn

  • Criteria in (Object…​ o):使用 IN 操作符为可变参数创建一个条件。spring-doc.cadn.net.cn

  • Criteria in (Collection<?> collection):使用集合创建一个采用 IN 运算符的条件。spring-doc.cadn.net.cn

  • Criteria is (Object o):通过使用列匹配(property = value)创建一个条件。spring-doc.cadn.net.cn

  • Criteria isNull ():使用 IS NULL 运算符创建一个条件。spring-doc.cadn.net.cn

  • Criteria isNotNull ():使用 IS NOT NULL 运算符创建一个条件。spring-doc.cadn.net.cn

  • Criteria lessThan (Object o):使用 < 运算符创建一个条件。spring-doc.cadn.net.cn

  • Criteria lessThanOrEquals (Object o):使用 运算符创建一个条件。spring-doc.cadn.net.cn

  • Criteria like (Object o):使用 LIKE 运算符创建一个条件,且不进行转义字符处理。spring-doc.cadn.net.cn

  • Criteria not (Object o):使用 != 运算符创建一个条件。spring-doc.cadn.net.cn

  • Criteria notIn (Object…​ o):使用 NOT IN 运算符为可变参数创建一个条件。spring-doc.cadn.net.cn

  • Criteria notIn (Collection<?> collection):使用集合创建一个采用 NOT IN 运算符的条件。spring-doc.cadn.net.cn

乐观锁

Spring Data 通过聚合根上标注了 @Version 的数值属性来支持乐观锁。 每当 Spring Data 保存具有此类版本属性的聚合时,会发生两件事:spring-doc.cadn.net.cn

  • 针对聚合根的更新语句将包含一个 WHERE 子句,用于检查数据库中存储的版本实际上未被更改。spring-doc.cadn.net.cn

  • 如果情况并非如此,则会抛出 OptimisticLockingFailureException 异常。spring-doc.cadn.net.cn

此外,version 属性在实体和数据库中都会递增,因此并发操作将察觉到这一变更,并在适用的情况下抛出 OptimisticLockingFailureException,如上文所述。spring-doc.cadn.net.cn

该过程同样适用于插入新的聚合对象,其中 null0 的版本号表示一个新实例,而随后递增的版本号则将该实例标记为不再是新的。这种方式在例如使用 UUID 作为 ID 并在对象构造期间生成 ID 的场景下,能够很好地发挥作用。spring-doc.cadn.net.cn

在执行删除操作时,版本检查同样适用,但版本号不会增加。spring-doc.cadn.net.cn