原题目:Spring 认证 |Spring Data JDBC 参考文档(内容起源:Spring 中国教育管理中心)
Spring Data JDBC 参考文档
- JDBC 存储库
本章指出了 JDBC 存储库反对的特殊性。这建设在应用 Spring Data Repositories 中解释的外围存储库反对之上。您应该对那里解释的基本概念有充沛的理解。
9.1. 为什么抉择 Spring Data JDBC?
Java 世界中关系数据库的次要长久化 API 必定是 JPA,它有本人的 Spring Data 模块。为什么还有一个?
JPA 做了很多事件来帮忙开发人员。除其余外,它跟踪对实体的更改。它为你做提早加载。它使您能够将宽泛的对象结构映射到同样宽泛的数据库设计。
这很棒,让很多事件变得非常简单。只需看一下根本的 JPA 教程。然而,对于 JPA 为什么要做某件事,这经常让人感到困惑。此外,概念上非常简单的事件在 JPA 中变得相当艰难。
Spring Data JDBC 旨在通过采纳以下设计决策在概念上更简略:
如果您加载一个实体,SQL 语句就会运行。实现此操作后,您将领有一个齐全加载的实体。没有进行提早加载或缓存。
如果您保留一个实体,它将被保留。如果您不这样做,则不会。没有脏跟踪,也没有会话。
有一个对于如何将实体映射到表的简略模型。它可能只实用于相当简略的状况。如果您不喜爱那样,您应该编写本人的策略。Spring Data JDBC 仅对应用正文自定义策略提供十分无限的反对。
9.2. 畛域驱动设计和关系数据库。
所有 Spring Data 模块都受到畛域驱动设计中“存储库”、“聚合”和“聚合根”概念的启发。这些对于 Spring Data JDBC 来说可能更为重要,因为在某种程度上,它们与应用关系数据库时的惯例做法南辕北辙。
聚合是一组实体,能够保障在对其进行原子更改之间保持一致。一个经典的例子是 Orderwith OrderItems。上的属性 Order(例如,numberOfItems 与 的理论数量 OrderItems 统一)在进行更改时保持一致。
跨聚合的援用不能保障在任何时候都是统一的。他们保障最终会变得统一。
每个聚合都有一个聚合根,它是聚合的实体之一。聚合仅通过该聚合根上的办法进行操作。这些是后面提到的原子变动。
存储库是对长久存储的形象,它看起来像是某种类型的所有聚合的汇合。对于 Spring Data 一般而言,这意味着您心愿 Repository 每个聚合根都有一个。此外,对于 Spring Data JDBC,这意味着可从聚合根拜访的所有实体都被视为该聚合根的一部分。Spring Data JDBC 假设只有聚合具备指向存储聚合的非根实体的表的外键,并且没有其余实体指向非根实体。
在以后的实现中,从聚合根援用的实体被 Spring Data JDBC 删除并从新创立。
您能够应用与您的工作和设计数据库的格调相匹配的实现来笼罩存储库办法。
9.3. 入门
疏导设置工作环境的一种简略办法是在 STS 中或从 Spring Initializr 创立一个基于 Spring 的我的项目。
首先,您须要设置一个正在运行的数据库服务器。请参阅您的供应商文档,理解如何为 JDBC 拜访配置数据库。
在 STS 中创立 Spring 我的项目:
转到 File → New → Spring Template Project → Simple Spring Utility Project,而后在呈现提醒时按 Yes。而后输出我的项目和包名称,例如 org.spring.jdbc.example.
将以下内容增加到 pom.xmlfilesdependencies 元素:
org.springframework.data
spring-data-jdbc
2.2.5
将 pom.xml 中 Spring 的版本改为
将 Maven 的 Spring Milestone 存储库的以下地位增加到您 pom.xml 的元素中,使其与您的元素处于同一级别:
spring-milestone
Spring Maven MILESTONE Repository
https://repo.spring.io/libs-m…
存储库也可在此处浏览。
Spring Data JDBC 参考文档
9.4. 示例库
有一个蕴含多个示例的 GitHub 存储库,您能够下载并试用这些示例,以理解该库的工作原理。
9.5. 基于注解的配置
Spring Data JDBC 存储库反对能够通过 Java 配置的注解来激活,如下例所示:
示例 54. 应用 Java 配置的 Spring Data JDBC 存储库
Spring Data JDBC 参考文档
@Configuration
@EnableJdbcRepositories
class ApplicationConfig extends AbstractJdbcConfiguration {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean
NamedParameterJdbcOperations namedParameterJdbcOperations(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
@Bean
TransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@EnableJdbcRepositories 为派生自的接口创立实现 Repository
AbstractJdbcConfiguration 提供 Spring Data JDBC 所需的各种默认 bean
创立 DataSource 到数据库的连贯。这是以下两个 bean 办法所必须的。
创立
NamedParameterJdbcOperationsSpring Data JDBC 用来拜访数据库的。
Spring Data JDBC 利用 Spring JDBC 提供的事务管理。
在后面的例子中的配置类,通过应用设置了一个嵌入式 HSQL 数据库 EmbeddedDatabaseBuilder 的 API spring-jdbc。该 DataSource 则用来建设
NamedParameterJdbcOperations 和 TransactionManager。咱们最终通过应用 @EnableJdbcRepositories. 如果没有配置根底包,则应用配置类所在的包。扩大 AbstractJdbcConfiguration 确保各种 bean 失去注册。笼罩其办法可用于自定义设置(见下文)。
应用 Spring Boot 能够进一步简化此配置。DataSource 一旦启动器
spring-boot-starter-data-jdbc 蕴含在依赖项中,应用 Spring Boot 就足够了。其余所有都由 Spring Boot 实现。
在此设置中,您可能须要自定义几项内容。
9.5.1. 方言
Spring Data JDBC 应用接口的实现 Dialect 来封装特定于数据库或其 JDBC 驱动程序的行为。默认状况下,会 AbstractJdbcConfiguration 尝试确定正在应用的数据库并注册正确的 Dialect. 这种行为能够通过笼罩来扭转 jdbcDialect(
NamedParameterJdbcOperations)。
如果您应用的数据库没有可用的方言,那么您的应用程序将无奈启动。在这种状况下,您必须要求您的供应商提供 Dialect 实现。或者,您能够:
施行您本人的 Dialect.
实现一个 JdbcDialectProvider 返回 Dialect.
通过 spring.factories 在上面创立资源来注册提供者 META-INF 并通过增加一行来执行注册
org.springframework.data.jdbc.repository.config.DialectResolver$JdbcDialectProvider=
9.6. 长久实体
能够应用该 CrudRepository.save(…)办法执行保留聚合。如果聚合是新的,这将导致聚合根的插入,而后是所有间接或间接援用的实体的插入语句。
如果聚合根不是新的,则所有援用的实体都会被删除,聚合根会更新,并且所有援用的实体都会再次插入。请留神,实例是否为新实例是实例状态的一部分。
这种办法有一些显著的毛病。如果理论更改的援用实体很少,则删除和插入是节约。尽管这个过程能够而且很可能会失去改良,但 Spring Data JDBC 能够提供的内容存在某些限度。它不晓得聚合的先前状态。因而,任何更新过程始终必须采纳它在数据库中找到的任何内容,并确保将其转换为传递给 save 办法的实体的任何状态。
9.6.1. 对象映射根底
本节涵盖 Spring Data 对象映射、对象创立、字段和属性拜访、可变性和不变性的基础知识。请留神,本节仅实用于不应用底层数据存储(如 JPA)的对象映射的 Spring Data 模块。此外,请务必查阅特定于存储的局部以获取特定于存储的对象映射,例如索引、自定义列或字段名称等。
Spring Data 对象映射的外围职责是创立域对象的实例并将存储本机数据结构映射到这些实例上。这意味着咱们须要两个根本步骤:
应用公开的构造函数之一创立实例。
实例填充以实现所有公开的属性。
对象创立
Spring Data 会主动尝试检测要用于具体化该类型对象的长久实体的构造函数。解析算法的工作原理如下:
如果只有一个构造函数,则应用它。
如果有多个构造函数并且只有一个用 正文 @PersistenceConstructor,则应用它。
如果存在无参数构造函数,则应用它。其余构造函数将被疏忽。
值解析假设结构函数参数名称与实体的属性名称匹配,即解析将被执行,就像要填充属性一样,包含映射中的所有自定义(不同的数据存储列或字段名称等)。这还须要类文件中可用的参数名称信息或 @ConstructorProperties 构造函数中存在的正文。
能够通过应用 @Value 特定于商店的 SpEL 表达式应用 Spring Framework 的值正文来自定义值解析。无关更多详细信息,请参阅无关商店特定映射的局部。
对象创立外部
为了防止反射的开销,Spring Data 对象创立默认应用运行时生成的工厂类,它会间接调用域类构造函数。即对于此示例类型:
Spring Data JDBC 参考文档
class Person {
Person(String firstname, String lastname) {…}
}
咱们将在运行时创立一个语义上等同于这个的工厂类:
class PersonObjectInstantiator implements ObjectInstantiator {
Object newInstance(Object… args) {
return new Person((String) args[0], (String) args[1]);
}
}
这使咱们比反射进步了大概 10% 的性能。对于有资格进行此类优化的域类,它须要恪守一组束缚:
它不能是私人课程
它不能是非动态外部类
它不能是 CGLib 代理类
Spring Data 应用的构造函数不能是公有的
如果这些条件中的任何一个匹配,Spring Data 将通过反射回退到实体实例化。
物业人口
一旦创立了实体的实例,Spring Data 就会填充该类的所有残余长久属性。除非实体的构造函数曾经填充(即通过其结构函数参数列表耗费),标识符属性将首先填充以容许循环对象援用的解析。之后,所有尚未由构造函数填充的非瞬态属性都在实体实例上设置。为此,咱们应用以下算法:
如果属性是不可变的但公开了一个 with…办法(见下文),咱们应用该 with…办法创立一个具备新属性值的新实体实例。
如果定义了属性拜访(即通过 getter 和 setter 拜访),咱们将调用 setter 办法。
如果属性是可变的,咱们间接设置字段。
如果属性是不可变的,咱们将应用持久性操作(请参阅对象创立)应用的构造函数来创立实例的正本。
默认状况下,咱们间接设置字段值。
财产人口外部
与咱们在对象结构中的优化相似,咱们也应用 Spring Data 运行时生成的拜访器类与实体实例进行交互。
class Person {
private final Long id;
private String firstname;
private @AccessType(Type.PROPERTY) String lastname;
Person() {
this.id = null;
}
Person(Long id, String firstname, String lastname) {
// Field assignments
}
Person withId(Long id) {
return new Person(id, this.firstname, this.lastame);
}
void setLastname(String lastname) {
this.lastname = lastname;
}
}
Spring Data JDBC 参考文档
示例 55. 生成的属性拜访器
class PersonPropertyAccessor implements PersistentPropertyAccessor {
private static final MethodHandle firstname;
private Person person;
public void setProperty(PersistentProperty property, Object value) {
String name = property.getName();
if (“firstname”.equals(name)) {
firstname.invoke(person, (String) value);
} else if (“id”.equals(name)) {
this.person = person.withId((Long) value);
} else if (“lastname”.equals(name)) {
this.person.setLastname((String) value);
}
}
}
Spring Data JDBC 参考文档
PropertyAccessor 持有底层对象的可变实例。这是为了启用其余不可变属性的渐变。
默认状况下,Spring Data 应用字段拜访来读取和写入属性值。依据 private 字段的可见性规定,MethodHandles 用于与字段进行交互。
该类公开了一个 withId(…)用于设置标识符的办法,例如,当一个实例插入到数据存储中并生成一个标识符时。调用 withId(…)创立一个新 Person 对象。所有后续渐变都将产生在新实例中,而前一个实例放弃不变。
应用属性拜访容许间接办法调用而不应用 MethodHandles.
这使咱们比反射进步了大概 25% 的性能。对于有资格进行此类优化的域类,它须要恪守一组束缚:
类型不得位于默认值或 java 包下。
类型及其构造函数必须是 public
属于外部类的类型必须是 static.
应用的 Java 运行时必须容许在原始 ClassLoader. Java 9 和更新版本施加了某些限度。
默认状况下,Spring Data 会尝试应用生成的属性拜访器,并在检测到限度时回退到基于反射的拜访器。
内容提醒:本文 (Spring Data JDBC 参考文档) 未完待续 ……