One of the core components of JPA is a JPA entity. This entity allows applications to transfer data to and from the database. You can think of an entity as a class representation of a database table. When we want to retrieve data from a database, we extract it from the target table and place it inside an entity. When we want to add data to a database table, we can create an entity and use it to add data to our target table.

In this topic, we will learn about the fundamentals of entities and see how they can be used to communicate with databases.

Creating a JPA Entity

In this section, we will take a look at how JPA entities are declared and structured. An entity is a POJO class, which represents the structure of a single database table. Our entity will have properties that represent each of the table columns. Each instance of an entity object will represent a single row in the table. In addition to object properties, a JPA entity must also contain a no-argument constructor. This is because JPA will instantiate your object dynamically, starting with an empty object and populating values as it reads from the database.

To help you understand entities better, let’s study an example using a database that contains internet orders, which we will call internet_orders. In our internet_orders database, we will have a table called orders, which contains orders placed by customers.

We can create a JPA entity that represents our orders table using the @Entity annotation, as you can see below.

@Entity
public class Order {
}

By default, JPA will match up the object name to the table name in the database. In our case, the Order class is mapped to the order table, but we need this class to match another table name, orders. Therefore, we need to explicitly tell JPA how to map to a table using the @Table annotation as shown below.

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

Now that we have a basic entity, we can start building up our object to match the table structure.

Creating Persistent Fields

In this section, we will learn how properties can be added to our entity. Before we start writing code, let’s discuss a few basic rules for entity properties. First, each property must be private, protected, or package-private. In addition to this, none of the fields can be static or final, in order to allow for data persistence. Similar to our class name, each property name will automatically map to the column in the table with the same name.

For example, suppose that our orders table has a field named product_type.

This field will contain text that describes the type of product added to the order. We can map this field to our entity by creating a property that matches its name, as shown below.

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

    private String productType;

}

Note that we use the type String to represent the SQL varchar type. Depending on the database used, types will be converted to equivalent database types when data is used with our entities.

In this example, our property name matches the column name, so they will automatically map through JPA. If we wanted to use a property name different from our column name, we could explicitly define the column mapping using the @Column annotation.

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

    @Column(name = "product_type")
    private String productType;

}

This is how we can map a property in our entity to a database column in our table. There are many different types of data we may want to represent, so we will move on to looking at temporal and enumerated data in the next section.

Using temporal and enumerated data

In many cases, we may want to store temporal data, such as the current date and time. In these instances, we can use a special annotation called @Temporal, which tells JPA that the data is temporal in nature.

For example, suppose we wanted to add the date of order to our orders table. This field will be named order_date, and it will be formatted as a date.

We can represent this in our entity by adding a temporal field. Since the Date object stores both the date and time in it, we need to tell JPA which portion of the date we need. We do this by specifying a temporal type inside of our temporal annotation. There are three main temporal types that exist in JPA:

  1. TemporalType.TIMESTAMP: used to specify that the temporal data is a timestamp value.
  2. TemporalType.DATE: used to specify that only the date portion is required.
  3. TemporalType.TIME: used to specify that only the time portion is required.

In our case, since we only need the date portion for our order_date field, we specify our field as a TemporalType.DATE:

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

    @Column(name = "product_type")
    private String productType;

    @Temporal(TemporalType.DATE)
    @Column(name = "order_date")
    private Date orderDate;

}

With this, we now have a field to store our order date.

The @Temporal annotation is primarily used with older versions of Java. Modern JPA versions support conversion to JDK8 temporal classes such as LocalDate without the need for annotations.

There are other field types that use annotations to provide the context of the expected format to JPA. For example, suppose we wanted to represent the status of an order in our table as shipped, pending, or received. Since there are three possible options, it would make sense to represent this value as an enum.

public enum Status{
    SHIPPED,
    PENDING,
    RECEIVED
}

We can then use this enum in our entity, specifying the data type of the enum in an @Enumerated annotation.

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

    @Column(name = "product_type")
    private String productType;

    @Temporal(TemporalType.DATE)
    @Column(name = "order_date")
    private Date orderDate;

    @Enumerated(EnumType.STRING)
    @Column(name = "order_status")
    private Status orderStatus;

}

Here we have specified that our enum is of String type. If we want to specify that an enum is a numeric type, we can use EnumType.ORDINAL instead.

With this, you should now have an idea of how various data types are represented in entities. Next, we will look at specifying fields that are non-persistent.

Defining non-persistent fields

In some situations, we may not want to save all the fields of an object into our database. For example, suppose a user is making a new order in an application. During the order creation process, we may want to track at what stage our order is for our application logic. We can store this data inside the Order object we created and specify that it should not be sent to the database, using the @Transient annotation.

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

    @Column(name = "product_type")
    private String productType;

    @Temporal(TemporalType.DATE)
    @Column(name = "order_date")
    private Date orderDate;

    @Enumerated(EnumType.STRING)
    @Column(name = "order_status")
    private Status orderStatus;

    @Transient
    private String orderProgress;

}

With our current Order class, the orderProgress field will be the only field that is not stored in the database.

Setting primary keys and generated values

In relational databases, it is essential for each table to have a unique identifier, known as a primary key. We can use the @Id annotation to specify that a field is an ID in an entity. For example, suppose that we add an integer field order_id, which uniquely identifies each order record. The updated class would look like this:

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

    @Id
    @Column(name = "order_id")
    private long orderId;

    @Column(name = "product_type")
    private String productType;

    @Temporal(TemporalType.DATE)
    @Column(name = "order_date")
    private Date orderDate;

    @Enumerated(EnumType.STRING)
    @Column(name = "order_status")
    private Status orderStatus;

    @Transient
    private String orderProgress;

}

Note that when we set an ID field, we use the long variable type. This is because long provides us with the largest ID values possible without the risk of possible overflows in the data.

We will often use automatically generated ID values to uniquely identify records in our database. In JPA, we have an annotation called @GeneratedValue that allows us to generate a value for a field based on a generation strategy. There are four strategy types:

  1. AUTO option that allows JPA to pick any strategy for generating the ID that it deems appropriate;
  2. IDENTITY option that allows a generation of values to use a database identity, such as AUTO_INCREMENT;
  3. SEQUENCE option that allows a generation of values to use a database sequence;
  4. TABLE option that allows a generation of values to use an underlying database table to ensure uniqueness.

For example, if we want our order to have an identity-based generated value, we can use the code below.

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "order_id")
    private long orderId;

    @Column(name = "product_type")
    private String productType;

    @Temporal(TemporalType.DATE)
    @Column(name = "order_date")
    private Date orderDate;

    @Enumerated(EnumType.STRING)
    @Column(name = "order_status")
    private Status orderStatus;

    @Transient
    private String orderProgress;

}

For AUTO– and IDENTITY-based generations, it is sufficient just to provide a strategy for the @GeneratedValue annotation. If you are using a sequence-based generation, you will need to provide the sequence name, initial value, and allocation size in addition to the strategy. If you are using a table-based generation, you will need to provide the table, primary key column name, and value column name for the generation.

As a final note, it is possible to create getters and setters for each entity field in case we want to access or change data in the entity object. These getters and setters resemble the typical getters and setters used in Java objects.

Conclusion

In this topic, you’ve learned about JPA entities, which are a fundamental component of the JPA persistence layer. Each JPA entity is a simple object annotated with the @Entity annotation. Inside of this class, we can define properties that represent the columns in our database tables, using the @Column annotation when the property name does not match the field name. You’ve seen how we can mark fields as @Transient, allowing them to exist in the class without being stored in the database table. Also, you’ve learned about @Temporal and @Enumerated fields, which allow us to provide the context of our field formats to JPA. Finally, you’ve studied how to set up primary keys in the entities, and how to create getters and setters for entity fields.

With this information, you have the necessary knowledge to represent data through JPA entities. Now that you know how to extract data from databases, you can start to use database data in your applications.

Leave a Reply

Your email address will not be published.