Part VIII explores the Java Persistence API. This part contains the following chapters:
Multiplicities are of the following types.
Each entity instance is related to a single instance of another entity.
One-to-one relationships use the javax.persistence.OneToOne annotation on the corresponding persistent property or field.
An entity instance can be related to multiple instances of the other entities.
One-to-many relationships use the javax.persistence.OneToMany annotation on the corresponding persistent property or field.
Multiple instances of an entity can be related to a single instance of the other entity.
This multiplicity is the opposite of a one-to-many relationship. In the example just mentioned, the relationship to CustomerOrder from the perspective of LineItem is many-to-one.
Many-to-one relationships use the javax.persistence.ManyToOne annotation on the corresponding persistent property or field.
The entity instances can be related to multiple instances of each other.
Many-to-many relationships use the javax.persistence.ManyToMany annotation on the corresponding persistent property or field.
The direction of a relationship can be either bidirectional or unidirectional.
A bidirectional relationship has both an owning side and an inverse side.
A unidirectional relationship has only an owning side.
The owning side of a relationship determines how the Persistence runtime makes updates to the relationship in the database.
In a bidirectional relationship, each entity has a relationship field or property that refers to the other entity. Through the relationship field or property, an entity class's code can access its related object. If an entity has a related field, the entity is said to "know" about its related object.
For example, if CustomerOrder knows what LineItem instances it has and if LineItem knows what CustomerOrder it belongs to, they have a bidirectional relationship.
Bidirectional relationships must follow these rules.
In a unidirectional relationship, only one entity has a relationship field or property that refers to the other.
For example, LineItem would have a relationship field that identifies Product, but Product would not have a relationship field or property for LineItem. In other words, LineItem knows about Product, but Product doesn't know which LineItem instances refer to it.
| Bidirection | Unidirection | |
|---|---|---|
| One(owner)-to-one | Profile <-> Customer | Profile -> Customer |
| One(inverse)-to-one | Customer -> Profile | |
| One(inverse)-to-many | CustomerOrder <-> LineItem | CustomerOrder -> LineItem |
| Many(owner)-to-one | LineItem -> CustomerOrder | |
| Many-to-many | Product <-> Tag | Product -> Tag |
@Entity
public class Profile {
@Id
@Column(name = "customer_id")
private long customerId;
@Column(name = "street_number")
private String streetNumber;
@Column(name = "street_name")
private String streetName;
private String district;
private String city;
@MapsId
@OneToOne
private Customer customer;
// Getters and Setters
}
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String email;
@Column(name = "created_date")
private Date createdDate;
@OneToOne(mappedBy = "customer")
private Profile profile;
// Getters and Setters
}
@Entity
public class Profile {
@Id
@Column(name = "customer_id")
private long customerId;
@Column(name = "street_number")
private String streetNumber;
@Column(name = "street_name")
private String streetName;
private String district;
private String city;
@MapsId
@OneToOne
private Customer customer;
// Getters and Setters
}
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String email;
@Column(name = "created_date")
private Date createdDate;
// @OneToOne(mappedBy = "customer")
// private Profile profile;
// Getters and Setters
}
@Embeddable
public class LineItemKey implements Serializable { // must be serializable.
@Column(name = "order_id")
private long orderId;
@Column(name = "product_id")
private long productId;
public LineItemKey() {
}
// Getters and Setter
// must implement the equals() and hashcode()
}
@Entity
@Table(name = "line_item")
public class LineItem {
@EmbeddedId
private LineItemKey lineItemKey;
private int quantity;
@Column(name = "item_price")
private BigDecimal itemPrice;
@Column(name = "sub_total")
private BigDecimal subTotal;
@ManyToOne
@MapsId("orderId")
private CustomerOrder order;
// Getters and Setters
}
@Entity
@Table(name = "customer_order")
public class CustomerOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private BigDecimal discount;
@Column(name = "created_date")
private Date createdDate;
@Column(name = "last_modified")
private Date lastModified;
private int status;
@OneToMany(mappedBy = "order")
private List<LineItem> items;
// Getters and Setters
}
@Embeddable
public class LineItemKey implements Serializable { // must be serializable.
@Column(name = "order_id")
private long orderId;
@Column(name = "product_id")
private long productId;
public LineItemKey() {
}
// Getters and Setter
// must implement the equals() and hashcode()
}
@Entity
@Table(name = "line_item")
public class LineItem {
@EmbeddedId
private LineItemKey lineItemKey;
private int quantity;
@Column(name = "item_price")
private BigDecimal itemPrice;
@Column(name = "sub_total")
private BigDecimal subTotal;
// @ManyToOne
// @MapsId("orderId")
// private CustomerOrder order;
// Getters and Setters
}
@Entity
@Table(name = "customer_order")
public class CustomerOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private BigDecimal discount;
@Column(name = "created_date")
private Date createdDate;
@Column(name = "last_modified")
private Date lastModified;
private int status;
@OneToMany
@JoinColumn(name = "order_id")
private List<LineItem> items;
// Getters and Setters
}
@Embeddable
public class LineItemKey implements Serializable { // must be serializable.
@Column(name = "order_id")
private long orderId;
@Column(name = "product_id")
private long productId;
public LineItemKey() {
}
// Getters and Setter
// must implement the equals() and hashcode()
}
@Entity
@Table(name = "line_item")
public class LineItem {
@EmbeddedId
private LineItemKey lineItemKey;
private int quantity;
@Column(name = "item_price")
private BigDecimal itemPrice;
@Column(name = "sub_total")
private BigDecimal subTotal;
@ManyToOne
@MapsId("orderId")
private CustomerOrder order;
// Getters and Setters
}
@Entity
@Table(name = "customer_order")
public class CustomerOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private BigDecimal discount;
@Column(name = "created_date")
private Date createdDate;
@Column(name = "last_modified")
private Date lastModified;
private int status;
// @OneToMany
// @JoinColumn(name = "order_id")
// private List<LineItem> items;
// Getters and Setters
}
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String description;
private BigDecimal price;
@ManyToMany
@JoinTable(name = "product_tag",
joinColumns = @JoinColumn(name = "product_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags;
// Getters and Setters
}
@Entity
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@ManyToMany(mappedBy = "tags")
private List<Product> products;
// Getters and Setters
}
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String description;
private BigDecimal price;
@ManyToMany
@JoinTable(name = "product_tag",
joinColumns = @JoinColumn(name = "product_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags;
// Getters and Setters
}
@Entity
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
// @ManyToMany(mappedBy = "tags")
// private List<Product> products;
// Getters Setters
}
Java Persistence query language and Criteria API queries often navigate across relationships.
The direction of a relationship determines whether a query can navigate from one entity to another.
For example, a query can navigate from LineItem to Product but cannot navigate in the opposite direction. For CustomerOrder and LineItem, a query could navigate in both directions because these two entities have a bidirectional relationship.
Entities that use relationships often have dependencies on the existence of the other entity in the relationship.
For example, a line item is part of an order; if the order is deleted, the line item also should be deleted. This is called a cascade delete relationship.
The javax.persistence.CascadeType enumerated type defines the cascade operations that are applied in the cascade element of the relationship annotations.
Cascade Operations for Entities
cascade={DETACH, MERGE, PERSIST, REFRESH, REMOVE}When a target entity in a one-to-one or one-to-many relationship is removed from the relationship, it is often desirable to cascade the remove operation to the target entity. Such target entities are considered "orphans," and the orphanRemoval attribute can be used to specify that orphaned entities should be removed. For example, if an order has many line items and one of them is removed from the order, the removed line item is considered an orphan. If orphanRemoval is set to true, the line item entity will be deleted when the line item is removed from the order.
The orphanRemoval attribute in @OneToMany and @oneToOne takes a Boolean value and is by default false.
@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<CustomerOrder> getOrders() { ... }