如何在避免代码重复的同时保持我的 Java 模型独立于 JPA 实体
How do I keep my Java model independent from JPA entities while avoiding code duplication
我的 Java 项目使用 JPA 作为持久性解决方案。随着我阅读越来越多的 JPA 教程和指南,我发现(也许是为了简单起见)每个作者都使用实体 classes 作为他们的模型 classes。就代码可扩展性和可维护性而言,这显然是一个糟糕的设计选择:更改持久性解决方案的决定意味着对整个模型进行完全重构(或重写)。
事实是,重写对应于每个实体 class 的模型 classes 感觉不合时宜且不优雅,因为所有这些都将与对应的实体 class 相同(在我个人看来案例),如果不是 JPA 注释。将它们作为实体 classes 实现的接口注销也感觉不对,因为当我可能需要使用与实体不对应的 class 扩展模型时出现潜在的不一致,或者因为 Java 中用于表示 @OneToMany
和 @ManyToMany
关系的泛型不是(也不应该)与抽象接口协变的事实。
我现在 post 一些代码来举例说明我的担忧:
package com.depvin.pps.orm;
import javax.persistence.*;
@Entity
@DiscriminatorValue("E")
public class EmployeeEntity extends UserEntity {
@ManyToMany
@JoinTable(name = "EmployeeProject",
joinColumns = @JoinColumn(name = "employee"),
inverseJoinColumns = @JoinColumn(name = "project"))
List<ProjectEntity> projects;
public EmployeeEntity(String username) {
super(username);
}
public List<ProjectEntity> getProjects() {
return projects;
}
}
考虑到我上面提到的问题,相应的模型class/interface应该如何实现?
从我的角度来看,这是一个很好的设计。实体 是 模型 class。它是(或应该是)业务对象的体现,是领域模型的一部分。
你是对的,你应该不建立持久性模型API。但是我在上面的示例中看不到您这样做的地方。除了注释,但那些并不构成您的模型,它只是元数据。你甚至可以把它放到一个额外的映射文件中,并且会有直接模型 classes.
如果您的意思是您从现有数据库创建模型 classes 并且生成的 classes 不表达业务模型,那么您应该考虑您的数据库模型。
而且您可以随时更改持久性解决方案,我不明白为什么您需要在这里坚持使用 JPA。例如,从任何实体 class 创建一个 JSON 非常容易,只需要一些注释。
最后一件事:如果您没有在 @OneToMany
.
中定义 targetEntity
属性,JPA 仅使用集合的泛型类型
JPA-Objects的注解是对象-关系映射,所以JPA-Objects是对象-关系映射的一部分。
你是对的,你经常在项目架构中看到 JPA 对象作为模型对象。我并不是说这个解决方案在任何情况下都足够糟糕。但是如果你想清楚你应该把对象关系映射层和业务层这两个方面分开。如果你这样做了,你可能有技术冗余但没有根据 SRP 的语义冗余。 SRP 说一个代码片段应该只为一个目的而改变。如果映射发生变化,那么 ORM 层就是正确的位置。如果业务规则发生变化,则转到模型层。
当然你可能有代码在层之间传输数据。 "String name" 可能出现在 JPA 对象和模型对象中。但它根本不是映射,它是缓存。缓存会产生让你不舒服的冗余。如果您不想缓存,请将模型层委托给 JPA 层。但要将业务逻辑与 JPA 层分开。
毕竟:如果您有一个小项目,您将不会遇到仅 JPAObject 解决方案的任何实际问题。这一切都带有尺寸。然后我推荐 BusinessObject -> DAO -> JPAObject 结构。
我的 Java 项目使用 JPA 作为持久性解决方案。随着我阅读越来越多的 JPA 教程和指南,我发现(也许是为了简单起见)每个作者都使用实体 classes 作为他们的模型 classes。就代码可扩展性和可维护性而言,这显然是一个糟糕的设计选择:更改持久性解决方案的决定意味着对整个模型进行完全重构(或重写)。
事实是,重写对应于每个实体 class 的模型 classes 感觉不合时宜且不优雅,因为所有这些都将与对应的实体 class 相同(在我个人看来案例),如果不是 JPA 注释。将它们作为实体 classes 实现的接口注销也感觉不对,因为当我可能需要使用与实体不对应的 class 扩展模型时出现潜在的不一致,或者因为 Java 中用于表示 @OneToMany
和 @ManyToMany
关系的泛型不是(也不应该)与抽象接口协变的事实。
我现在 post 一些代码来举例说明我的担忧:
package com.depvin.pps.orm;
import javax.persistence.*;
@Entity
@DiscriminatorValue("E")
public class EmployeeEntity extends UserEntity {
@ManyToMany
@JoinTable(name = "EmployeeProject",
joinColumns = @JoinColumn(name = "employee"),
inverseJoinColumns = @JoinColumn(name = "project"))
List<ProjectEntity> projects;
public EmployeeEntity(String username) {
super(username);
}
public List<ProjectEntity> getProjects() {
return projects;
}
}
考虑到我上面提到的问题,相应的模型class/interface应该如何实现?
从我的角度来看,这是一个很好的设计。实体 是 模型 class。它是(或应该是)业务对象的体现,是领域模型的一部分。
你是对的,你应该不建立持久性模型API。但是我在上面的示例中看不到您这样做的地方。除了注释,但那些并不构成您的模型,它只是元数据。你甚至可以把它放到一个额外的映射文件中,并且会有直接模型 classes.
如果您的意思是您从现有数据库创建模型 classes 并且生成的 classes 不表达业务模型,那么您应该考虑您的数据库模型。
而且您可以随时更改持久性解决方案,我不明白为什么您需要在这里坚持使用 JPA。例如,从任何实体 class 创建一个 JSON 非常容易,只需要一些注释。
最后一件事:如果您没有在 @OneToMany
.
targetEntity
属性,JPA 仅使用集合的泛型类型
JPA-Objects的注解是对象-关系映射,所以JPA-Objects是对象-关系映射的一部分。
你是对的,你经常在项目架构中看到 JPA 对象作为模型对象。我并不是说这个解决方案在任何情况下都足够糟糕。但是如果你想清楚你应该把对象关系映射层和业务层这两个方面分开。如果你这样做了,你可能有技术冗余但没有根据 SRP 的语义冗余。 SRP 说一个代码片段应该只为一个目的而改变。如果映射发生变化,那么 ORM 层就是正确的位置。如果业务规则发生变化,则转到模型层。
当然你可能有代码在层之间传输数据。 "String name" 可能出现在 JPA 对象和模型对象中。但它根本不是映射,它是缓存。缓存会产生让你不舒服的冗余。如果您不想缓存,请将模型层委托给 JPA 层。但要将业务逻辑与 JPA 层分开。
毕竟:如果您有一个小项目,您将不会遇到仅 JPAObject 解决方案的任何实际问题。这一切都带有尺寸。然后我推荐 BusinessObject -> DAO -> JPAObject 结构。