In this post, we will explore how to map a database tables to JPA Entities. In our example, we will use a Spring Boot environment, JPA 2.2 and Hibernate 5.3.7 . However, the information presented here should apply to any environment with a JPA version of 2.1 or later.
Introduction
In our previous post, we discovered how to get started with a Spring boot application with a persistent JPA context. Using Hibernate’s auto-ddl property, we were able to create the database schema along with the corresponding tables automatically.
However, in a production environment you will probably not need to create a new database on application start up. It can also be, that there is already an existing database which we would like to map to our application.
Therefore, in this post, we will explore how to map database tables to JPA entities. We will mainly focus on column mapping, constraints and id generation. Mapping of entity relationships (OneToOne, OneToMany, ManyToOne, ManyToMany) will be explored in much greater detail in upcoming posts. Please check our Spring JPA series for the corresponding content. In this tutorial, we will use a new table called the “bank_branch” table.
Mapping annotations
Now, let us start mapping a Postgres database table to a JPA persistence entity. The following database Postgres table will be used as an example for this lesson. It can be created using the following script:
CREATE TABLE public.bank_branch ( id bigint NOT NULL, branch_code character varying(255) NOT NULL, zip_code character varying(50) NOT NULL, name character varying(255) NOT NULL, CONSTRAINT bank_branch_pkey PRIMARY KEY (id), CONSTRAINT bank_branch_branch_code_key UNIQUE (branch_code) )
@Table
In order to tell Hibernate the name of the table that the entity maps to, we will need to use the @Table annotation. The annotation is used to set the name of the table, the catalog of the table, and the schema where the table exists. Since we created our table in the default catalog and schema of the nullbeansdemo database, we only need to tell Hibernate about the table name. Let us create a class called “BankBranch”.
@Entity @Table(name = "bank_branch" ) public class BankBranch { ........
Here, we set the name of the table just as we created it on the database side. We should also not forget the @Entity annotation to mark the class as a persistence entity for Hibernate.
@Column
The column annotation indicates that the property/field maps to a table column. Various column parameters can be set for a column. Below are the most frequently used:
- name (String): The name of the column on the database side.
- unique (boolean): This indicates if a unique constraint exists for the given column. Default value is false.
- nullable (boolean): Indicates if the column value can be null. Default value is true.
- insertable (boolean): Indicates if the column should be included in SQL insert statements issued by the JPA implementation provider (such as Hibernate). If set to false, then insert statements will not include the column. Default value is true.
- updateable (boolean): Indicates if the column should be included in SQL update statements issued by the JPA implementation provide. If set to false, then update statements will not include the column. Usually we would set this to false if we do not want a column to be modified after initial insertion. Default value is true.
- length (int): If the column represents a String/Variable character, then the length represents the maximum length of that string. Default value is 255. If the default value is different than that on the database side, then the length parameter should be set.
So, let us now map the “zip_code” and the “name” columns.
@Column(name = "name", nullable = false) public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(name = "zip_code", nullable = false, length = 50) public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; }
The next step is to map the “branch_code” column. Notice that we added a unique constraint in the table creation script. Therefore we need to set the “unique” column property to “true”.
@Column(name = "branch_code", unique = true, nullable = false) public String getBranchCode() { return branchCode; } public void setBranchCode(String branchCode) { this.branchCode = branchCode; }
@Id
The @Id annotation indicates that the given column holds the primary key value of the entity. The ID field can be any Java primitive data type, String, java.util.Date, java.sql.Date, BigDecimal or a BigInteger. The @Id annotation is usually combined with the @Column annotation in order to indicate the column name on the database side, nullability, length, etc.. If the @Column annotation is missing, then the column name is assumed to be the same as the name of the field/parameter in the Java class.
@GeneratedValue
The @GeneratedValue annotation indicates that the column value is not manually set, but is generated via a specific strategy, such as a SequenceGenerator, a TableGenerator, etc.. The generation strategy can be set in the GeneratedValue annotation. If left empty, the default value is “AUTO”, which means that the JPA provider will use the strategy that the persistence provider (for example, our Postgres DBMS) offers. For simplicity reasons, we will leave the default value in our example. Now, let us map the Id column of our entity:
@Id @GeneratedValue @Column(name = "id", nullable = false) public long getId() { return id; } public void setId(long id) { this.id = id; }
Do we need to set the column “unique” property to “true” when using the @GeneratedValue annotation?
No. If you are using the @GeneratedValue annotation, then the JPA provider will ignore whatever value you set using the “setId” method and will regenerated the value anyway using the generation strategy chosen. In general, it is not a good idea to manually set the database ID value of a persistence entity.
@NaturalId
While having an auto-generated primary key makes good sense on a database level, it is probable that it will not be used by users in the real world. For example, in our entity we have both the “id” field, which is used by the database as a primary key, we also have the “branch_code” field, which is also Unique and is more “human readable”. Another example would be in a digital library of books. A user who is looking for a book will probably not be aware of the book’s database ID. They will probably search for a book by something else such as the name or the ISBN. Such identifications are called Natural IDs.
If your entity contains such an ID, then it is important to mark them with the @NaturalId if you expect a lot of searches using that field. The reason is that Hibernate will cache your natural IDs if you use caching. This will provide a large performance boost for your application during searches. Now, let us revisit the “branch_code” field. In order to mark the field as a natural ID, we simply need to add the annotation.
@NaturalId @Column(name = "branch_code", unique = true, nullable = false) public String getBranchCode() { return branchCode; }
Please note that a NaturalId field is still not a replacement to a database id field. An auto generated database ID field will still be more useful and faster for inserting new database rows, such as in bulk insert operations, deletions and ID space management.
Summary
In this tutorial, we explored how to map existing database table columns to entity fields. We discussed how to specify the table name using the @Table annotation. Then we discussed how to map columns using the @Column annotation and the different properties that could be configured. Then we had a brief overview of the @Id and @GeneratedValue annotations and what they are used for. Finally, we explored the @NaturalId annotation and how to apply it to natural IDs in our entities.
If you liked this tutorial or if you have any questions, then please let us know in the comments below 🙂
Leave a Reply
You must be logged in to post a comment.