简体   繁体   中英

Classes Relationships with JPA

I have a set of Java classes with the following UML diagram:

在此处输入图片说明

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

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

        }

My purpose is using JPA annotations to establish the different relationships between them. There is a composition relationship between Invoice and InvoiceDetail, which is resolved using @Embedded and @Embeddable annotations for Invoice and InvoiceDetail respectively. However, a problem appears by establishing the relationships between InvoiceDetail, Class3 and Class4. In these relationships InvoiceDetail must be annotated as @Entity. However, when a class is annotated at the same time as @Entity and @Embeddable, the corresponding server will throw a runtime error during the deployment. Basing on the information of this website , I have written the following possible solution:

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

Would be this right in order to resolve my problem?

Thanks in advance.

Although without knowing what the classes really are it is hard to tell, I suppose that you have a design problem. The composition between Class1 and Class2 says that any Class2 instance only exists within the lifecycle of a corresponding Class1 instance. But on the other hand you have Class3 instances and Class4 instances which can / must have a relationship to a Class2 instance.

What I'm trying to say is that from my point of view the relationship between Class1 and Class2 should be a simple association and not a composition. Following this path Class2 would be an Entity in JPA and then you should have your problem solved.

I usually use @Embeddable for classes whose instances never exist by themselfes and @Entity for any class whose instances can exist without other instances. An address for example could be implemented either way but not on the same system. Address would be @Embeddable if I don't want to link addresses but it had to be @Entity if I want to make sure the same address isn't saved in more than one row.


[edit: added after classes 1 and 2 were renamed to Invoice and InvoiceDetails]

Having a composition between Invoice and InvoiceDetails makes perfect sense. But I still think you should avoid the need of double personality for InvoiceDetails. I can think of two solutions (both refactorings):

  1. If you prefer having InvoiceDetails as @Embeddable you could change the associations of Class3 and Class4 to Invoice instead of InvoiceDetails. InvoiceDetails would still be traversable via the Invoice object.
  2. If you prefer keeping the associations as is you could declare InvoiceDetails to be an entity. You could still achieve your composition with a cascading delete (see javax.persistence.CascadeType). As it seems that InvoiceDetails already has it's own table, this probably is the better option.

I checked my JPA applications and haven't found any occurence of the same class being @Entity and @Embeddable. Honestly, I doubt if this is possible at all because the official javadoc of @Embeddable says:

Specifies a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity.

As @Entity has it's own identity, you would try to declare the same object having two identities - and this can't work.

[/edit]


[edit2: adding code for solution proposal #2]

This code should work with some assumptions (see below). This is the implementation of bi-directional navigation for a 1:n-relationship.

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

Assumptions: Tables are named like the entities, the foreign key column for invoice_details table is named "invoice_id" and both tables have a primary key column named "id". Note that the mappedBy-value "invoice" refers to the entity field while the name-value "invoice_id" refers to the database table. Be cautious when deleting an Invoice object whose InvoiceDetails still are referenced by your Class3 or Class4 instances - you have to release these references first.

For information about JPA refer to these resources:

[/edit]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM