As a java developer, you are probably often confronted with the creation of spring boot projects, and unfortunately, there are always repetitive tasks that arise.
Tasks like :
- Database table and columns configuration
- Creating entities
- Relational database mapping classes
Creating again and again the same fields and columns common to all classes, can be pretty annoying and redundant, especially if there are similar fields shared by these classes.
Well, in this tutorial, I will present you a simple and efficient approach to defining a basic template for all your JPA/Hibernate entity classes.
Dependencies
Here are some dependencies you will need to create an efficient base entity template for Hibernate/JPA project.
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.9.Final</version>
</dependency>
</dependencies>
Base entity for ID
For our Base Entity class, we need to set up the one and only property that all the entities have in common β the ID column
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
import java.util.Objects;
import java.util.UUID;
@Getter
@Setter
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
@Id
@GeneratedValue
@Column(columnDefinition = "BINARY(16)", updatable = false, nullable = false)
private UUID id;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BaseEntity)) return false;
BaseEntity that = (BaseEntity) o;
return id.equals(that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "BaseEntity {" +
"id = " + id +
"}";
}
}
Want to know why ?
Take a look at this article below for further explanations. β
Now letβs take a brief look about these annotations in our base entity class:
MappedSuperClass
- is used to ensure that the Base Entity class will not have a separate representation as table of the extending class.- We use
public abstract class BaseEntity
to prevent developers from instantiating an instance of this class. Only extending classes can instantiate an instance. id
- The id is annotated with@Id
, which marks it as the primary key to identify this entity.@GeneratedValue
- Combined with the type UUID of the id property will make Hibernate use the uuid2 generator strategy, and theBINARY(16)
will make the value of UUID more human-readable.Getter
andSetter
are Lombok annotations we use to avoid writing long boring get and set methods in our class.
Base entity for audit
Next, we will need to create another class BaseEntityAudit
to track and monitor when and who updates or creates the entity, with fields createdBy
, updatedBy
, createdAt
, updatedAt
.
import jakarta.persistence.Column;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
@Getter
@Setter
@MappedSuperclass
public abstract class BaseEntityAudit extends BaseEntity implements Serializable {
private String createdBy;
private String updatedBy;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private Date createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private Date updatedAt;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BaseEntityAudit)) return false;
if (!super.equals(o)) return false;
BaseEntityAudit that = (BaseEntityAudit) o;
return createdBy.equals(that.createdBy) &&
updatedBy.equals(that.updatedBy) &&
createdAt.equals(that.createdAt) &&
updatedAt.equals(that.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(),
createdBy, updatedBy, createdAt, updatedAt);
}
@Override
public String toString() {
return "BaseEntityAudit{" +
"createdBy='" + createdBy + '\\'' +
", updatedBy='" + updatedBy + '\\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
"}" +
super.toString();
}
}
Why are we using equals and hashcode?
The object class defines both equals() and hashCode() methods, which are implicitly defined in every Java class, including the ones we create.
- The default implementation of
equals()
in the Object class says that equality is the same as object identity, by overriding the equals() method we now consider not only the object identity, but also the value of relevant properties. hashCode()
returns an integer representing the current instance of the class. We should calculate this value consistent with the definition of equality for the class. Thus, if we override the equals() method, we also have to override hashCode().
Project Lombok also provides an @EqualsAndHashCode
annotation. Note again how equals() and hashCode() βgo together
β and even have a common annotation.
Entity Class
Finally, when you want to create an Entity Class for your business logic, you just have to extend the BaseEntityAudit class, which itself extends BaseEntity.
import com.example.entity.common.BaseEntityAudit;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Getter
@Setter
@Table(name = "users")
public class User extends BaseEntityAudit {
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
@Override
public String toString() {
return "User{" +
"name='" + name + '\\'' +
", email='" + email + '\\'' +
'}' +
super.toString();
}
}
If you would like to test this code and assure that you have all the column generated by Hibernate, you can set up a MySQL database container and start your application with this command.
./mvnw spring-boot:run
I hope you enjoyed reading this, and I'm curious to hear if this tutorial helped you. Please let me know your thoughts below in the comments. Don't forget to subscribe to my newsletter to avoid missing my upcoming blog posts.
You can also find me here LinkedIn β’ Twitter β’ GitHub or Medium
Further Read
Take the code sample of this tutorial on my GitHub here.
If you want to understand some notions about hibernate in depth, I highly recommend these articles below:
https://vladmihalcea.com/hibernate-and-uuid-identifiers/
https://blog.codinghorror.com/primary-keys-ids-versus-guids/