JPA Primary Key

Every entity stored in the database has a primary key. Once assigned, the primary key cannot be modified and represents the entity for as long as it exists in the database.

As an object database, ObjectDB supports implicit object IDs, so an explicit primary key is not required. However, ObjectDB also supports standard JPA primary keys, including explicit keys, composite primary keys, and automatic sequential value generation. This powerful feature is absent from many other object-oriented databases.

Entity identification

Every entity in the database is uniquely identified and can be retrieved by the combination of its type and its primary key. Primary key values must be unique for each entity class. Instances of different entity classes, however, may share the same primary key value.

Only entities have primary keys. Instances of other persistable types are always stored as part of their containing entities and do not have a separate identity.

Automatic primary key

By default, ObjectDB automatically sets the primary key for every new entity. The key is a sequential 64-bit number (long). The primary key of the first entity is 1, the second is 2, and so on. Primary key values are not recycled when entities are deleted from the database.

You can access an entity's primary key value by declaring a primary key field:

@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity.
public class Project {
    @Idjakarta.persistence.IdIdentifies the primary key of an entity. @GeneratedValuejakarta.persistence.GeneratedValueSpecifies a generation strategy for generated primary keys. long id; // still set automatically
     :
}

The @Idjakarta.persistence.IdIdentifies the primary key of an entity. annotation marks a field as a primary key field. When a primary key field is defined, ObjectDB automatically injects the primary key value into that field.

The @GeneratedValuejakarta.persistence.GeneratedValueSpecifies a generation strategy for generated primary keys. annotation specifies that the primary key is automatically allocated by ObjectDB. The Generated Values section discusses automatic value generation in detail.

Application-set primary key

If an entity has a primary key field that is not marked with @GeneratedValuejakarta.persistence.GeneratedValueSpecifies a generation strategy for generated primary keys., an automatic primary key value is not generated, and the application is responsible for setting a primary key by initializing the primary key field. That must be done before any attempt to persist the entity:

@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity.
public class Project {
    @Idjakarta.persistence.IdIdentifies the primary key of an entity. long id; // must be initialized by the application
     :
}

An application-set primary key field can have one of the following types:

  • Primitive types: boolean, byte, short, char, int, long, float, and double.
  • Equivalent wrapper classes from the java.lang package:
    Byte, Short, Character, Integer, Long, Float, and Double.
  • java.math.BigInteger and java.math.BigDecimal.
  • java.lang.String.
  • java.util.Date, java.sql.Date, java.sql.Time, and java.sql.Timestamp.
  • java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime,
    java.time.OffsetTime, java.time.OffsetDateTime, java.time.Instant.
  • Any enum type.
  • A reference to another entity.

Composite primary key

A composite primary key consists of multiple primary key fields. Each primary key field must be one of the supported types listed above.

For example, the primary key of the following Project entity class consists of two fields:

@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity. @IdClassjakarta.persistence.IdClassSpecifies a composite primary key type whose fields or properties map to the   identifier   fields or properties of the annotated entity class.(ProjectId.class)
public class Project {
    @Idjakarta.persistence.IdIdentifies the primary key of an entity. int departmentId;
    @Idjakarta.persistence.IdIdentifies the primary key of an entity. long projectId;
     :
}

When an entity has multiple primary key fields, JPA requires you to define a special ID class and attach it to the entity class by using the @IdClassjakarta.persistence.IdClassSpecifies a composite primary key type whose fields or properties map to the identifier fields or properties of the annotated entity class. annotation. The ID class reflects the primary key fields, and its objects can represent primary key values:

public class ProjectId {
    int departmentId;
    long projectId;
}

ObjectDB does not require you to define an ID class. However, an ID class is required to retrieve entities by their primary key, as shown in the Retrieving Entities section.

Embedded primary key

An alternative way to represent a composite primary key is to use an embeddable class:

@Entity
jakarta.persistence.EntityDeclares that the annotated class is an entity.public class Project {
    @EmbeddedIdjakarta.persistence.EmbeddedIdSpecifies that the annotated persistent field or property of an entity class or mapped superclass is the composite primary key of the entity. ProjectId id;
     :
}
@Embeddablejakarta.persistence.EmbeddableDeclares a type whose instances are stored as an intrinsic part of an owning entity, sharing the identity of the entity.
public class ProjectId {
    int departmentId;
    long projectId;
}

The primary key fields are defined in an embeddable class. The entity contains a single primary key field that is annotated with @EmbeddedIdjakarta.persistence.EmbeddedIdSpecifies that the annotated persistent field or property of an entity class or mapped superclass is the composite primary key of the entity. and holds an instance of that embeddable class. When you use this form, a separate ID class is not required because the embeddable class itself can represent complete primary key values.

Obtaining the primary key

JPA provides a generic method for getting the object ID (primary key) of a managed entity. For example:

PersistenceUnitUtiljakarta.persistence.PersistenceUnitUtilUtility interface between the application and the persistence provider managing the persistence unit. util = emf.getPersistenceUnitUtiljakarta.persistence.EntityManagerFactory.getPersistenceUnitUtil()Return interface providing access to utility methods for the persistence unit.();
Object projectId = util.getIdentifierjakarta.persistence.PersistenceUnitUtil.getIdentifier(Object)Return the id of the entity.(project);

You obtain a PersistenceUnitUtiljakarta.persistence.EntityManagerFactory.getPersistenceUnitUtil()Return interface providing access to utility methods for the persistence unit. instance from the EntityManagerFactoryjakarta.persistence.EntityManagerFactoryInterface used to interact with the persistence unit, and to create new instances of EntityManager .. The getIdentifierjakarta.persistence.PersistenceUnitUtil.getIdentifier(Object)Return the id of the entity. method takes one argument, a managed entity, and returns the primary key. For a composite primary key, the method returns an instance of the ID class or the embeddable class.

Using primary keys for object clustering

Entities are physically stored in the database ordered by their primary key. Sometimes, it is useful to choose a primary key that helps cluster entities efficiently in the database. This technique is especially useful for queries that return large result sets.

For example, consider a real-time system that detects events from various sensors and stores the details in a database. Each event is represented by an Event entity that holds the time, sensor ID, and other details. Suppose that queries to retrieve all events from a specific sensor in a specific period are common and return thousands of Event objects. In that case, the following primary key can significantly improve query performance:

@Entity
jakarta.persistence.EntityDeclares that the annotated class is an entity.public class Event {
    @EmbeddedIdjakarta.persistence.EmbeddedIdSpecifies that the annotated persistent field or property of an entity class or mapped superclass is the composite primary key of the entity. EventId id;
     :
}
@Embeddablejakarta.persistence.EmbeddableDeclares a type whose instances are stored as an intrinsic part of an owning entity, sharing the identity of the entity.
public class EventId {
    int sensorId;
    Date time;
}

Because entities are ordered in the database by their primary key, events from the same sensor during a period are stored contiguously and can be collected by accessing a minimum number of database pages.

On the other hand, such a primary key requires more storage space and is less efficient for store operations. This is especially true if there are many references to Event objects in the database, because references to entities hold primary key values. Therefore, you must consider all factors, and a benchmark might be necessary to evaluate the alternatives and select the best solution.