Java Platform, Enterprise Edition:
The Java EE Tutorial

Part VIII explores the Java Persistence API. This part contains the following chapters:

  • Chapter 37, "Introduction to the Java Persistence API"
    • 37.1, Entities
      • 37.1.4, Multiplicity in Entity Relationships
      • 37.1.5, Direction in Entity Relationships
  • Chapter 38, "Running the Persistence Examples"
  • Chapter 39, "The Java Persistence Query Language"
  • Chapter 40, "Using the Criteria API to Create Queries"
  • Chapter 41, "Creating and Using String-Based Criteria Queries"
  • Chapter 42, "Controlling Concurrent Access to Entity Data with Locking"
  • Chapter 43, "Creating Fetch Plans with Entity Graphs"
  • Chapter 44, "Using a Second-Level Cache with Java Persistence API Applications"

Example: The order Application

37.1.4 Multiplicity in Entity Relationships

Multiplicities are of the following types.

  • One-to-one
  • One-to-many
  • Many-to-one
  • Many-to-many

One-to-one

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.

One-to-many

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.

Many-to-one

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.

Many-to-many

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.

37.1.5 Direction in Entity Relationships

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.

Bidirectional Relationships

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.

  • The inverse side of a bidirectional relationship must refer to its owning side by using the mappedBy element of the @OneToOne, @OneToMany, or @ManyToMany annotation. The mappedBy element designates the property or field in the entity that is the owner of the relationship.
  • The many side of many-to-one bidirectional relationships must not define the mappedBy element. The many side is always the owning side of the relationship.
  • For one-to-one bidirectional relationships, the owning side corresponds to the side that contains the corresponding foreign key.
  • For many-to-many bidirectional relationships, either side may be the owning side.

Unidirectional Relationships

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

Bidirectional One-to-one Profile <-> Customer

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

One(owner)-to-one Profile -> Customer

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

One(inverse)-to-one Customer -> Profile

Bidirectional One-to-many CustomerOrder <-> LineItem

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

One(inverse)-to-many CustomerOrder -> LineItem

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

Many(owner)-to-one LineItem -> CustomerOrder

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

Bidirectional Many-to-many Product <-> Tag

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

Many-to-many Product -> Tag

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

Queries and Relationship Direction

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.

Cascade Operations and Relationships

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

ALL
All cascade operations will be applied to the parent entity's related entity. All is equivalent to specifying cascade={DETACH, MERGE, PERSIST, REFRESH, REMOVE}
DETACH
If the parent entity is detached from the persistence context, the related entity will also be detached.
MERGE
If the parent entity is merged into the persistence context, the related entity will also be merged.
PERSIST
If the parent entity is persisted into the persistence context, the related entity will also be persisted.

REFRESH
If the parent entity is refreshed in the current persistence context, the related entity will also be refreshed.
REMOVE
If the parent entity is removed from the current persistence context, the related entity will also be removed.

Orphan Removal in Relationships

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() { ... }