Hibernate IQs

  1. Hibernate Mapping with Multiple column
  2.  How data is stored in Cassandra 
  3.  What is difference between Cassandra and RDBMS 
  4. Embedded and embeddable
  5. N+1 Problem and solution

N+1 Problem

Definition

The N+1 problem is a performance issue that can occur in database-driven applications, particularly those using object-relational mapping (ORM) frameworks like Hibernate, when fetching data using relationships. The problem arises when the application executes N+1 database queries to retrieve N entities along with their associated relationships.

Explanation :

  1. Explanation:
    • Suppose you have an entity Parent with a one-to-many relationship to Child. When you fetch a list of Parent entities, each with a collection of associated Child entities, the N+1 problem occurs.
    • For each Parent entity fetched, an additional query is executed to fetch its associated Child entities. Hence, if you have N Parent entities, you end up executing N+1 queries (N queries to fetch the Parent entities and 1 query for each Parent to fetch its Child entities).
  2. Example:
    • Consider the following code:
List<Parent> parents = entityManager.createQuery("SELECT p FROM Parent p", Parent.class).getResultList();
for (Parent parent : parents) {
    // Each iteration triggers an additional query to fetch Child entities for each Parent
    List<Child> children = parent.getChildren();
}

In this example, if there are N Parent entities, it leads to N+1 queries.

Solution to N+1 Problem:

  1. Eager loading (not recommended)
  2. lazy loading with batch fetching(recommended)

Eager Fetching:

  • One solution is to use eager fetching, where the associated entities are fetched along with the main entity in a single query.
  • In JPA/Hibernate, you can use the @OneToMany or @ManyToOne annotation with the fetch attribute set to FetchType.EAGER.
@Entity
public class Parent {
    // Other annotations...

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "parent")
    private List<Child> children;
}

However, eager fetching has its downsides, such as potentially fetching more data than needed and impacting performance in other scenarios.

Lazy Loading with Batch Fetching:

  • A more efficient solution is to use lazy loading for relationships and employ batch fetching to minimize the number of queries.
  • Lazy loading defers the loading of associated entities until they are actually accessed, and batch fetching allows fetching multiple entities in a single query.
@Entity
public class Parent {
    // Other annotations...

    @OneToMany(mappedBy = "parent")
    private List<Child> children;
}

@Entity
public class Child {
    // Other annotations...

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent;
}


List<Parent> parents = entityManager.createQuery("SELECT p FROM Parent p", Parent.class).getResultList();
// Batch fetch the children for all parents in a single query
parents.forEach(parent -> {
    List<Child> children = parent.getChildren(); // Lazy loading
    children.size(); // Trigger batch fetch
});

  • With this approach, you fetch the children in batches, reducing the number of queries.

It’s essential to carefully choose the fetching strategy based on your application’s requirements and performance considerations to avoid the N+1 problem and ensure efficient database access.

Embedded and embeddable

@Embedded and @Embeddable annotations are used to represent and map composite or embedded objects in database tables. These annotations allow you to model a part of an entity as an embedded object, and the data of this embedded object is stored in the same table as the owning entity.

@Embeddable:

  1. Annotation:
    • @Embeddable is used to annotate a class whose instances are intended to be embedded into entities.
    • It marks a class as embeddable, meaning its instances can be used as components within other entities.
  2. Example:
    • Let’s say you have an Address class that you want to embed in other entities.
@Embeddable
public class Address {
    private String street;
    private String city;
    private String zipCode;

    // Constructors, getters, setters
}

@Embedded:

  1. Annotation:
    • @Embedded is used to embed an @Embeddable object within an entity.
    • It is placed on a field or a method in the entity class to indicate that the attribute should be stored as an embedded object in the database.
  2. Example:
    • Consider an Employee entity that includes an embedded Address.
@Entity
public class Employee {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @Embedded
    private Address address;

    // Constructors, getters, setters
}

In this example, the Employee entity has an @Embedded attribute, address, which is of type Address. The @Embeddable annotation on the Address class indicates that instances of Address can be embedded within other entities.

With these annotations, when Hibernate generates the database schema, it creates a single table for the Employee entity that includes columns for both the regular attributes of the Employee entity and the attributes of the embedded Address object.

CREATE TABLE Employee (
    id BIGINT PRIMARY KEY,
    name VARCHAR(255),
    address_street VARCHAR(255),
    address_city VARCHAR(255),
    address_zipCode VARCHAR(255)
);

This way, the data of the embedded object is stored as part of the owning entity’s table, allowing you to model more complex data structures in your domain model.