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

java - 与JPA的类关系-LMLPHP

 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,分别为Invoice和InvoiceDetail使用@Embedded和@Embeddable批注。但是,通过建立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;
  ...
}


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

提前致谢。

最佳答案

尽管很难知道这些类的真正含义,但是我认为您有一个设计问题。 Class1和Class2之间的组合表明,任何Class2实例仅存在于相应Class1实例的生命周期内。但是另一方面,您具有Class3实例和Class4实例,它们可以/必须与Class2实例具有关系。

我要说的是,从我的角度来看,Class1和Class2之间的关系应该是简单的关联,而不是组成。遵循此路径,Class2将成为JPA中的一个Entity,那么您应该已解决了问题。

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



[编辑:在第1类和第2类重命名为Invoice和InvoiceDetails之后添加]

在Invoice和InvoiceDetails之间有一个组合很有意义。但是我仍然认为您应该避免InvoiceDetails具有双重性格。我可以想到两种解决方案(两种重构):


如果您希望将InvoiceDetails设置为@Embeddable,则可以将Class3和Class4的关联更改为Invoice,而不是InvoiceDetails。 InvoiceDetails仍可通过Invoice对象遍历。
如果您希望保持原样的关联,则可以将InvoiceDetails声明为实体。您仍然可以通过级联删除来实现合成(请参见javax.persistence.CascadeType)。看来InvoiceDetails已经拥有了自己的表,所以这可能是更好的选择。


我检查了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_id”是指数据库表。
删除仍由您的Class3或Class4实例引用其InvoiceDetails的Invoice对象时要小心-您必须首先释放这些引用。

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


The Java EE 7 Tutorial: Persistence
Wikibooks: Java Persistence
Javadoc of Package javax.persistence


[/编辑]

08-07 08:36