Jakarta Persistence annotations

Jakarta Persistence (JPA) is a Java object-relational mapping (ORM) standard that allows us to work with relational databases through objects. Instead of writing SQL queries directly, JPA allows us to work with Java objects that are automatically mapped to database table records.

JPA uses plain Java classes called POJOs (Plain Old Java Objects) to represent data. These classes contain only fields and accessor methods (get/set) without depending on any specific infrastructure or base classes. When we add JPA annotations to a POJO class, it becomes an entity that can be stored in the database. An entity represents a table, and each row in the table corresponds to an object of that class.

@Entity

The annotation is added at the class level, setting the latter as entity. The Entity class must have a default constructor and a field set as the primary key.

@Entity
public class Student {
    @Id
    private Long id;
    private String name;
    // getters and setters
}

@Table

Sets the primary table of the annotated entity.

In most cases, we don’t want the name of the database table to match the name of the entity class. In such a situation, we can explicitly specify it using the @Table annotation.

@Entity
@Table(name="Students")
public class Student {
    
    // fields, getters and setters
}

The optional annotation elements indexes, uniqueConstraints, schema, and catalog can be used to specify the indexes, unique constraints, schema, and catalog of the table.

@Id

Every JPA entity must have a primary key that uniquely identifies it. The @Id annotation defines the primary key. We can generate the identifiers in different ways, which are specified by the @GeneratedValue annotation. We can choose from four strategies for generating an identifier with the strategy element. The value can be AUTO, TABLE, SEQUENCE, UUID, or IDENTITY.

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    // getters and setters
}

@Column

Specifies the details of a column in the table. Elements such as name, length, nullable, and unique can be specified.

@Entity
@Table(name="Students")
public class Student {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    
    @Column(name="student_name", length=50, nullable=false, unique=false)
    private String name;
    
    // other fields, getters and setters
}

@Transient

Sometimes we may want to make a field transient. We can achieve this with the @Transient annotation. It indicates that the field will not be persisted. For example, we can calculate the age of a student from the date of birth. So let’s annotate the age field with the @Transient annotation:

@Entity
@Table(name="Students")
public class Student {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    
    @Column(name="student_name", length=50, nullable=false)
    private String name;
    
    @Transient
    private Integer age;
    
    // other fields, getters and setters
}

@CreationTimestamp

Hibernate annotation. Marks the field as a timestamp for the object’s creation.

@Entity
public class Book {
    //other fields

    @CreationTimestamp
    private Instant createdOn;

    // standard setters and getters
}

@UpdateTimestamp

Hibernate annotation. Marks the field as a timestamp for the object’s update.

@Entity
public class Book {
    //other fields

    @UpdateTimestamp
    private Instant lastUpdatedOn;

    // standard setters and getters
}

@OneToMany

Specifies a one-to-many (1:M) association.

The @OneToMany annotation can be used within an embedding class contained within an entity class to indicate a relationship to a collection of entities. If the relationship is bidirectional, the mappedBy element must be used to specify the relationship field or property of the entity that owns the relationship.

Example:

Class Customer

@Entity
@Table(name="customers")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    ****

    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, 
    orphanRemoval = true)
    private Set<Order> orders = new HashSet<>();
}

Class Order

@Entity
@Table(name="orders")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

	***

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="task_id", nullable = false)
    private Customer customer;
}

@ManyToMany

Specifies a many-to-many association (M:M).

Example 1

@Entity
@Table(name="Customers")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
     ***
     
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name="Cust_phones")
    private Set<PhoneNumber> phones;
}


@Entity
@Table(name="phone")

public class PhoneNumber {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToMany(mappedBy="phones", fetch = FetchType.EAGER)
    private Set<Customer> customers;
}

Example 2

@Entity
@Table(name="customers")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    ***
    
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "CUST_PHONES",
	joinColumns = @JoinColumn(name = "customer_id", referencedColumnName = "id"),
	inverseJoinColumns = @JoinColumn(name = "phone_id", referencedColumnName = "id"))
private Set<PhoneNumber> phones;
}

@Entity
@Table(name="phone_numbers")
public class PhoneNumber {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    ****

    @ManyToMany(mappedBy="phones")
    private Set<Customer> customers; 
}