领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多这是关于如何解决使用 Spring Data JDBC 时可能遇到的各种挑战的系列文章的第二篇文章。该系列包括:
Spring Data JDBC - 如何实现双向关系?(本文)
如果您不熟悉 Spring Data JDBC,则应首先阅读其介绍和这篇文章,其中解释了聚合在 Spring Data JDBC 上下文中的相关性。相信我,这很重要。
本文基于我在2021 年 Spring One 大会上发表的演讲的一部分。
Spring Data JDBC 没有对双向关系提供特殊支持。要理解为什么你实际上不需要任何支持,我们必须查看两种不同的关系:我们将聚合内的引用和聚合间的引用区分开来。
让我们首先看看聚合内部的引用。这些由 Spring Data JDBC 中的实际 Java 引用建模。这些引用总是从聚合根指向聚合内的实体。实际上,引用是从更靠近聚合根的实体指向更内部的实体。但是相同的论点适用,因此我们将只考虑聚合根和一个内部实体。
如果您遵循 DDD 的思想和规则,您永远不会直接访问内部实体。相反,当您想要操作内部实体时,您会在聚合根上调用一个方法,然后聚合根会在内部实体上调用相应的方法。如果该方法需要对聚合根的引用,您只需在调用内部实体上的方法时将其传递过去。中间实体也是如此。
但也许您有很多这样的方法,并且不想到处传递this
。在这种情况下,您只需在构造聚合时传递引用,而不是在方法调用期间传递。只是普通的 Java 代码,没有什么特别的。
例如,考虑一个Minion
及其Toy
,它应该有一个指向Minion
的引用,以便它可以告诉其所有者的姓名。Minion
将自身设置为所有玩具的主人。
class Minion {
@Id
Long id;
String name;
final Set<Toy> toys = new HashSet<>();
Minion(String name) {
this.name = name;
}
@PersistenceConstructor
private Minion(Long id, String name, Collection<Toy> toys) {
this.id = id;
this.name = name;
toys.forEach(this::addToy);
}
public void addToy(Toy toy) {
toys.add(toy);
toy.minion = this;
}
public void showYourToys() {
toys.forEach(Toy::sayHello);
}
}
class Toy {
String name;
@Transient // org.SPRINGframework.DATA...
Minion minion;
Toy(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("I'm " + name + " and I'm a toy of " + minion.name);
}
}
请注意,您需要使用 Spring Data 注解(而不是 JPA 注解)将这些反向引用设置为@Transient
。否则,Spring Data JDBC 将尝试持久化它们,这将导致无限循环。
聚合之间的引用情况更简单。这些引用不是由 Java 引用实现,而是使用被引用聚合的 ID,可选地包装在AggregateReference
中。
导航此类引用转换为使用目标聚合的存储库及其findById
方法。例如,一个Minion
可能引用其邪恶的主人,一个Person
。
class Minion {
@Id
Long id;
String name;
AggregateReference<Person, Long> evilMaster;
Minion(String name, AggregateReference<Person, Long> evilMaster) {
this.name = name;
this.evilMaster = evilMaster;
}
}
class Person {
@Id
Long id;
String name;
Person(String name) {
this.name = name;
}
}
给定一个Minion
,您现在可以加载其邪恶的主人。
@Autowired
PersonRepository persons;
//...
Minion minion = //...
Optional<Person> evilMaster = persons.findById(minion.evilMaster.getId());
为了反向导航关系,您可以在MinionRepository
中声明一个方法,该方法查找给定邪恶主人的相应爪牙。
interface MinionRepository extends CrudRepository<Minion, Long> {
@Query("SELECT * FROM MINION WHERE EVIL_MASTER = :id")
Collection<Minion> findByEvilMaster(Long id);
}
@Autowired
MinionRepository minions;
//...
Person evilMaster = // ...
Collection<Minion>findByEvilMaster(evilMaster.id);
使用 Spring Data JDBC 2.3,您不再需要使用@Query
注解,因为查询派生支持AggregateReference
作为参数类型。
虽然 Spring Data JDBC 没有对双向关系提供显式支持,但事实证明您不需要特殊支持。您只需要现有功能和标准 Java 代码。完整的示例代码可在 Spring Data 示例存储库中找到。这里有一个内部引用的示例和一个外部引用的示例。
还会有更多类似的文章。如果您希望我涵盖特定主题,请告诉我。