gpt4 book ai didi

java - 带有身份的 Spring Data 和 JPA + Hibernate 问题的 DDD 实现

转载 作者:行者123 更新时间:2023-12-04 12:59:01 26 4
gpt4 key购买 nike

所以我第一次尝试在一个不太复杂的项目中通过将我的所有代码分成应用程序来实现领域驱动设计基础设施接口(interface) 包。

我还进行了 JPA 实体到域模型的整体分离,这些模型将我的业务逻辑作为丰富的模型保存,并使用构建器模式进行实例化。这种方法让我很头疼,无法确定在使用 JPA + ORM 和 Spring Data with DDD 时我是否做错了。

过程说明该应用程序是一个 Rest API 使用者(无需任何用户交互),每天通过调度程序任务处理相当大量的数据资源,并将其存储或更新到 MySQL 中。我使用 RestTemplate 获取 JSON 响应并将其转换为域对象,然后我从那里应用域本身内的任何业务逻辑,例如验证、事件等

根据我的阅读,聚合根对象在其整个生命周期中应该有一个身份并且应该是唯一的。我使用了其余 API 对象的 ID,因为它已经是我用来在我的业务领域中识别和跟踪的东西。我还为技术 ID 创建了一个属性,因此当我将实体转换为域对象时,它可以保存更新过程的引用。

当我第一次需要将域持久保存到数据源 (MySQL) 时,我将它们转换为实体对象并使用 save() 持久保存它们。方法。到目前为止一切顺利。

现在,当我需要更新数据源中的这些记录时,我首先从数据源中将它们作为员工列表获取,将实体对象转换为域对象,然后从其余 API 中获取员工列表作为域模型。到目前为止,我有两个与 List<Employee> 相同的域对象类型列表。 .我正在使用 Streams 迭代它们并检查对象是否不是 equal()如果是,则在它们之间创建一个列表项集合作为第三个列表,其中包含需要更新的 Employee 对象。在这里,我已经将技术 ID 传递给了第三个员工列表中的域对象,因此 Hibernate 可以识别并用于更新已经存在的记录。

在我使用 saveAll() 之前,到这里都是相当简单的东西更新记录的方法。

问题

  • 我总是看到 Hibernate 使用 INSERT 而不是更新列表记录。因此,如果我正确,Hibernate session 无法识别我扔进去的东西,因为当我把它们分开时使用转换为域对象?
  • 有没有人知道我该如何以不同的方式实现或解决这个问题这个问题?
  • 或者我应该停止将这种方法用作两个不同的对象并继续使用它们作为丰富的实体模型?

用代码解释的简单类

EmployeeDO.java

@Entity
@Table(name = "employees")
public class EmployeeDO implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;

public EmployeeDO() {}

...omitted getter/setters
}

员工.java

public class Employee {

private Long persistId;
private Long employeeId;

private String name;

private Employee() {}

...omitted getters and Builder
}

EmployeeConverter.java

public class EmployeeConverter {

public static EmployeeDO serialize(Employee employee) {
EmployeeDO target = new EmployeeDO();

if (employee.getPersistId() != null) {
target.setId(employee.getPersistId());
}

target.setName(employee.getName());

return target;
}

public static Employee deserialize(EmployeeDO employee) {
return new Country.Builder(employee.getEmployeeId)
.withPersistId(employee.getId()) //<-- Technical ID setter
.withName(employee.getName())
.build();
}
}

EmployeeRepository.java

@Component
public class EmployeeReporistoryImpl implements EmployeeRepository {

@Autowired
EmployeeJpaRepository db;

@Override
public List<Employee> findAll() {
return db.findAll().stream()
.map(employee -> EmployeeConverter.deserialize(employee))
.collect(Collectors.toList());
}

@Override
public void saveAll(List<Employee> employees) {
db.saveAll(employees.stream()
.map(employee -> EmployeeConverter.serialize(employee))
.collect(Collectors.toList()));

}

}

EmployeeJpaRepository.java

@Repository
public interface EmployeeJpaRepository extends JpaRepository<EmployeeDO, Long> {

}

最佳答案

我在我的项目中使用相同的方法:域和持久性的两种不同模型。

首先,我建议您不要使用转换器方法,而是使用 Memento图案。您的域实体导出一个纪念品对象,它可以从同一个对象中恢复。是的,域有 2 个与域无关的函数(它们的存在只是为了提供非功能性需求),但是,另一方面,您避免公开域业务逻辑永远不会公开的函数、getter 和构造函数使用。

关于持久性部分,我不使用 JPA 正是因为这个原因:您必须编写大量代码才能正确地重新加载、更新和持久化实体。我直接编写 SQL 代码:我可以快速编写和测试它,一旦它工作,我确信它会做我想要的。使用 Memento 对象,我可以直接拥有我将在插入/更新查询中使用的内容,并且我避免了很多关于处理复杂表结构的 JPA 的麻烦。

无论如何,如果你想使用JPA,唯一的解决办法是:

  • 加载持久性实体并将它们转换为实体
  • 根据您必须在域中进行的更改更新实体
  • 保存实体,这意味着:
    • 重新加载持久性实体
    • 根据您从更新的实体中获得的更改更改或创建新的实体
    • 保存持久性实体

我尝试了一种混合解决方案,其中 实体由持久性 实体扩展(做起来有点复杂)。应格外小心,避免 模型适应来自持久性 模型的 JPA 限制。

Here关于这两种模型的 split ,有一篇有趣的读物。

最后,我的建议是考虑域的复杂程度,并使用最简单的解决方案来解决问题:

  • 它很大并且有很多复杂的行为吗?预计它会长大一个大?使用domainpersistence两种模型,直接用SQL管理持久化,避免了很多read/update/save阶段的caos。

    <
  • 简单吗?那么,首先,我应该使用 DDD 方法吗?如果真的是,我会让 JPA 注释在 内拆分。是的,这不是纯粹的 DDD,但我们生活在现实世界中,以纯粹的方式做一些简单的事情的时间不应该比我需要的时间大几个数量级一些妥协。而且,另一方面,我可以在基础设施层中用 XML 编写所有这些东西,避免用它弄乱。正如在 Spring DDD 示例中所做的那样 here .

关于java - 带有身份的 Spring Data 和 JPA + Hibernate 问题的 DDD 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61757512/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com