Close

JPA - Cascading Entities

[Updated: Sep 4, 2017, Created: Aug 30, 2017]

JPA allows to propagate entity operations (like EntityManager#persist()) through entity relationships. That means with cascading enabled, if an entity A is persisted, then the entity B (related to A by a relationship e.g. ManyToOne) will also be persisted without explicitly being persisted.

CascadeType enum

package javax.persistence;

public enum CascadeType {
    /** Cascade all operations */
    ALL,
    /** Cascade persist operation */
    PERSIST,
    /** Cascade merge operation */
    MERGE,
    /** Cascade remove operation */
    REMOVE,
    /** Cascade refresh operation */
    REFRESH,
    /**
     * Cascade detach operation
     * @since Java Persistence 2.0
     */
    DETACH
}

Annotations with element cascade()

  • @OneToOne
  • @ManyToOne
  • @OneToMany
  • @ManyToMany

All above annotations have following element:

CascadeType[] cascade() default {};

Setting cascading Globally

The cascade-persist element of persistence.xml applies to all relationships in the persistence unit.

<entity-mappings .....>
   <persistence-unit-metadata>
     <persistence-unit-defaults>
       <cascade-persist/>
     </persistence-unit-defaults>
   </persistence-unit-metadata>
</entity-mappings>

Example

In the following example, we are going to use bidirectional @OneToMany/@ManyToOne relationship where both annotations going to have cascade = CascadeType.ALL.

@Entity
public class Customer {
  @Id
  @GeneratedValue
  private int id;
  private String name;
  private String address;
  @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
  private List<OrderItem> orderItems = new ArrayList<>();
    .............
  public void addOrderItem(String itemName, int qty) {
      OrderItem orderItem = new OrderItem();
      orderItem.setItemName(itemName);
      orderItem.setQuantity(qty);
      addOrderItem(orderItem);
  }

  public void addOrderItem(OrderItem orderItem) {
      orderItem.setCustomer(this);
      this.orderItems.add(orderItem);
  }
    .............
}
@Entity
public class OrderItem {
  @Id
  @GeneratedValue
  private int id;
  private String itemName;
  private int quantity;
  @ManyToOne(cascade = {CascadeType.ALL})
  @JoinColumn(name = "CUST_ID")
  private Customer customer;
    .............
}

Persisting and loading data

public class ExampleMain {

  public static void main(String[] args) {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("test1");
      try {
          printTables(emf);
          persistCustomerEntity(emf);
          persistOrderItemEntity(emf);
          nativeQueries(emf);
          loadEntities(emf);
      } finally {
          emf.close();
      }
  }

  private static void printTables(EntityManagerFactory emf) {
      EntityManager em = emf.createEntityManager();
      nativeQuery(em, "SHOW TABLES");
      nativeQuery(em, "SHOW COLUMNS from Customer");
      nativeQuery(em, "SHOW COLUMNS from OrderItem");
  }

  private static void nativeQueries(EntityManagerFactory emf) {
      System.out.println(" -- native queries --");
      EntityManager em = emf.createEntityManager();
      nativeQuery(em, "Select * from Customer");
      nativeQuery(em, "Select * from OrderItem");
      em.close();
  }

  private static void persistCustomerEntity(EntityManagerFactory emf) {
      System.out.println("-- Persisting Customer entity --");
      EntityManager em = emf.createEntityManager();

      Customer customer = new Customer();
      customer.setName("Maria Dorsey");
      customer.setAddress("1719 Shumaker Boulevard, Sylvania");
      customer.addOrderItem("GEN SSD", 2);
      customer.addOrderItem("SK Monitor", 3);

      em.getTransaction().begin();
      em.persist(customer);
      em.getTransaction().commit();

      em.close();
  }

  private static void persistOrderItemEntity(EntityManagerFactory emf) {
      System.out.println("-- Persisting OrderItem entity --");
      EntityManager em = emf.createEntityManager();

      OrderItem orderItem = new OrderItem();
      orderItem.setItemName("QK USB Drive");
      orderItem.setQuantity(5);

      Customer customer = new Customer();
      customer.setName("Heather Barton");
      customer.setAddress("563 Holbein Square, Woodbine");
      customer.addOrderItem(orderItem);

      em.getTransaction().begin();
      em.merge(orderItem);
      em.getTransaction().commit();

      em.close();
  }

  private static void loadEntities(EntityManagerFactory emf) {
      System.out.println("-- Loading Customer --");
      EntityManager em = emf.createEntityManager();
      List<Customer> customers = em.createQuery("Select t from Customer t")
                                     .getResultList();
      customers.forEach(System.out::println);

      System.out.println("-- Loading OrderItem --");
      List<OrderItem> orderItems = em.createQuery("Select t from OrderItem t")
                                      .getResultList();
      orderItems.forEach(System.out::println);
      em.close();
  }

  public static void nativeQuery(EntityManager em, String s) {
      System.out.printf("---------------------------%n'%s'%n", s);
      Query query = em.createNativeQuery(s);
      List list = query.getResultList();
      for (Object o : list) {
          if (o instanceof Object[]) {
              System.out.println(Arrays.toString((Object[]) o));
          } else {
              System.out.println(o);
          }
      }
  }
}

Output

---------------------------
'SHOW TABLES'
[CUSTOMER, PUBLIC]
[ORDERITEM, PUBLIC]
---------------------------
'SHOW COLUMNS from Customer'
[ID, INTEGER(10), NO, PRI, NULL]
[ADDRESS, VARCHAR(255), YES, , NULL]
[NAME, VARCHAR(255), YES, , NULL]
---------------------------
'SHOW COLUMNS from OrderItem'
[ID, INTEGER(10), NO, PRI, NULL]
[ITEMNAME, VARCHAR(255), YES, , NULL]
[QUANTITY, INTEGER(10), NO, , NULL]
[CUST_ID, INTEGER(10), YES, , NULL]
-- Persisting Customer entity --
-- Persisting OrderItem entity --
-- native queries --
---------------------------
'Select * from Customer'
[1, 1719 Shumaker Boulevard, Sylvania, Maria Dorsey]
[4, 563 Holbein Square, Woodbine, Heather Barton]
---------------------------
'Select * from OrderItem'
[2, GEN SSD, 2, 1]
[3, SK Monitor, 3, 1]
[5, QK USB Drive, 5, 4]
-- Loading Customer --
Customer{id=1, name='Maria Dorsey', address='1719 Shumaker Boulevard, Sylvania', orderItems=[OrderItem{id=2, itemName='GEN SSD', quantity=2}, OrderItem{id=3, itemName='SK Monitor', quantity=3}]}
Customer{id=4, name='Heather Barton', address='563 Holbein Square, Woodbine', orderItems=[OrderItem{id=5, itemName='QK USB Drive', quantity=5}]}
-- Loading OrderItem --
OrderItem{id=2, itemName='GEN SSD', quantity=2}
OrderItem{id=3, itemName='SK Monitor', quantity=3}
OrderItem{id=5, itemName='QK USB Drive', quantity=5}

As seen in above output, we just need to persist either Customer entity or OrderItem entity to save the other one. If we remove cascade attribute then either the related entity won't be saved or will end up in an exception.

Example Project

Dependencies and Technologies Used:

  • h2 1.4.193: H2 Database Engine.
  • hibernate-core 5.2.8.Final: The core O/RM functionality as provided by Hibernate.
    Implements javax.persistence:javax.persistence-api version 2.1
  • JDK 1.8
  • Maven 3.3.9

Cascading Bidirectional Relationship Example Select All Download
  • jpa-cascade-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • Customer.java
          • resources
            • META-INF

    See Also