JPA Entity Class Basics

In this post, we will learn how to create a JPA entity, what are rules to create a JPA entity, and what are features JPA Entity provides like cascading, lazy, relationships, inheritance, etc.

Table of contents

  1. Defining a JPA Entity Class
  2. Requirements for Entity Classes
  3. Persistent Fields and Properties in Entity Classes
  4. Persistent Fields
  5. Persistent Properties
  6. Using Collections in Entity Fields and Properties
  7. Primary Keys in Entities
  8. Relationship Mapping
  9. Cascade Operations and Relationships
  10. Orphan Removal in Relationships
  11. Embeddable Classes in Entities

1. Defining a JPA Entity Class

An entity is a lightweight persistence domain object. Typically, an entity represents a table in a relational database, and each entity instance corresponds to a row in that table. The primary programming artifact of an entity is the entity class, although entities can use helper classes.

The Point Entity Class Example

To be able to store Point objects in the database using JPA we need to define an entity class. A JPA entity class is a POJO (Plain Old Java Object) class, i.e. an ordinary Java class that is marked (annotated) as having the ability to represent objects in the database. Conceptually this is similar to serializable classes, which are marked as having the ability to be serialized.
The following Point class, which represents points in the plane, is marked as an entity class, and accordingly, provides the ability to store Point objects in the database and retrieve Point objects from the database:
@Entity
public class Point {
    // Persistent Fields:
    private int x;
    private int y;

    // Constructor:
    Point (int x, int y) {
        this.x = x;
        this.y = y;
    }

    // Accessor Methods:
    public int getX() { return this.x; }
    public int getY() { return this.y; }

    // String Representation:
    @Override
    public String toString() {
        return String.format("(%d, %d)", this.x, this.y);
    }
}
As you can see above, an entity class is an ordinary Java class. The only unique JPA addition is the @Entity annotation, which marks the class as an entity class.

2. Requirements for Entity Classes

Let's discuss what are the rules or requirements to create a JPA entity class. An entity class must follow these requirements.
  • The class must be annotated with the jakarta.persistence.Entity annotation.
  • The class must have a public or protected, no-argument constructor. The class may have other constructors.
  • The class must not be declared final. No methods or persistent instance variables must be declared final.
  • If an entity instance is passed by value as a detached object, such as through a session bean's remote business interface, the class must implement the Serializable interface.
  • Entities may extend both entity and non-entity classes, and non-entity classes may extend entity classes.
  • Persistent instance variables must be declared private, protected, or package-private and can be accessed directly only by the entity class's methods. Clients must access the entity's state through accessor or business methods.

3. Persistent Fields and Properties in Entity Classes

The persistent state of an entity can be accessed through either the entity's instance variables or properties. 
The fields or properties must be of the following Java language types:
  1. java.lang.String
  2. Other serializable types, including:
  • Wrappers of Java primitive types
  • java.math.BigInteger
  • java.math.BigDecimal
  • java.util.Date
  • java.util.Calendar
  • java.sql.Date
  • java.sql.Time
  • java.sql.TimeStamp
  • User-defined serializable types
  • byte[]
  • Byte[]
  • char[]
  • Character[]
  1. Other entities and/or collections of entities

4. Persistent Fields

If the entity class uses persistent fields, the Persistence runtime accesses entity-class instance variables directly. All fields not annotated jakarta.persistence.Transient or not marked as Java transient will be persisted to the data store. The object/relational mapping annotations must be applied to the instance variables.
By default, each field is mapped to a column with the name of the field. You can change the default name via @Column (name="newColumnName").
The following typical annotations can be used for fields/getter and setter
  • @Id - Identifies the unique ID of the database entry
  • @GeneratedValue - Together with an ID this annotation defines that this value is generated automatically.
  • @Transient - Field will not be saved in a database
Example:
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;

    @Column(name = "first_name")
    private String firstName;
    
    @Transient // Field will not be saved in a database
    private String lastName;
    // getter and setter methods
}

5. Persistent Properties

Here persistent properties mean getter and setter methods. If the entity uses persistent properties, the entity must follow the method conventions of JavaBeans components. JavaBeans-style properties use getter and setter methods that are typically named after the entity class's instance variable names.
The object/relational mapping annotations for persistent properties must be applied to the getter methods. Mapping annotations cannot be applied to fields or properties annotated @Transient or marked transient.
Example: Apply mapping annotations to getter methods
@Entity
@Table(name = "student")
public class Student {
   
    private int id;  
    private String firstName;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(name = "first_name")
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

6. Using Collections in Entity Fields and Properties

Collection-valued persistent fields and properties must use the supported Java collection interfaces regardless of whether the entity uses persistent fields or properties. 
The following collection interfaces may be used:
  • java.util.Collection
  • java.util.Set
  • java.util.List
  • java.util.Map
For example, the below example uses Set:
    @ManyToMany(mappedBy = "projects", cascade = { CascadeType.ALL })
    private Set<Employee> employees = new HashSet<Employee>();
For example, the below example uses List:
    @OneToMany(mappedBy = "instructor", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH,
      CascadeType.REFRESH })
    private List<Course> courses;

7. Primary Keys in Entities

Every JPA entity must have a primary key. An entity may have either a simple or a composite primary key.
Simple primary keys use the jakarta.persistence.Id annotation to denote the primary key property or field.
For example: Configuring a JPA Entity Simple Primary Key Field
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;

    @Column(name = "first_name")
    private String firstName;
    
    // getter and setter methods
}
Composite primary keys are used when a primary key consists of more than one attribute, which corresponds to a set of single persistent properties or fields. Composite primary keys must be defined in a primary key class. 
Composite primary keys are denoted using the jakarta.persistence.EmbeddedId and jakarta.persistence.IdClass annotations.
Example 1: Embeddable Composite Primary Key Class
@Embeddable
public class EmployeePK implements Serializable {
    private String name;
    private long id;

    public EmployeePK() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
}
Example 2: JPA Entity With an Embedded Composite Primary Key Class
@Entity
public class Employee implements Serializable {
    EmployeePK primaryKey;
 
    public Employee() {
    }
 
    @EmbeddedId
    public EmployeePK getPrimaryKey() {
        return primaryKey;
    }
 
    public void setPrimaryKey(EmployeePK pk) {
        primaryKey = pk;
    }
 
    ...
}
An alternate way to represent a composite primary key is to use @IdClass annotation.
For example, the primary key of the following Project entity class consists of two fields:
@Entity @IdClass(ProjectId.class)
public class Project {
    @Id int departmentId;
    @Id long projectId;
     :
}
When an entity has multiple primary key fields, JPA requires defining a special ID class that is attached to the entity class using the @IdClass annotation. The ID class reflects the primary key fields and its objects can represent primary key values:
Class ProjectId {
    int departmentId;
    long projectId;
}
The primary key, or the property or field of a composite primary key, must be one of the following Java language types:
  • Java primitive types
  • Java primitive wrapper types
  • java.lang.String
  • java.util.Date (the temporal type should be DATE)
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger
Floating-point types should never be used in primary keys. If you use a generated primary key, only integral types will be portable.
A primary key class must meet these requirements.
  • The access control modifier of the class must be public.
  • The properties of the primary key class must be public or protected if property-based access is used.
  • The class must have a public default constructor.
  • The class must implement the hashCode() and equals(Object other) methods.
  • The class must be serializable.
  • A composite primary key must be represented and mapped to multiple fields or properties of the entity class or must be represented and mapped as an embeddable class.
  • If the class is mapped to multiple fields or properties of the entity class, the names and types of the primary key fields or properties in the primary key class must match those of the entity class.

8. Relationship Mapping

JPA allows us to define relationships between classes, e.g. it can be defined that a class is part of another class (containment). Classes can have one-to-one, one-to-many, many-to-one, and many-to-many relationships with other classes.
JPA provides below annotations to  perform different relationships:
  • @OneToOne
  • @OneToMany
  • @ManyToOne
  • @ManyToMany
Check out below complete example:

9. Cascade Operations and Relationships

Entities that use relationships often have dependencies on the existence of the other entity in the relationship. For example, a line item is part of an order; if the order is deleted, the line item also should be deleted. This is called a cascade delete relationship.
The jakarta.persistence.CascadeType enumerated type defines the cascade operations that are applied in the cascade element of the relationship annotations.
Below is a list of cascade operations for Entities -
ALL - All cascade operations will be applied to the parent entity's related entity. All is equivalent to specifying cascade={DETACH, MERGE, PERSIST, REFRESH, REMOVE}
  1. DETACH - If the parent entity is detached from the persistence context, the related entity will also be detached.
  2. MERGE - If the parent entity is merged into the persistence context, the related entity will also be merged.
  3. PERSIST - If the parent entity is persisted into the persistence context, the related entity will also be persisted.
  4. REFRESH - If the parent entity is refreshed in the current persistence context, the related entity will also be refreshed.
  5. REMOVE - If the parent entity is removed from the current persistence context, the related entity will also be removed.
Cascade delete relationships are specified using the cascade=REMOVE element specification for @OneToOne and @OneToMany relationships. For example:
@OneToMany(cascade=REMOVE, mappedBy="customer")
public Set<CustomerOrder> getOrders() { return orders; }
Read more at Guide to JPA and Hibernate Cascade Types

10. Orphan Removal in Relationships

When a target entity in a one-to-one or one-to-many relationship is removed from the relationship, it is often desirable to cascade the remove operation to the target entity. Such target entities are considered "orphans," and the orphanRemoval attribute can be used to specify that orphaned entities should be removed. For example, if an order has many line items and one of them is removed from the order, the removed line item is considered an orphan. If orphanRemoval is set to true, the line item entity will be deleted when the line item is removed from the order.
The orphanRemoval attribute in @OneToMany and @oneToOne takes a Boolean value and is by default false.
The following example will cascade the remove operation to the orphaned order entity when the customer entity is deleted:
@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<CustomerOrder> getOrders() { ... }

11. Embeddable Classes in Entities

JPA provides the facility to embed one entity inside another entity, so they are mapped to a single table.
We can use the @Embeddable annotation to mark a class to be eligible as an embeddable class and we use the @Embedded annotation to embed an embeddable class. The attributes of an embedded class can be overridden using the @AttributeOverrides and the @AttributeOverride annotations.
Example: Creating an Embeddable Class using @Embeddable
Let's create the Address class as Embeddable Class. The advantage of using embedded objects is that they are reusable and you can map two entities to the same database table.
package net.javaguides.hibernate.entity;

import jakarta.persistence.Embeddable;

@Embeddable
public class Address {
    private String addressLine1;

    private String addressLine2;

    private String city;

    private String state;

    private String country;

    private String zipCode;

    public String getAddressLine1() {
        return addressLine1;
    }

    public void setAddressLine1(String addressLine1) {
        this.addressLine1 = addressLine1;
    }

    public String getAddressLine2() {
        return addressLine2;
    }

    public void setAddressLine2(String addressLine2) {
        this.addressLine2 = addressLine2;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }
}
Example: Embedding an Embeddable object with @Embedded
We can simply embed an embeddable class using the @Embedded annotation. This class will be embedded in the same database table as the source.
import jakarta.persistence.*;

/**
 * Created by ramesh
 */
@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;

    public User() {

    }

    public User(Name name, String email, Address address) {
        this.name = name;
        this.email = email;
        this.address = address;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Name getName() {
        return name;
    }

    public void setName(Name name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}
Embeddable classes may also contain relationships to other entities or collections of entities. If the embeddable class has such a relationship, the relationship is from the target entity or collection of entities to the entity that owns the embeddable class.

What's Next?

In the next article, we will discuss the architecture of the JPA specification.
Check out complete example at Hibernate Component Mapping Using @Embeddable and @Embedded Annotation.

Comments

  1. firstName field is written two times . I guess third field is lastName which is transient


    @Column(name = "first_name")
    private String firstName;

    @Transient // Field will not be saved in a database
    private String firstName;
    // getter and setter methods

    ReplyDelete

Post a Comment

Leave Comment