Different Ways to Store JPA Entity Objects into a Database

In this article, we will discuss different ways to store entity objects in a database.
New entity objects can be stored in the database either explicitly by invoking the persist method or implicitly as a result of a cascade operation.
Learn complete JPA at JPA Tutorial - Java Persistence API 
Learn Hibernate ORM Framework at Hibernate Tutorial

We will discuss the following topics in this article:

  1. Explicit persist() method
  2. Referenced Embedded Objects
  3. Cascading Persist
  4. Global Cascading Persist
  5. Batch Store

1. Using Explicit persist() Method

The following code stores an instance of the Student entity class in the database:
private static void insertEntity() {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("PERSISTENCE");
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();

    Student student = new Student("Ramesh", "Fadatare", "[email protected]");
    entityManager.persist(student);
    entityManager.getTransaction().commit();
    entityManager.close();
    entityManagerFactory.close();
}
The Student instance is constructed as an ordinary Java object and its initial state is New. An explicit call to persist associates the object with an owner EntityManager em and changes its state to Managed. The new entity object is stored in the database when the transaction is committed.
An IllegalArgumentException is thrown by persist() method if the argument is not an instance of an entity class. Only instances of entity classes can be stored in the database independently. Objects of other persistable types can only be stored in the database embedded in containing entities (as field values).
A TransactionRequiredException is thrown if there is no active transaction when persist is called because operations that modify the database require an active transaction.

2. Referenced Embedded Objects

The advantage of using embedded objects is that they are reusable and you can map two entities to the same database table. The following code stores a User instance with a reference to an Address instance:
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private Name name;

    private String email;

    @Embedded
    @AttributeOverrides(value = {
        @AttributeOverride(name = "addressLine1", column = @Column(name = "house_number")),
        @AttributeOverride(name = "addressLine2", column = @Column(name = "street"))
    })
    private Address address;

    //getters and setters
}
@Embeddable
public class Address {
    private String addressLine1;

    private String addressLine2;

    private String city;

    private String state;

    private String country;

    private String zipCode;
    
   // getters and setters
Instances of persistable types other than entity classes are automatically stored embedded in containing entity objects. Therefore, if Address is defined as an embeddable class the User entity object is automatically stored in the database with its Address instance as an embedded object.
Notice that embedded objects cannot be shared by multiple entity objects. Each containing entity object should have its own embedded objects.
Check out a complete example of Embeddable and embedded objects at JPA Component Mapping Using @Embeddable and @Embedded Annotation

3. Cascading Persist

Marking a reference field with CascadeType.PERSIST (or CascadeType.ALL that also covers PERSIST) indicates that persist operations should be cascaded automatically to entity objects that are referenced by that field (multiple entity objects can be referenced by a collection field):
@Entity
class User {
     :
    @OneToOne(cascade=CascadeType.PERSIST)
    private Address address;
     :
}
In the example above, the User entity class contains an address field that references an instance of Address, which is another entity class. Due to the CascadeType.PERSIST setting, when a User instance is persisted the operation is automatically cascaded to the referenced Address instance which is then automatically persisted without the need for a separate persist call for Address. Cascading may continue recursively when applicable (e.g. to entity objects that the Address object references, etc.).

4. Global Cascading Persist

Instead of specifying CascadeType.PERSIST individually for every relevant reference field, it can be specified globally for any persistent reference, either by setting in a JPA portable way, by specifying the cascade-persist XML element in the XML mapping file:
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
 http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
   <persistence-unit-metadata>
     <persistence-unit-defaults>
       <cascade-persist/>
     </persistence-unit-defaults>
   </persistence-unit-metadata>
</entity-mappings>
The mapping file has to be located either in the default location, META-INF/orm.xml or in another location that is specified explicitly in the persistence unit definition (in persistence.xml).

5. Batch Store

Storing a large number of entity objects requires special consideration. The combination of the clear and flush methods can be used to save memory in large transactions:
  em.getTransaction().begin();
  for (int i = 1; i <= 1000000; i++) {
      Point point = new Point(i, i);
      em.persist(point);
      if ((i % 10000) == 0) {
          em.flush();
          em.clear();
      }
  }
  em.getTransaction().commit();
Storing a large number of entity objects can also be performed by multiple transactions:
  em.getTransaction().begin();
  for (int i = 1; i <= 1000000; i++) {
      Point point = new Point(i, i);
      em.persist(point);
      if ((i % 10000) == 0) {
          em.getTransaction().commit();
          em.clear();          
          em.getTransaction().begin();
      }
  }
  em.getTransaction().commit();
Splitting a batch store into multiple transactions is more efficient than using one transaction with multiple invocations of the flush and clear methods. So using multiple transactions is preferred when applicable.
Refer complete JPA CRUD operations example at JPA CRUD Example
Learn complete JPA at JPA Tutorial - Java Persistence API
Learn Hibernate ORM Framework at Hibernate Tutorial

Related Articles

Comments