【问题标题】:How do I keep my Java model independent from JPA entities while avoiding code duplication [closed]如何使我的 Java 模型独立于 JPA 实体,同时避免代码重复 [关闭]
【发布时间】:2016-03-02 09:16:03
【问题描述】:

我的 Java 项目使用 JPA 作为持久性解决方案。随着我阅读越来越多的 JPA 教程和指南,我发现(可能是为了简单起见)每个作者都使用实体类作为他们的模型类。就代码可扩展性和可维护性而言,这显然是一个糟糕的设计选择:更改持久性解决方案的决定将意味着对整个模型进行完全重构(或重写)。

事情是,重写与每个实体类对应的模型类感觉不合适且不优雅,因为如果不是 JPA 注释,所有这些都将与相应的实体类相同(在我个人的情况下)。将它们写为实体类实现的接口也感觉不对,无论是因为当我可能需要使用不对应于实体的类扩展模型时出现潜在的不一致,还是因为在 Java 中泛型用于表示 @OneToMany@ManyToMany 关系不(也不应该)与抽象接口协变。

我现在将发布一些代码来说明我的担忧:

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;
    }
}

考虑到我上面提的问题,对应的模型类/接口应该如何实现?

【问题讨论】:

  • 为什么您认为更改持久性解决方案需要对模型类进行“完全重构或重写”?我认为可以将此类与另一种持久性技术原样使用,并且删除注释几乎不符合完全重构或重写的条件,是吗?特别是因为这些注释中的大多数都是可选的,并且只有在您想要覆盖列的默认名称时才需要?
  • 一个问题带有流控制注释,如 PrePersist PostPersist。一种新的持久性技术必须至少具有相同的断言。我认为情况会如此,但您清楚地看到,代码执行绑定到 JPA。这应该没什么大不了的,但它显示了对 JPA 的控制流依赖。所以注释不仅仅是类的元数据。它们也是 JPA 环境何时执行什么的数据。

标签: java jpa architecture modularity


【解决方案1】:

从我的角度来看,这是一个很好的设计。一个实体一个模型类。它是(或应该是)业务对象的体现,它是领域模型的一部分。

你是对的,你应该为持久化 API 构建你的模型。但我在上面的例子中没有看到你这样做的地方。除了注释,但那些不构成您的模型,它只是元数据。您甚至可以将其放入一个额外的映射文件中,并拥有直接的模型类。

如果您的意思是从现有数据库创建模型类并且生成的类不表达业务模型,那么您应该考虑您的数据库模型。

而且您可以随时更改持久性解决方案,我不明白为什么您需要在这里坚持使用 JPA。例如,从任何带有一些注释的实体类创建 JSON 将非常容易。

最后一件事:如果您没有在@OneToMany 中定义targetEntity 属性,JPA 仅使用集合的泛型类型。

【讨论】:

  • 我一定是忽略了 XML 选项,这正是我所需要的,谢谢!
【解决方案2】:

JPA-Objects的注解是对象-关系映射,所以JPA-Objects是对象-关系映射的一部分。

您是对的,您经常将 JPA 对象视为项目架构中的模型对象。我并不是说这种解决方案在任何情况下都足够糟糕。但是如果你想清楚你应该把对象关系映射层和业务层这两个方面分开。如果你这样做了,根据 SRP,你可能有技术冗余但没有语义冗余。 SRP 说一个代码片段应该只为一个目的而改变。如果映射发生变化,那么 ORM 层就是正确的位置。如果业务规则发生变化,请转到模型层。

当然,您可能有代码在层之间传输数据。 “字符串名称”可能出现在 JPA 对象和模型对象中。但它根本不是映射,它的缓存。缓存会产生让您不舒服的冗余。如果您不想缓存,请从模型层委托给 JPA 层。但请保持业务逻辑与 JPA 层分离。

毕竟:如果您有一个小型项目,那么您不会遇到仅使用 JPAObject 解决方案的任何实际问题。这一切都伴随着大小。然后我推荐 BusinessObject -> DAO -> JPAObject 结构。

【讨论】:

  • 我不同意你的分析:映射不是任何对象,它是注解(或映射文件)。如果业务逻辑发生变化,您将更改模型对象。如果映射发生更改,您将更改注释。但通常一件事会导致另一件事。如果我用 一个 地址建模了一个人,而现在我需要多个地址,我会将属性更改为 Collection 并使用 @OneToMany 对其进行注释。即使您为您的方法提供了一个很好的示例,我也会将其称为规则中的例外,因为这种情况非常罕见。我见过很多项目都有自己的size
  • 映射就是注解,我给你那个。但你不能走开。如果您创建对象关系映射,则您的对象会受到使用的 ORM 框架的限制。它将具有一定的结构,可能与目标域对象不同(但通常是)。您必须看一下:对于每个 JPA 映射,您都可以创建相应的类。但是你不能为每个类创建一个 JPA 映射。因此,对于您要映射的每个不符合域模型的类,您必须编写应该在 DAO 层而不是域中的额外代码。
  • 我不会离开 ;-) 我要问的只是域模型合理且我们无法创建映射的示例?我不需要为每个 类创建映射,我只需要为业务对象创建它。对于这些对象之间的几乎所有关系,您都会找到相应的映射。并且请添加一个示例,您将如何在您的方法中加载/存储“模型对象”,因为我觉得这会使事情变得更加复杂。我不得不承认服务层以某种方式将我们绑定到 JPA,但这不是问题。
  • 顺便说一句。我刚刚看到 OP 要求解决方案同时避免代码重复,这与您的答案相冲突。我想知道他为什么仍然接受它。
  • 我回答了这个问题:根据 SRP 没有代码重复。在某些情况下,它可能是足够的设计,但不是一个整体的“好”设计。我还提到了大多数项目中留下的努力。他们把它放在哪里,他们不得不问自己在哪里放置额外的代码以将 JPA-Objects 拉直到域层。 “我不需要为每个班级创建一个映射。” --> 问题是:是否至少有一个类不能被映射?答案是肯定的:从技术上讲,您至少有机会。但是,如果出现语义,它就变得几乎不可能了。
猜你喜欢
  • 2016-02-29
  • 1970-01-01
  • 2011-05-19
  • 1970-01-01
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
  • 2015-12-15
  • 1970-01-01
相关资源
最近更新 更多