00 Troubleshooting Programming Stories & Behind the scene & Going into proper depth - lukes8/wiki-notes GitHub Wiki

JPA, hibernate, persistence context, detached entity, cannot fetch data as entity is in detached state

When we have detached or shared exceptions from JPA hibernate then we should take care about persistence context. Simply way is to call clear() in order to start from zero. Here is short story where lets assume that in createOrder method is called for orderItem entity entityManager.detach(orderItem). Afterwards Bob is not able to fetch data via method query findByOrderId as persistence context is dirty. We need to fetch entities as new instances after clear() or we need to merge old detached entity to keep it in persistence context again - re-attaching.


// Method that calls createOrder and fetches order items by order ID
public void processOrder() {
    // Create an order item
    OrderItem orderItem = // create order item
    
    // Call createOrder method to create an order
    Order savedOrder = createOrder(orderItem);
    
    // Fetch order items associated with the saved order
    entityManager.clear(); // Clear the persistence context
    List<OrderItem> orderItems = orderItemRepository.findByOrderId(savedOrder.getId());
}
In this example, after creating the order and its associated items, we clear the persistence context using entityManager.clear(). This ensures that any previously detached entities are no longer managed, and subsequent queries will fetch new managed instances of the entities from the database.


Circular dependency, bidirectional relation, infinitive recursion

ok then what if we dont include Exclude annotation for lombok and circular dependency occurs. Show me generated equals or hashcode how might looks like
ChatGPT
If you don't use the @EqualsAndHashCode.Exclude annotation and have bidirectional relationships in your entities, Lombok's generated equals and hashCode methods will include all fields, including those that reference the other entity. This inclusion can lead to circular dependencies and infinite recursion when comparing entities.

Here's what the generated equals and hashCode methods might look like if you don't exclude the relationship fields:

Order.java without @EqualsAndHashCode.Exclude
java
Zkopírovat kód
package com.example.demo.model;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Data
@EqualsAndHashCode
public class Order {

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

    private String description;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<OrderItem> orderItems = new HashSet<>();
}
OrderItem.java without @EqualsAndHashCode.Exclude
java
Zkopírovat kód
package com.example.demo.model;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;

@Entity
@Data
@EqualsAndHashCode
public class OrderItem {

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

    private String name;
    private Integer quantity;
    private Double price;

    @ManyToOne
    @JoinColumn(name = "order_id")
    private Order order;
}
Generated equals Method
When Lombok generates the equals method without the @EqualsAndHashCode.Exclude, it will look like this:

Generated equals Method for Order.java
java
Zkopírovat kód
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Order order = (Order) o;

    if (id != null ? !id.equals(order.id) : order.id != null) return false;
    if (description != null ? !description.equals(order.description) : order.description != null) return false;
    return orderItems != null ? orderItems.equals(order.orderItems) : order.orderItems == null;
}

@Override
public int hashCode() {
    int result = id != null ? id.hashCode() : 0;
    result = 31 * result + (description != null ? description.hashCode() : 0);
    result = 31 * result + (orderItems != null ? orderItems.hashCode() : 0);
    return result;
}
Generated equals Method for OrderItem.java
java
Zkopírovat kód
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    OrderItem orderItem = (OrderItem) o;

    if (id != null ? !id.equals(orderItem.id) : orderItem.id != null) return false;
    if (name != null ? !name.equals(orderItem.name) : orderItem.name != null) return false;
    if (quantity != null ? !quantity.equals(orderItem.quantity) : orderItem.quantity != null) return false;
    if (price != null ? !price.equals(orderItem.price) : orderItem.price != null) return false;
    return order != null ? order.equals(orderItem.order) : orderItem.order == null;
}

@Override
public int hashCode() {
    int result = id != null ? id.hashCode() : 0;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + (quantity != null ? quantity.hashCode() : 0);
    result = 31 * result + (price != null ? price.hashCode() : 0);
    result = 31 * result + (order != null ? order.hashCode() : 0);
    return result;
}
Circular Dependency Issue
Order.equals Method:

Includes orderItems in the comparison.
Calls OrderItem.equals for each item in orderItems.
OrderItem.equals Method:

Includes order in the comparison.
Calls Order.equals for the order field.
This setup causes infinite recursion when Order and OrderItem are compared because Order.equals calls OrderItem.equals, which in turn calls Order.equals, and so on. This results in a StackOverflowError.

Example Scenario
Example Code
java
Zkopírovat kód
public class Main {
    public static void main(String[] args) {
        Order order1 = new Order();
        order1.setId(1L);
        order1.setDescription("Order 1");

        Order order2 = new Order();
        order2.setId(2L);
        order2.setDescription("Order 2");

        OrderItem item1 = new OrderItem();
        item1.setId(1L);
        item1.setName("Item 1");
        item1.setQuantity(10);
        item1.setPrice(100.0);
        item1.setOrder(order1);

        OrderItem item2 = new OrderItem();
        item2.setId(1L);  // Same ID as item1
        item2.setName("Item 1");
        item2.setQuantity(10);
        item2.setPrice(100.0);
        item2.setOrder(order2);

        order1.getOrderItems().add(item1);
        order2.getOrderItems().add(item2);

        System.out.println(order1.equals(order2));  // This will cause StackOverflowError
    }
}
⚠️ **GitHub.com Fallback** ⚠️