Does Lombok Work with Hibernate JPA Entities?
Problem
When I added Lombok to my Spring Boot project, I wanted to use it on JPA entities to reduce boilerplate.
I tried this:
@Entity@Data // Includes @EqualsAndHashCode!public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
private String email; private String name;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private Set<Order> orders;}But I ran into issues:
- Lazy loading triggered during equality checks
- Duplicate entities in Sets
- Missing entities after ID assignment
Environment
- Spring Boot 3.x
- Hibernate 6.x (JPA)
- Lombok 1.18.x
What happened?
I added some entities to a Set:
Set<User> users = new HashSet<>();users.add(user1);users.add(user2);
// Later, after persisting user1...users.contains(user1); // Returns false sometimes!The problem is Lombok’s @Data includes @EqualsAndHashCode, which:
- Uses all fields including lazy-loaded collections
- Includes the database ID
- Changes hashCode when ID is assigned
Here’s what happens:
Transient entity: hashCode = based on null ID + email + name + ordersAfter persisting: hashCode = based on 123 + email + name + orders → Entity in wrong bucket in HashSet!How to solve it?
I found the right approach: use selective Lombok annotations and implement equals/hashCode manually.
Safe Lombok Annotations for JPA Entities
Use these:
@Getter- Generate getters@Setter- Generate setters (Hibernate needs them)@NoArgsConstructor- Required for Hibernate proxy creation@ToStringwith exclusions for lazy-loaded fields
Avoid these:
@Data- Includes problematic@EqualsAndHashCode@EqualsAndHashCode- Breaks entity identity
Solution Code
import lombok.*;import jakarta.persistence.*;import java.util.HashSet;import java.util.Objects;import java.util.Set;
@Entity@Table(name = "users")@Getter@Setter@NoArgsConstructor@AllArgsConstructor@ToString(exclude = "orders") // Exclude lazy-loaded collectionspublic class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@Column(unique = true, nullable = false) private String email; // Business key for equality
private String name;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private Set<Order> orders = new HashSet<>();
// Manual equals/hashCode based on business key (email) @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof User)) return false; User user = (User) o; return Objects.equals(email, user.email); }
@Override public int hashCode() { return Objects.hash(email); }}Why Business Key?
Using email as the business key works before and after persisting:
Transient: hashCode(email) = consistentPersisted: hashCode(email) = same value → Entity stays in correct HashSet bucketUsing @NaturalId (Hibernate-specific)
Hibernate provides @NaturalId for business keys:
import lombok.*;import jakarta.persistence.*;import org.hibernate.annotations.NaturalId;
@Entity@Getter@Setter@NoArgsConstructor@ToString(exclude = "orders")public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@NaturalId // Hibernate-specific business key marker @Column(unique = true, nullable = false) private String email;
private String name;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private Set<Order> orders = new HashSet<>();
@Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof User)) return false; User user = (User) o; return Objects.equals(email, user.email); }
@Override public int hashCode() { return Objects.hash(email); }}The reason
I think the key issues are:
-
Mutability Requirement: JPA entities must be mutable - Hibernate updates state during persistence operations.
-
Proxy Support: Hibernate creates runtime proxies for lazy loading. Classes must be non-final with accessible constructors.
-
Identity Management: Entities in Sets rely on stable hashCode. Lombok’s hashCode changes when ID is assigned.
-
Lazy Loading Triggers:
@EqualsAndHashCodeaccesses all fields, triggering database queries for lazy-loaded collections.
Summary
In this post, I showed how to use Lombok with Hibernate JPA entities. The key point is: use @Getter, @Setter, @NoArgsConstructor for boilerplate, and manually implement equals/hashCode based on a business key like email. Avoid @Data and @EqualsAndHashCode to prevent lazy-loading issues and maintain entity identity correctness.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Project Lombok Documentation
- 👨💻 Hibernate ORM Documentation
- 👨💻 How to implement equals and hashCode (Vlad Mihalcea)
- 👨💻 Reddit Discussion: Lombok and Hibernate
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments