Spring Data JDBC、引用和聚合
之前的博客文章中,我、描述了如何设置和使用 Spring Data JDBC。我还描述了使 Spring Data
原标题:Spring认证|Spring Data JDBC、引用和聚合
JDBC 比 JPA 更容易理解的前提。一旦您考虑参考资料,这就会变得有趣。作为第一个示例,请考虑以下域模型:
class PurchaseOrder {
private @Id Long id;
private String shippingAddress;
private Set items = new HashSet();
void addItem(int quantity, String product) {
items.add(createOrderItem(quantity, product));
}
private OrderItem createOrderItem(int quantity, String product) {
OrderItem item = new OrderItem();
item.product = product;
item.quantity = quantity;
return item;
}
}
class OrderItem {
int quantity;
String product;
}
此外,请考虑如下定义的存储库:
interface OrderRepository extends CrudRepository {
@Query("select count(*) from order_item")
int countItems();
}
如果您创建一个包含商品的订单,您可能希望所有订单都能够持久化。这正是发生的事情:
@Autowired OrderRepository repository;
@Test
public void createUpdateDeleteOrder() {
PurchaseOrder order = new PurchaseOrder();
order.addItem(4, "Captain Future Comet Lego set");
order.addItem(2, "Cute blue angler fish plush toy");
PurchaseOrder saved = repository.save(order);
assertThat(repository.count()).isEqualTo(1);
assertThat(repository.countItems()).isEqualTo(2);
…
此外,如果您删除PurchaseOrder,其所有项目也应被删除。再一次,事情就是这样。
…
repository.delete(saved);
assertThat(repository.count()).isEqualTo(0);
assertThat(repository.countItems()).isEqualTo(0);
}
但是,如果我们考虑句法相同但语义不同的关系呢?
class Book {
// …
Set authors = new HashSet();
}
当一本书绝版时,您将其删除。所有的作者都走了。当然不是你想要的,因为一些作者可能也写了其他书。现在,这没有意义。或者是吗?我认为确实如此。
为了理解为什么这确实有意义,我们需要退后一步,看看哪些存储库实际存在。这与一个反复出现的问题密切相关:您是否应该在 JPA 中每个表有一个存储库?
而正确且权威的答案是“NO”。存储库持久化并加载聚合。聚合是形成一个单元的一组对象,它应该始终保持一致。此外,它应该始终保持(和加载)在一起。它有一个对象,称为聚合根,它是唯一允许接触或引用聚合内部的对象。聚合根是传递给存储库以持久化聚合的内容。
这带来了一个问题:Spring Data JDBC 如何确定哪些是聚合的一部分,哪些不是?答案很简单:您可以通过遵循非瞬态引用从聚合根访问的所有内容都是聚合的一部分。
考虑到这一点, 的行为是OrderRepository完全合理的。 OrderItem实例是聚合的一部分,因此会被删除。 Author相反,实例不是Book聚合的一部分,因此不应被删除。所以他们不应该被Book类引用。
问题解决了。嗯,...不是真的。我们仍然需要存储和访问有关之间的关系的信息Book和Author。答案可以再次在领域驱动设计 (DDD) 中找到,它建议使用 ID 而不是直接引用。这适用于各种多对 x 关系。
如果多个聚合引用同一个实体,则该实体不能成为引用它的聚合的一部分,因为它只能是一个聚合的一部分。因此,任何多对一和多对多关系都必须仅通过引用 id 来建模。
如果你应用这一点,你可以实现多项目标:
您清楚地表示了聚合的边界。
您还完全解耦(至少在应用程序的域模型中)涉及的两个聚合。
这种分离可以在数据库中以不同的方式表示:
保持数据库的正常状态,包括所有外键。这意味着您必须确保以正确的顺序创建和保存聚合。
使用延迟约束,仅在事务的提交阶段进行检查。这可能会实现更高的吞吐量。它还编纂了最终一致性的一个版本,其中“最终”与事务的结束相关联。这也允许引用从不存在的聚合,只要它只在事务期间发生。这对于避免大量基础设施代码只是为了满足外键和非空约束可能很有用。
完全删除外键,实现真正的最终一致性。
将引用的聚合保留在不同的数据库中,甚至可能是 No SQL 存储。
无论您采取何种分离方式,即使是 Spring Data JDBC 强制执行的最低限度的分离,也会鼓励您的应用程序模块化。此外,如果您尝试过迁移一个真正有 10 年历史的单体应用程序,您就会了解它的价值。
使用 Spring Data JDBC,您可以对多对多关系进行建模,如下所示:
class Book {
private @Id Long id;
private String title;
private Set authors = new HashSet();
public void addAuthor(Author author) {
authors.add(createAuthorRef(author));
}
private AuthorRef createAuthorRef(Author author) {
Assert.notNull(author, "Author must not be null");
Assert.notNull(author.id, "Author id, must not be null");
AuthorRef authorRef = new AuthorRef();
authorRef.author = author.id;
return authorRef;
}
}
@Table("Book_Author")
class AuthorRef {
Long author;
}
class Author {
@Id Long id;
String name;
}
注意额外的类 ( AuthorRef),它代表 Book 聚合关于作者的知识。它可能包含有关作者的其他聚合信息,然后这些信息实际上会在数据库中复制。考虑到作者数据库可能与书籍数据库完全不同,这有很多事情要做。
另请注意,作者集是一个私有字段,实例的AuthorRef实例化发生在私有方法中。所以聚合之外的任何东西都不能直接访问它。Spring Data JDBC 绝不要求这样做,但 DDD 鼓励这样做。域将像这样使用:
@Test
public void booksAndAuthors() {
Author author = new Author();
author.name = "Greg L. Turnquist";
author = authors.save(author);
Book book = new Book();
book.title = "Spring Boot";
book.addAuthor(author);
books.save(book);
books.deleteAll();
assertThat(authors.count()).isEqualTo(1);
}
总结一下:Spring Data JDBC 不支持多对一或多对多关系。为了对这些进行建模,请使用 ID。这鼓励了领域模型的干净模块化。它还消除了人们必须解决的一整套问题,并学习推理这种映射是否可行。
按照类似的思路,避免双向依赖。聚合内的引用从聚合根到元素。聚合之间的引用由一个方向的 ID 表示。此外,如果您需要反向导航,请使用存储库中的查询方法。这使得明确无误地明确哪个聚合负责维护引用。
以下是示例使用的数据库结构。
Purchase_Order (
id
shipping_address
)
Order_Item (
purchase_order
quantity
product
);
Book (
id
title
)
Author (
id
name
)
Book_Author (
book
author
)
完整的示例代码可在Spring中国教育管理中心(Spring认证)数据示例库访问!
Spring Data JDBC、引用和聚合相关推荐
- Spring Data JDBC入门使用Demo
Spring Data JDBC入门使用Demo Spring Data JDBC is a simple, limited, opinionated ORM. Spring Data JDBC特点: ...
- Spring Data JDBC通用DAO实现–迄今为止最轻量的ORM
我很高兴宣布Spring Data JDBC存储库项目的第一个版本. 这个开放源代码库的目的是为基于Spring框架中 JdbcTemplate关系数据库提供通用,轻量且易于使用的DAO实现,与项目的 ...
- Spring Data JDBC自动生成的增删改查CRUD分页、排序SQL语句非常简洁没有多余的SQL
通过在application.properties文件中设置记录SQL日志 logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG ...
- spring data jdbc 基本使用
文章目录 配置 使用 创建表 创建实体类 创建操作接口Repository 读写数据 总结 官方文档: https://docs.spring.io/spring-data/jdbc/docs/1.0 ...
- 【转】Spring Data JDBC - Reference Documentation
Spring Data JDBC - Reference Documentation Jens Schauder, Jay Bryant, Mark Paluch, Bastian Wilhelm V ...
- Spring Data JDBC 详解
目录 一.JPA背景 二.Spring Boot 整合Spring data JDBC 1. 配置数据源 2. 配置Druid的admin后台 3. Spring-data-jdbc常用接口查询策略 ...
- spring data jdbc的简单教程
你需要先做一番配置,跟我来! 目录 配置springconfig 数据库和实体类配置. 数据库:就俩字段,一个是userid,一个是username 实体类: DAO层接口: 开始测试: 其他一些事项 ...
- Spring Data JPA 实战
课程介绍 <Spring Data JPA 实战>内容是基于作者学习和工作中实践的总结和升华,有一句经典的话:"现在的开发人员是站在巨人的肩上,弯道超车".因现在框架越 ...
- Spring Data JPA 五分钟快速入门和实践
Spring Data JPA(类似于Java Web 中的 DAO) 操作声明持久层的接口(Repository) 三个核心接口: CrudRepository PagingAndSortingRe ...
最新文章
- delphi下的MVC架构-eMVC
- 信阳哪些技校有学计算机的,2018年信阳十大技校排名 排名前十的学校有哪些
- MySQL数据库从入门到实战(四)
- 微软 .net 你更新这么快IDE vs2015 、语法糖 6.0、framework、‘吹得这么牛,然并用...
- 内存泄漏代码_调查内存泄漏第1部分–编写泄漏代码
- win10更新不动_win10沙盒功能和其他同类程序的区别
- 苹果新技术或让无线充电更便捷
- [Python] 切片函数:silce()
- C# 连接sql 2005
- html5文本域禁止拖动,textarea用法 TextArea怎么禁用行滚动条
- 奶制品的生产lingo软件编辑
- Java面试题总结(附答案)
- c语言 error c4028,在Visual Studio2010中,“警告C4028:正式參數1與聲明不同”顯示。...
- 等价类划分法用例设计
- 【锂电】锂电工艺大全
- Air202学习 三 (模块串口使用)
- Android中使用webview加载网页上的按钮点击失效
- ps修改头发颜色----和修改衣服颜色-------给褶皱的衣服添加图案
- 可恶的RunDll广告怎么关闭
- 成功解决Unable to allocate xxx MiB for an array with shape (xxxx, xxxx)
热门文章
- Google Earth Engine——可视化的美国加州圣华金河流域的100年气候预测模型
- 在Kotlin中@Suppress可能取到的值
- 明略科技吴明辉:每一次新数据都会带来商业模式的迭代 | 会员专栏
- RabbitMQ的Routing 路由模式(Direct)
- 基于STM32F103的液晶显示电子钟
- 微信公众平台菜单编辑php,Vue.js实现微信公众号菜单编辑器步骤详解(上)
- Xmind for Mac 绿色破解(包含激活序列码)
- 微信有鸿蒙版的的吗,微信鸿蒙版本下载-微信鸿蒙版本下载最新官方版 v7.0.20-游人手游网...
- C语言——简单图形打印学习
- STM32|HSE/HSI调频