类与JPA的关系

时间:2016-07-11 12:29:43

标签: java jpa composition model-associations

我有一组带有以下UML图的Java类:

enter image description here

 public class Invoice {
           @Id
          private long id;
...
        }

public class InvoiceDetail {
          @Id
          private long id;
          ...
          private String productName;
          private int quantity;
          private double price;

        }

我的目的是使用JPA注释来建立它们之间的不同关系。 Invoice和InvoiceDetail之间存在组合关系,resolved分别使用@Embedded和@Embeddable注释为Invoice和InvoiceDetail。但是,通过建立InvoiceDetail,Class3和Class4之间的关系会出现问题。在这些关系中,InvoiceDetail必须注释为@Entity。但是,当一个类与@Entity和@Embeddable同时注释时,相应的服务器将在部署期间抛出运行时错误。 基于此website的信息,我写了以下可能的解决方案:

@Entity
public class Invoice {
  @Id
  private long id;
  ...
  @ElementCollection
  @CollectionTable(name="INVOICEDETAIL", joinColumns=@JoinColumn(name="INVOICE_ID"))
  private List<InvoiceDetail> invoiceDetails;
  ...
}

为了解决我的问题,这是正确的吗?

提前致谢。

1 个答案:

答案 0 :(得分:1)

虽然不知道什么是真正的类,但很难说,我想你有一个设计问题。 Class1和Class2之间的组合表示任何Class2实例仅存在于相应Class1实例的生命周期内。但另一方面,你有Class3实例和Class4实例,它们/必须与Class2实例有关系。

我想说的是,从我的观点来看,Class1和Class2之间的关系应该是一个简单的关联,而不是一个组合。遵循此路径,Class2将成为JPA中的实体,然后您应该解决您的问题。

我通常将@Embeddable用于其实例从不存在的类,而@Entity用于其实例可以在没有其他实例的情况下存在的任何类。例如,地址可以以任何方式实现,但不能在同一系统上实现。如果我不想链接地址,地址将是@Embeddable,但如果我想确保相同的地址不保存在多行中,则必须是@Entity。

[编辑:在第1和第2课后重新命名为Invoice and InvoiceDetails]

在Invoice和InvoiceDetails之间进行合成非常有意义。但我仍然认为你应该避免InvoiceDetails需要双重人格。我可以想到两个解决方案(两个重构):

  1. 如果您希望将InvoiceDetails作为@Embeddable,则可以将Class3和Class4的关联更改为Invoice而不是InvoiceDetails。 InvoiceDetails仍然可以通过Invoice对象遍历。
  2. 如果您希望保持关联,则可以将InvoiceDetails声明为实体。您仍然可以通过级联删除来实现您的组合(请参阅javax.persistence.CascadeType)。由于InvoiceDetails似乎已经有了自己的表,这可能是更好的选择。
  3. 我检查了我的JPA应用程序,但没有发现同一个类是@Entity和@Embeddable。老实说,我怀疑这是否可行,因为official javadoc of @Embeddable说:

      

    指定一个类,其实例存储为拥有实体的内在部分,共享实体的标识

    由于@Entity拥有自己的身份,你会尝试声明具有两个身份的同一个对象 - 这不起作用。

    [/编辑]

    [edit2:为解决方案提案#2添加代码]

    此代码应该与一些假设一起使用(见下文)。这是1:n关系的双向导航的实现。

    @Entity
    public class Invoice {
      @Id
      private long id;
    
      @OneToMany(mappedBy="invoice", cascade = CascadeType.ALL)
      private List<InvoiceDetail> details;
    }
    
    @Entity
    public class InvoiceDetails {
      @Id
      private long id;
    
      @ManyToOne
      @JoinColumn(name="invoice_id")
      private Invoice invoice;
    }
    

    假设:表的名称与实体类似,invoice_details表的外键列名为“invoice_id”,两个表都有一个名为“id”的主键列。请注意,mappedBy值“invoice”是指实体字段,而name-value“invoice_id”是指数据库表。 删除InvoiceDetails仍然被Class3或Class4实例引用的Invoice对象时要小心 - 您必须先释放这些引用。

    有关JPA的信息,请参阅以下资源:

    [/编辑]