Retrieving JPA Entities
Jakarta Persistence (JPA) provides various ways to retrieve objects from the database. Retrieving objects does not require an active transaction because it does not change the database content.
The persistence context serves as a cache for retrieved entities. If a requested entity is not in the persistence context, JPA constructs a new object and populates it with data from the database or the Level 2 (L2) cache, if enabled. JPA then adds the new entity to the persistence context as a managed entity and returns it to the application.
The construction of a new managed object during retrieval uses the no-argument constructor. Therefore, you should avoid time-consuming operations in the no-argument constructors of entity classes and keep them as simple as possible.
Retrieval by class and primary key
Every entity can be uniquely identified and retrieved by the combination of its class and its primary key. Given an EntityManagerjakarta.persistence.EntityManagerInterface used to interact with the persistence context. instance em, the following example retrieves an Employee object with a primary key of 1:
Employee employee = em.findjakarta.persistence.EntityManager.find(Class,Object)Find by primary key.(Employee.class, 1);
You do not need to cast the retrieved object to Employee because findjakarta.persistence.EntityManager.find(Class,Object)Find by primary key. uses generics and returns an instance of the class that you provide as its first argument.
An IllegalArgumentException is thrown if the specified class is not an entity class.
If the EntityManager already manages the specified entity in its persistence context, it returns the existing managed object without querying the database. Otherwise, JPA retrieves the object's data from the database, constructs a new managed entity, and returns it. If the object is not in the database, find returns null.
A similar method, getReferencejakarta.persistence.EntityManager.getReference(Class,Object)Obtain a reference to an instance of the given entity class with the given primary key, whose state may be lazily fetched., is a lazy version of find:
Employee employee = em.getReferencejakarta.persistence.EntityManager.getReference(Class,Object)Obtain a reference to an instance of the given entity class with the given primary key, whose state may be lazily fetched.(Employee.class, 1);
The getReference method is similar to the find method, but if the entity is not already managed by the EntityManager, getReference might return a hollow object instead of querying the database immediately. It never returns null. A hollow object is initialized with a valid primary key, but all its other persistent fields are uninitialized. The object's content is retrieved from the database, and its persistent fields are initialized lazily when the entity is first accessed. If the requested object does not exist in the database, JPA throws an EntityNotFoundExceptionjakarta.persistence.EntityNotFoundExceptionThrown by the persistence provider when an entity reference obtained by EntityManager.getReference is accessed but the entity does not exist. when the object is first accessed.
The getReference method is useful when you need a reference to an entity but not its data, for example, to set a relationship on another entity.
Retrieval by eager fetch
Retrieval of Retrieving an entity from the database can trigger the automatic retrieval of related entities. By default, a retrieval operation cascades through all non-collection and non-map persistent fields, such as one-to-one and many-to-one relationships. Therefore, when you retrieve an entity, all other entities that are reachable from it through these fields are also retrieved. In extreme cases, this behavior can lead to retrieving the entire database into memory, which is often unacceptable.
You can exclude a persistent reference field from this automatic cascaded retrieval by specifying a lazy fetch type:
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. class Employee { : @ManyToOnejakarta.persistence.ManyToOneSpecifies a single-valued association to another entity class that has many-to-one multiplicity.(fetchjakarta.persistence.ManyToOne.fetch(Optional) Whether the association should be lazily loaded or must be eagerly fetched.=FetchTypejakarta.persistence.FetchTypeDefines strategies for fetching data from the database..LAZYjakarta.persistence.FetchType.LAZYData may be lazily fetched.) private Employee manager; : }
The default for non-collection and non-map references is FetchTypejakarta.persistence.FetchTypeDefines strategies for fetching data from the database..EAGERjakarta.persistence.FetchType.EAGERData must be eagerly fetched., which means the retrieval operation cascades through the field. Specifying FetchTypejakarta.persistence.FetchTypeDefines strategies for fetching data from the database..LAZYjakarta.persistence.FetchType.LAZYData may be lazily fetched. in either the @OneToOnejakarta.persistence.OneToOneSpecifies a single-valued association to another entity class that has one-to-one multiplicity. or @ManyToOnejakarta.persistence.ManyToOneSpecifies a single-valued association to another entity class that has many-to-one multiplicity. annotation excludes the field from cascaded retrieval. (ObjectDB currently does not distinguish between these two annotations.)
When an entity is retrieved, all its persistent fields are initialized. A persistent reference field with a FetchType.LAZY fetch policy is initialized with a reference to a new, managed hollow object, unless the referenced object is already managed by the EntityManager. In the preceding example, when an Employee instance is retrieved, its manager field might reference a hollow Employee instance. In a hollow object, the primary key is set, but other persistent fields remain uninitialized until they are accessed.
Conversely, the default fetch policy for persistent collection and map fields is FetchType.LAZY. Therefore, by default, when you retrieve an entity, any other entities that it references through its collection and map fields are not retrieved with it.
You can change this behavior with an explicit FetchType.EAGER setting:
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. class Employee { : @ManyToManyjakarta.persistence.ManyToManySpecifies a many-valued association with many-to-many multiplicity, mapping to an intermediate table called the join table .(fetchjakarta.persistence.ManyToMany.fetch(Optional) Whether the association should be lazily loaded or must be eagerly fetched.=FetchTypejakarta.persistence.FetchTypeDefines strategies for fetching data from the database..EAGERjakarta.persistence.FetchType.EAGERData must be eagerly fetched.) private Collection<Project> projects; : }
Specifying FetchTypejakarta.persistence.FetchTypeDefines strategies for fetching data from the database..EAGERjakarta.persistence.FetchType.EAGERData must be eagerly fetched. in either the @OneToManyjakarta.persistence.OneToManySpecifies a many-valued association with one-to-many multiplicity. or @ManyToManyjakarta.persistence.ManyToManySpecifies a many-valued association with many-to-many multiplicity, mapping to an intermediate table called the join table . annotation enables cascaded retrieval for the field. (ObjectDB currently does not distinguish between these two annotations.) In the preceding example, when an Employee instance is retrieved, all the referenced Project instances are also retrieved automatically.
Retrieval by navigation and
You can access all persistent fields of an entity freely, regardless of the fetch policy, as long as the EntityManager is open. This includes fields that reference entities that have not yet been loaded from the database and are represented by hollow objects. If the EntityManager is open when a hollow object is first accessed, its content is automatically retrieved from the database, and all its persistent fields are initialized.
From a developer's perspective, it appears as if the entire object graph is in memory. This illusion, based on ObjectDB's lazy, transparent activation and retrieval of objects, helps hide direct interaction with the database and simplifies database programming.
For example, after you retrieve an Employee instance from the database, the manager field might contain a hollow Employee entity:
Employee employee = em.findjakarta.persistence.EntityManager.find(Class,Object)Find by primary key.(Employee.class, 1); Employee managed = employee.getManager(); // might be hollow
If manager is a hollow object, transparent activation occurs when it is first accessed. For example:
String managerName = manager.getName();
Accessing a persistent field in a hollow object, such as the manager's name in the preceding example, triggers the retrieval of the missing content from the database and initializes all persistent fields.
As shown, the entire object graph is available for navigation, regardless of the fetch policy. The fetch policy, however, affects performance. Eager retrieval can minimize round trips to the database and improve performance, but retrieving unnecessary entities that are not in use decreases performance.
The fetch policy also affects entities that become detached, for example, when the EntityManager is closed. Transparent activation is not supported for detached objects. Therefore, only content that has already been fetched from the database is available in detached objects.
JPA provides methods to check if a specified entity or a persistent field is loaded. For example:
PersistenceUtiljakarta.persistence.PersistenceUtilUtility interface between the application and the persistence provider(s). util = Persistencejakarta.persistence.PersistenceBootstrap class used to obtain an EntityManagerFactory in Java SE environments..getPersistenceUtiljakarta.persistence.Persistence.getPersistenceUtil()Return the PersistenceUtil instance(); boolean isObjectLoaded = util.isLoadedjakarta.persistence.PersistenceUtil.isLoaded(Object)Determine the load state of an entity.(employee); boolean isFieldLoaded = util.isLoadedjakarta.persistence.PersistenceUtil.isLoaded(Object,String)Determine the load state of a given persistent attribute.(employee, "address");
As shown in the example, you obtain a PersistenceUtiljakarta.persistence.PersistenceUtilUtility interface between the application and the persistence provider(s). instance from the static getPersistenceUtiljakarta.persistence.Persistence.getPersistenceUtil()Return the PersistenceUtil instance method. It provides two isLoaded methods: one for checking an entity and another for checking a persistent field of an entity.
Retrieval by query
The Query Using queries is the most flexible method for retrieving objects from the database. The official query language for JPA is the Jakarta Persistence Query Language (JPQL). It enables you to retrieve objects from the database by using both simple and complex queries. JPA queries and JPQL are described in Chapter 4.
Retrieval by refresh
You can reload managed objects from the database by using the refreshjakarta.persistence.EntityManager.refresh(Object)Refresh the state of the given managed entity instance from the database, overwriting unflushed changes made to the entity, if any. method:
em.refreshjakarta.persistence.EntityManager.refresh(Object)Refresh the state of the given managed entity instance from the database, overwriting unflushed changes made to the entity, if any.(employee);
The content of the managed object in memory, including any changes, is discarded and replaced with data retrieved from the database. This can be useful to ensure that the application is working with the most up-to-date version of an entity, in case it was changed by another EntityManager since it was retrieved.
The refresh method throws an IllegalArgumentException if the argument is not a managed entity, including entities in the New, Removed, or Detached state. If the object no longer exists in the database, refresh throws an EntityNotFoundExceptionjakarta.persistence.EntityNotFoundExceptionThrown by the persistence provider when an entity reference obtained by EntityManager.getReference is accessed but the entity does not exist..
Cascading refresh
Marking a reference field with CascadeTypejakarta.persistence.CascadeTypeDefines the set of cascadable operations that are propagated to the associated entity..REFRESHjakarta.persistence.CascadeType.REFRESHCascade the refresh operation (or CascadeTypejakarta.persistence.CascadeTypeDefines the set of cascadable operations that are propagated to the associated entity..ALLjakarta.persistence.CascadeType.ALLCascade all operations, which includes REFRESHjakarta.persistence.CascadeType.REFRESHCascade the refresh operation) indicates that refreshjakarta.persistence.EntityManager.refresh(Object)Refresh the state of the given managed entity instance from the database, overwriting unflushed changes made to the entity, if any. operations should automatically cascade to entities that are referenced by that field. Multiple entities can be referenced by a collection field.
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. class Employee { : @OneToOnejakarta.persistence.OneToOneSpecifies a single-valued association to another entity class that has one-to-one multiplicity.(cascadejakarta.persistence.OneToOne.cascade(Optional) The operations that must be cascaded to the target of the association.=CascadeTypejakarta.persistence.CascadeTypeDefines the set of cascadable operations that are propagated to the associated entity..REFRESHjakarta.persistence.CascadeType.REFRESHCascade the refresh operation) private Address address; : }
In the preceding example, the Employee entity class contains an address field that references an Address instance, which is another entity class. Because of the CascadeType.REFRESH setting, when an Employee instance is refreshed, the operation automatically cascades to the referenced Address instance, which is also refreshed. Cascading can continue recursively if applicable, for example, to entities that the Address object references.