JPA Entity Fields
The fields of persistable user-defined classes (entity classes, embeddable classes, and mapped superclasses) can be classified into the following five groups:
- Transient fields
- Persistent fields
- Inverse (
mappedBy) fields - Primary key (ID) fields
- Version field
The first three groups (transient, persistent, and inverse fields) can be used in both entity classes and embeddable classes. However, the last two groups (primary key and version fields) can be used only in entity classes.
Primary key fields are discussed in the Primary Key section.
Transient fields
Transient entity fields are fields that don't participate in persistence, so their values are never stored in the database. This behavior is similar to transient fields in Java, which don't participate in serialization. Static and final entity fields are always considered transient. You can declare other fields as transient by using either the Java transient modifier (which also affects serialization) or the JPA @Transientjakarta.persistence.TransientSpecifies that the annotated property or field is not persistent. annotation (which affects only persistence):
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. public class EntityWithTransientFields { static int transient1; // not persistent because of static final int transient2 = 0; // not persistent because of final transient int transient3; // not persistent because of transient @Transientjakarta.persistence.TransientSpecifies that the annotated property or field is not persistent. int transient4; // not persistent because of @Transient }
The preceding entity class contains only transient (non-persistent) fields and has no data to store in the database.
Persistent fields
Every non-static non-final entity field is persistent by default unless specified otherwise (for example, by using the @Transientjakarta.persistence.TransientSpecifies that the annotated property or field is not persistent. annotation).
Storing an entity in the database stores only its persistent state, not its methods or code. This state is reflected by its persistent fields, including fields inherited from superclasses.
When an entity is stored in the database, each persistent field must contain either null or a value of one of the supported persistable types. ObjectDB supports persistent fields with any declared type, including a generic java.lang.Object, as long as the actual runtime value is a persistable type or null.
You can mark each persistent field with one of the following annotations:
@OneToOnejakarta.persistence.OneToOneSpecifies a single-valued association to another entity class that has one-to-one multiplicity.,@ManyToOnejakarta.persistence.ManyToOneSpecifies a single-valued association to another entity class that has many-to-one multiplicity.: For references to entity types.@OneToManyjakarta.persistence.OneToManySpecifies a many-valued association with one-to-many multiplicity.,@ManyToManyjakarta.persistence.ManyToManySpecifies a many-valued association with many-to-many multiplicity, mapping to an intermediate table called the join table .: For collections and maps of entity types.@Basicjakarta.persistence.BasicThe simplest type of mapping of a persistent field or property to a single database column.: For any other persistable type.
In JPA, only @Basicjakarta.persistence.BasicThe simplest type of mapping of a persistent field or property to a single database column. is optional; the other relationship annotations are required when applicable. However, ObjectDB doesn't require any of these annotations. They are useful only for classes that are also used with an ORM JPA provider, such as Hibernate, or for changing default field settings. For example:
@Entity jakarta.persistence.EntityDeclares that the annotated class is an entity.public class EntityWithFieldSettings { @Basicjakarta.persistence.BasicThe simplest type of mapping of a persistent field or property to a single database column.(optionaljakarta.persistence.Basic.optional(Optional) Specifies whether the value of the field or property may be null.=false) Integer field1; @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..ALLjakarta.persistence.CascadeType.ALLCascade all operations) MyEntity field2; @OneToManyjakarta.persistence.OneToManySpecifies a many-valued association with one-to-many multiplicity.(fetchjakarta.persistence.OneToMany.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.) List<MyEntity> field3; }
The preceding entity class declaration demonstrates how to use field and relationship annotations to change the default behavior. By default, null values are allowed. Specifying optional=false, as shown for field1, throws an exception if you try to store an entity with a null value in that field. Cascade and fetch settings are explained in Chapter 3.
You can mark a persistent field of an embeddable type with the @Embeddedjakarta.persistence.EmbeddedDeclares a persistent field or property of an entity whose value is an instance of an embeddable class. annotation. This annotation requires ObjectDB to verify that the type is embeddable:
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. public class Person { @Embeddedjakarta.persistence.EmbeddedDeclares a persistent field or property of an entity whose value is an instance of an embeddable class. Address address; }
Inverse fields
Inverse (or mappedBy) fields contain data that isn't stored as part of the entity in the database. Instead, this data is made available by an automatic query when the entity is retrieved.
Note: Navigation through inverse fields is much less efficient than navigation through ordinary persistent fields because it requires running queries. Inverse fields are essential for collection fields when using ORM JPA implementations, but not when using ObjectDB. Avoiding bidirectional relationships and inverse fields by maintaining two unidirectional relationships is usually more efficient, unless navigation in the inverse direction is rare.
The following entity classes show a bidirectional relationship:
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. public class Employee { String name; @ManyToOnejakarta.persistence.OneToManySpecifies a many-valued association with one-to-many multiplicity. Department department; } @Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. public class Department { @OneToManyjakarta.persistence.OneToManySpecifies a many-valued association with one-to-many multiplicity.(mappedByjakarta.persistence.OneToMany.mappedByThe field that owns the relationship.="department") Set<Employee> employees; }
The mappedByjakarta.persistence.OneToMany.mappedByThe field that owns the relationship. element in the preceding example specifies that the employees field is an inverse field, not a persistent field. The content of the employees set isn't stored as part of a Department entity. Instead, employees is automatically populated when a Department entity is retrieved from the database. ObjectDB accomplishes this by effectively running the following query, where :d represents the Department entity:
SELECT e FROM Employee e WHERE e.department = :d
The mappedByjakarta.persistence.OneToMany.mappedByThe field that owns the relationship. element defines a bidirectional relationship. In a bidirectional relationship, the side that stores the data (the Employee class in this example) is the owner. Only changes to the owner side affect the database because the other side isn't stored and is instead calculated by a query.
An index on the owner field can accelerate the inverse query and the loading of the inverse field. However, even with an index, executing a query to load a field is relatively slow. Therefore, if the employees field is used often, a persistent field is more efficient than an inverse field. In that case, the Employee and Department classes manage two unidirectional, unrelated relationships, and the application is responsible for keeping them synchronized.
Inverse fields can improve efficiency when you manage very large collections that change often. This is because a change in the inverse field doesn't require storing the entire collection again; only the owner side is stored in the database.
Special settings are available for inverse fields of type List or Map. For an inverse list field, you can set the order of the retrieved owner entities by using the @OrderByjakarta.persistence.OrderBySpecifies the ordering of the elements of a collection-valued association or element collection at the point when the association or collection is retrieved. annotation:
@Entity public class Department { @OneToManyjakarta.persistence.OneToManySpecifies a many-valued association with one-to-many multiplicity.(mappedByjakarta.persistence.OneToMany.mappedByThe field that owns the relationship.="department") @OrderByjakarta.persistence.OrderBySpecifies the ordering of the elements of a collection-valued association or element collection at the point when the association or collection is retrieved.("name") List<Employee> employees; }
In this case, the employees field is populated with the results of the following query:
SELECT e FROM Employee e WHERE e.department = :d ORDER BY e.name
The specified field ("name") must be a sortable field on the owner side.
For an inverse map field, you can extract the keys from the inverse query results by specifying the key field with the @MapKeyjakarta.persistence.MapKeySpecifies the map key for associations of type Map<K,V> when the map key is itself the primary key or a persistent field or property of the entity that is the value of the map. annotation:
@Entity public class Department { @OneToManyjakarta.persistence.OneToManySpecifies a many-valued association with one-to-many multiplicity.(mappedByjakarta.persistence.OneToMany.mappedByThe field that owns the relationship.="department") @MapKeyjakarta.persistence.MapKeySpecifies the map key for associations of type Map<K,V> when the map key is itself the primary key or a persistent field or property of the entity that is the value of the map.(namejakarta.persistence.MapKey.name(Optional) The name of the persistent field or property of the associated entity that is used as the map key.="name") Map<String,Employee> employees; }
The employees map is populated with a mapping of employee names to their corresponding Employee objects.
Single-value inverse fields are also supported:
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. public class Employee { @OneToOnejakarta.persistence.OneToOneSpecifies a single-valued association to another entity class that has one-to-one multiplicity. MedicalInsurance medicalInsurance; } @Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. public class MedicalInsurance { @OneToOnejakarta.persistence.OneToOneSpecifies a single-valued association to another entity class that has one-to-one multiplicity.(mappedByjakarta.persistence.OneToOne.mappedBy(Optional) The field that owns the relationship.="medicalInsurance") Employee employee; }
A single-value inverse field is less efficient than an inverse collection or map field because no proxy class is used and the inverse query is executed eagerly when the entity is first accessed.
Version Field
ObjectDB maintains a version number for every entity. The initial version of a new entity is 1 when it's stored in the database for the first time. In every transaction that modifies an entity, its version number is automatically incremented by one. Version fields are used with optimistic locking, as explained in the Locking in JPA section in Chapter 3.
You can expose entity version values to your application by marking a field in your entity class with the @Versionjakarta.persistence.VersionDeclares the version field or property of an entity class, which is used to detect optimistic lock failures, ensuring the integrity of optimistic transactions. annotation. A version field must have a numeric type:
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. public class EntityWithVersionField { @Versionjakarta.persistence.VersionDeclares the version field or property of an entity class, which is used to detect optimistic lock failures, ensuring the integrity of optimistic transactions. long version; }
If a version field exists, ObjectDB automatically injects the version value into that field. Version fields should be treated as read-only by the application, and you shouldn't write mutator methods for them. Only one version field is allowed per entity class. A single version field per entity class hierarchy is sufficient because subclasses inherit the version field.
Unlike ORM JPA providers, ObjectDB always manages versions for entities, regardless of whether a version field is explicitly defined. Therefore, ObjectDB supports optimistic locking even when a version field isn't defined. Nevertheless, defining a version field has some advantages:
- The application becomes more portable to ORM-based JPA implementations.
- Exposing version values can be useful for debugging and logging, even if the application doesn't use them directly.
- Version values can't be preserved for detached entities (explained in Chapter 3) unless the entity class is enhanced (explained in Chapter 5) or a version field is explicitly defined.
Property access
When an entity is stored in the database, data is extracted from its persistent fields. Likewise, when an entity is retrieved, its persistent fields are initialized with data from the database.
By default, ObjectDB accesses fields directly. However, it also supports accessing fields indirectly as properties by using getter and setter methods. To use property access mode, each non-transient field must have getter and setter methods that follow the JavaBean property convention.
To enable property access, move all JPA annotations from the fields to their respective getter methods and add the @Accessjakarta.persistence.AccessUsed to specify an access type to be applied to an entity class, mapped superclass, or embeddable class, or to a specific attribute of such a class. annotation to the class:
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. @Accessjakarta.persistence.AccessUsed to specify an access type to be applied to an entity class, mapped superclass, or embeddable class, or to a specific attribute of such a class.(AccessTypejakarta.persistence.AccessTypeUsed with the Access annotation to specify an access type to be applied to an entity class, mapped superclass, or embeddable class, or to a specific attribute of such a class..PROPERTYjakarta.persistence.AccessType.PROPERTYProperty-based access is used, that is, state is accessed via getter and setter methods.) public static class PropertyAccess { private int _id; @Idjakarta.persistence.IdIdentifies the primary key of an entity. int getId() { return _id; } void setId(int id) { _id = id; } private String str; String getStr() { return str; } void setStr(String str) { this.str = str; } }
In some JPA implementations, such as Hibernate, using property access can have performance benefits. This isn't the case with ObjectDB. Therefore, because of the extra complexity involved in setting up and maintaining property access, the default field access mode is usually preferred.