DEV Community

Priyanshu Belwal
Priyanshu Belwal

Posted on

Avoiding Pitfalls: Unraveling Lombok and JPA Integration 🌟

Introduction:

The combination of Lombok and JPA offers tremendous convenience and productivity in Java development. With Lombok's annotations, we can reduce boilerplate code, while JPA simplifies database interactions. However, like any powerful tool, there are potential pitfalls to be aware of.

In this article, we will explore three common issues that may arise when using Lombok and JPA together, along with practical examples and solutions to keep your codebase running smoothly. Let's dive in! πŸ’»

1. Accidentally Loading Lazy Attributes 😱

Lombok's @data annotation generates various methods, including getters and setters, for our entities. While this can save us time, it's crucial to understand its impact on lazy loading. Consider the following scenario:

@Entity
@Data
public class Book {
    @Id
    private Long id;

    private String title;

    @OneToMany(mappedBy = "book", fetch = FetchType.LAZY)
    private List<Comment> comments;
}
Enter fullscreen mode Exit fullscreen mode

In this case, Lombok generates a getter for the comments field. If we mistakenly call this getter outside a transactional context, we might trigger a lazy loading exception. To avoid this, always ensure that you access lazy attributes within a transaction or eagerly fetch the necessary data when querying the entity.

Lombok also provides equals()/hashCode()/toString() annotations to generate respective methods. These methods includes all the object fields by default. This can have an unwanted side-effect for JPA entities: accidentally loading lazy attributes or this can easily harm the application performance if lazy loading objects are large.

2. Malfunctioning of HashSets and HashMaps 😨

Lombok's @EqualsAndHashCode annotation automatically generates the equals() and hashCode() methods for our entities, considering all fields by default.

In case of a new entity, generally the Id or unique identieir of an entity gets generated by the database, while trigerring the insert operation. So it gets changed after the entity is persisted.

Lets understand this problem with the help of an example:

@Entity
@EqualsAndHashCode
public class Book {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(nullable = false)
   private Long id;

   private String title;

}
Enter fullscreen mode Exit fullscreen mode

Now we will execute below code and will check the result:

Book newBook = new Book();
newBook.setTitle("Book1");
Set<Book> set = new HashSet<>();

set.add(newBook);
bookRepository.save(newBook);

if(set.contains(newBook)) {
    System.out.println("Set Contains Book");
}
else {
    System.out.println("Set Does Not Contains Book");
}
Enter fullscreen mode Exit fullscreen mode

By executing, we found that above code prints Set Does Not Contains Book, even we added the entity in set initially. The reason is as below:

(i). Lombok's @EqualsAndHashCode method generated a hashCode() method for this entity and considered id and title.
(ii). On a high level, HashSet/HashMap uses the hashCode() method to determine where to store a particular object, and it is also used during lookup when calling the contains method. First, the hashCode method is calculated, which returns the location that is referred to in order to check if the object is present at that particular level.
(iii). When we added the entity to set initially, the id attribute of entity was null, hence the hashCode was something different.
(iv). Now, since the entity is persisted in database, the id attribute of entity is changed. This resulted in the changed value of hashCode.
(v). When hashset will check if entity exists or not, it will refer the hashCode method and will found that at that location, entity does not exists.

This problem can lead to unexpected behaviour in our application logic.

3. Missing No-Argument Constructor πŸ˜•

JPA relies on a no-argument constructor to create entities. Lombok's @NoArgsConstructor annotation seems like an ideal solution, automatically generating the required constructor for us. However, we must exercise caution, especially when using inheritance or constructors with arguments.

Consider the following example:

@Entity
@Data
public class Book {
    // ...

    public Book(String title) {
        this.title = title;
    }
}
Enter fullscreen mode Exit fullscreen mode

In this case, if we attempt to create an instance of Book using the no-argument constructor, it will throw a runtime exception due to the missing title argument. To overcome this, we can explicitly define a no-argument constructor, add @NoArgsConstructor annotation or provide default values for all constructor parameters.

Conclusion:

Lombok and JPA integration can significantly enhance our Java development experience, but it's crucial to understand potential pitfalls that may arise. By being mindful of lazy loading, carefully handling collections in equals() and hashCode() methods, and addressing the no-argument constructor requirements, we can build robust and efficient applications. Remember, the key is to leverage these powerful tools while remaining vigilant in our coding practices. Happy coding! πŸš€πŸ”₯

Top comments (0)