JPA Fundamentals & Hibernate - 1) Entities and Context
In this series, we are going to learn the JPA fundamentals and we are going to use one of its implementation Hibernate. Before diving into the series, …
In this post, we are going to find out “what are the options to provide automatic Id generation instead of setting manually”. We will look at the generation strategies.
To use generation strategies, we should use @GeneratedValue
annotation which has two properties:
If you only need to see the code, here is the github link
We have four strategies: (represent as enum values)
GenerationType.AUTO
: If we use this type, we allow the persistence provider(in our example it is the Hibernate) to choose the appropriate strategy.If you use Hibernate, Hibernate will choose the appropriate strategy depending on the database. If you use MySQL, hibernate will choose different strategy rather than PostgreSQL
GenerationType.IDENTITY
: If we use this type, we already set the auto-increment feature when we were creating our sql table.GenerationType.TABLE
: If we use this type, there is another table which is used as a generator.GenerationType.SEQUENCE
: Similar to TABLE
, difference is that implementation will use the sequence in the sql.I will only talk about the GenerationType.TABLE
and Generation.IDENTITY
. The rest is up to you.
In this case, we need separate table, id generation. By default in the JPA, Id generation table should have two columns namely sequence_name with type VARCHAR(100) & next_val with type INT.
In the @GeneratedValue
, we will give the id generation table name.
Let’s do it an example:
DROP TABLE product;
CREATE TABLE product
(
id INT,
name VARCHAR(100),
price NUMERIC,
expiration_date DATE
);
CREATE TABLE id_generation
(
sequence_name VARCHAR(100),
next_val INT
);
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "id_generation")
private int id;
// ...
}
public class Main {
public static void main(String[] args) {
// ...
Product product = new Product();
product.setName("Cheese");
product.setPrice(5.4);
product.setExpirationDate(LocalDate.now());
try {
entityManager.getTransaction().begin();
entityManager.persist(product);
entityManager.getTransaction().commit();
}catch (Exception e) {
System.out.println("Exception: " + e.getMessage());
}
}
}
First let’s look at the tables:
select * from product ;
id | name | price | expiration_date
----+--------+-------+-----------------
1 | Cheese | 5.4 | 2021-08-01
(1 row)
select * from id_generation ;
sequence_name | next_val
---------------+----------
product | 100
(1 row)
In the application console, there will be many queries run by the hibernate:
Hibernate:
select
tbl.next_val
from
id_generation tbl
where
tbl.sequence_name=? for update
of tbl
Hibernate:
insert
into
id_generation
(sequence_name, next_val)
values
(?,?)
Hibernate:
update
id_generation
set
next_val=?
where
next_val=?
and sequence_name=?
Hibernate:
select
tbl.next_val
from
id_generation tbl
where
tbl.sequence_name=? for update
of tbl
Hibernate:
update
id_generation
set
next_val=?
where
next_val=?
and sequence_name=?
Hibernate:
insert
into
product
(expiration_date, name, price, id)
values
(?, ?, ?, ?)
Process finished with exit code 0
If you want to use different column names rather than sequence_name
& next_val
, you can use the @TableGenerator
annotation.
In the PostgreSQL,
SERIAL
pseudo-type is used to define auto-increment columns in tables.Actually in the PostgreSQL, a sequence is a special kind of database object that generates a sequence of integers. When creating a new table, the sequence can be created through the
SERIAL
pseudo-type.CREATE TABLE table_name( id SERIAL ); -- serial and also primary key CREATE TABLE table_name( id SERIAL PRIMARY KEY, );
Here is the table creation query:
CREATE TABLE product
(
id SERIAL PRIMARY KEY,
name VARCHAR(100),
price NUMERIC,
expiration_date DATE
);
testdatabase=# drop table product ;
testdatabase=# CREATE TABLE product
testdatabase-# (
testdatabase(# id SERIAL PRIMARY KEY,
testdatabase(# name VARCHAR(100),
testdatabase(# price NUMERIC,
testdatabase(# expiration_date DATE
testdatabase(# );
In real word, type of id field generally is the long
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
// ...
}
In this case, I don’t need to call setter of id field, this will be done by automatically
public static void main(String[] args) {
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
EntityManager entityManager = entityManagerFactory.createEntityManager();
Product product = new Product();
product.setName("Cheese");
product.setPrice(5.4);
product.setExpirationDate(LocalDate.now());
try {
entityManager.getTransaction().begin();
entityManager.persist(product);
entityManager.getTransaction().commit();
}catch (Exception e) {
System.out.println("Exception: " + e.getMessage());
}
}
testdatabase=# select * from product ;
id | name | price | expiration_date
----+--------+-------+-----------------
1 | Cheese | 5.4 | 2021-08-01
2 | Cheese | 5.4 | 2021-08-01
(2 rows)
If integer generator is not enough, you should create random string for each id field. You can do that with this.id = UUID.randomUUID().toString();
but this approach is not recommended. Instead you can use Hibernate specific implementation.
Because we will use hibernate specific implementation, if you change JPA implementation (such as from Hibernate to EclipseLink) in the future, this method will not work.
Let’s do that:
In this case, id field will be varchar
DROP TABLE product ;
CREATE TABLE product
(
id VARCHAR(100),
name VARCHAR(100),
price NUMERIC,
expiration_date DATE
);
import org.hibernate.annotations.GenericGenerator; // Hibernate specific implementation
import javax.persistence.*;
import java.time.LocalDate;
@Entity
@Table(name = "product")
public class Product {
@Id
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDHexGenerator")
@GeneratedValue(generator = "uuid")
private String id;
// ....
}
If you get the following warning while initializing your application:
... WARN 26489 --- [ main] org.hibernate.id.UUIDHexGenerator : HHH000409: Using org.hibernate.id.UUIDHexGenerator which does not generate IETF RFC 4122 compliant UUID values; consider using org.hibernate.id.UUIDGenerator instead
Please change the strategy to:
public class Product {
@Id
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
@GeneratedValue(generator = "uuid")
private String id;
// ....
}
select * from product ;
id | name | price | expiration_date
----------------------------------+--------+-------+-----------------
ff8080817b01e5a6017b01e5a8f30000 | Cheese | 5.4 | 2021-08-01
(1 row)
Last but not least, wait for the next post …
In this series, we are going to learn the JPA fundamentals and we are going to use one of its implementation Hibernate. Before diving into the series, …
In this post, we are going to setup spring boot rest project with using JWT. we will also integrate the our spring boot application with the frontend …