Hibernate Interview Questions

Hibernate Interview Questions | Also see:- Java 8 Interview Questions, Most Commonly Asked Java Interview Questions, Spring Boot Interview Questions

Hibernate is an object-relational mapping (ORM) tool used to map Java Objects and database tables.

It provides JPA implementation hence we can use JPA annotations as well as XML configurations to achieve this mapping.

  • Hibernate eliminates all the boilerplate code that comes with JDBC.
  • It supports HQL (Hibernate query language) which is more Object-oriented.
  • It provides transaction management implicitly.
  • Hibernate throws JDBCException or HibernateException which are the unchecked exceptions, so we don’t need to worry about handling using try and catch.
  • Hibernate supports caching for better performance.
  1. SessionFactory (org.hibernate.SessionFactory):- An instance of this is used to retrieve Session objects for database operations. We need to initialize that once and can cache it to reuse it again and again. It’s like one SessionFactory object per database connection. Like 1 for MySQL, 1 for Oracle. SessionFactory gives Session object.
  2. Session (org.hibernate.Session):- It’s a factory for transactions, it’s used for connecting applications with persistent stores like hibernate framework / DB. It is used to get a physical connection with the database. It also provides methods for CRUD operations. It provides a Transaction object.
  3. Transaction (org.hibernate.Transaction):- This specifies single/atomic units of work.
SessionFactory factory = metadata.getSessionFactoryBuilder().build();
Session session = factory.openSession();
Transaction t = session.beginTransaction();

session.save(persistantObj);

t.commit();
session.close(); // Close session first
factory.close(); // Then close factory
  • @Entity: Used with model classes to specify that they are entity beans.
  • @Table: Used with entity beans to define the corresponding table name in the database.
  • @Access: Used to define the access type, either field or property. The default value is field and if you want hibernate to use getter/setter methods then you need to set it to property. Example:- @Access(value = AccessType.PROPERTY)
  • @Id: Used to define the primary key in the entity bean.
  • @EmbeddedId: Used to define composite primary key in the entity bean. 
  • @Column: Used to define the column name in a database table.
  • @GeneratedValue: Used to define the strategy to be used for the generation of the primary key. Used in conjunction with GenerationType enum eg: GenerationType.IDENTITY.
  • @OneToOne: Used to define the one-to-one mapping between two entity beans. We have other similar annotations as @OneToMany, @ManyToOne, and @ManyToMany 
  • org.hibernate.annotations.Cascade: Used to define the cascading between two entity beans, used with mappings. It works in conjunction with org.hibernate.annotations.CascadeType
  • javax.persistence.PrimaryKeyJoinColumn: Used to define the property for the foreign key used with org.hibernate.annotations.GenericGenerator, and org.hibernate.annotations.Parameter

There are 4 types of Association mappings in Hibernate:-

  • One-To-One
  • Many-To-One
  • One-To-Many
  • Many-To-Many
@OneToOne(targetEntity=Employee.class)
private Employee employee;
// many to One
class Student {
   @ManyToOne(cascade=CascadeType.ALL)
   private Address address;
}
// many to many
class Student {
    @ManyToMany(targetEntity = Degree.class, cascade={CascadeType.ALL})
    @JoinTable(name="DegreeStudentThirdTable", 
        joinColumns = {@JoinColumn(name = "StudentId")},
        inverseJoinColumns = {@JoinColumn(name = "CertificateId")}
    )
    private List<Degree> degrees;
}
// one to many
class Department {
    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
    private List<Employee> employees;

    // Getters and Setters
}

class Employee {
    @ManyToOne
    @JoinColumn(name = "department_id")
    private Department department;

    // Getters and Setters
}
  • Contains database-specific configurations and is used to initialize SessionFactory.
  • Conventionally, its name should be hibernate.cfg.xml
  • If you need to connect to Oracle/SQL Server then create another one here.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- JDBC Database connection settings -->
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/your_database</property>
        <property name="hibernate.connection.username">your_username</property>
        <property name="hibernate.connection.password">your_password</property>

        <!-- JDBC connection pool settings -->
        <property name="hibernate.c3p0.min_size">5</property>
        <property name="hibernate.c3p0.max_size">20</property>
        <property name="hibernate.c3p0.timeout">300</property>
        <property name="hibernate.c3p0.max_statements">50</property>
        <property name="hibernate.c3p0.idle_test_period">3000</property>

        <!-- SQL dialect -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="hibernate.current_session_context_class">thread</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- Names the annotated entity class -->
        <mapping class="com.example.YourEntityClass"/>
    </session-factory>
</hibernate-configuration>

The mapping file name conventionally should be class_name.hbm.xml.

  • hibernate-mapping: root element
  • class: specifies the Persistent class.
  • id: specifies the primary key attribute in the class. 
  • generator: used to generate the primary key. 
  • property: specifies the property name of the Persistent class.

getCurrentSession() method returns the session bound to the context. Since this session object belongs to the context of Hibernate, it is okay if you don’t close it. Once the SessionFactory is closed, this session object gets closed.

While openSession() method helps in opening a new session. You should close this session object once you are done with all the database operations. Also, you should open a new session for each request in a multi-threaded environment.

Hibernate is a widely used object-relational mapping (ORM) framework in the Java ecosystem, and it offers several advantages, including:-

  1. Simplified Database Operations: Hibernate simplifies the interaction with the database by abstracting away the low-level JDBC (Java Database Connectivity) code. Developers can work with objects and entities directly, rather than dealing with SQL queries, making database operations more intuitive and less error-prone.
  2. Database Portability: Hibernate provides a layer of abstraction over the underlying database, allowing developers to write database-independent code. This means that applications developed with Hibernate can easily switch between different database vendors without significant changes to the codebase, enhancing portability and reducing vendor lock-in.
  3. Automatic Table Generation: Hibernate can automatically generate database tables based on the entity classes defined in the application. This feature eliminates the need for developers to write DDL (Data Definition Language) scripts manually, saving time and effort in the development process.
  4. Caching Mechanisms: Hibernate includes built-in caching mechanisms that help improve application performance by reducing the number of database queries. It supports various levels of caching, including first-level cache (session cache) and second-level cache (shared cache), allowing developers to optimize data retrieval and minimize database round-trips.
  5. Lazy Loading: Hibernate supports lazy loading, which means that it loads associated objects or collections from the database only when they are accessed for the first time. This can lead to significant performance improvements, especially when dealing with large object graphs, as it avoids fetching unnecessary data upfront.
  6. Transparent Persistence: With Hibernate, developers can achieve transparent persistence, meaning that changes made to the managed entities are automatically synchronized with the database without explicit save or update operations. This simplifies the codebase and reduces the amount of boilerplate code required for database interactions.
  7. Object-Oriented Approach: Hibernate allows developers to work with domain model objects directly, rather than dealing with relational data in a procedural manner. This aligns well with object-oriented programming principles and facilitates better code organization, maintainability, and readability.
  8. Integration with JPA: Hibernate implements the Java Persistence API (JPA) specification, which is a standard API for object-relational mapping in Java EE applications. This allows developers to leverage JPA annotations and APIs while benefiting from Hibernate’s additional features and optimizations.

Overall, Hibernate provides a robust and feature-rich solution for managing the persistence layer in Java applications, offering developers greater productivity, flexibility, and performance optimization capabilities.

In Hibernate, both get and load methods are used to retrieve persistent objects from the database, but they have some differences in behavior:-

  1. Timing of Database Access:
  • get: The get method immediately hits the database and retrieves the object. If the object with the specified identifier is not found in the database, get returns null.
  • load: The load method doesn’t hit the database immediately. Instead, it returns a proxy object (a placeholder) with the given identifier. The actual database access (lazy loading) occurs only when a method is invoked on the proxy object that requires the real data. If the object with the specified identifier doesn’t exist in the database and is not found during lazy loading, load throws an ObjectNotFoundException.
  1. Return Type:
  • get: Returns the actual object if found in the database, or null if not found.
  • load: Returns a proxy object with the given identifier. This proxy object is of the same type as the entity class but is not initialized with real data until needed.
  1. Proxy vs. Real Object:
  • get: Always returns either a fully initialized object (if found) or null.
  • load: Returns a proxy object initially, and the real object is loaded from the database when needed. This allows for lazy loading of associated objects, potentially improving performance by avoiding unnecessary database calls.
  1. Exception Handling:
  • get: Returns null if the object is not found in the database. No exception is thrown.
  • load: Throws an ObjectNotFoundException if the object with the specified identifier is not found in the database during lazy loading.
  1. Usage:
  • get is suitable when you expect the object to exist in the database and want to retrieve it immediately.
  • load is suitable when you want to optimize performance by deferring the loading of associated objects until they are actually needed.

In summary, while both get and load methods are used to retrieve objects from the database in Hibernate, get retrieves the actual object immediately or returns null, while load returns a proxy object and defers the database access until necessary, potentially resulting in better performance through lazy loading. However, developers need to handle the possibility of ObjectNotFoundException when using load.

Lazy loading and eager loading are strategies used in Hibernate for fetching associated objects or collections from the database. The default loading behavior depends on how the mapping is configured.

  1. Lazy Loading:
  • Lazy loading is a mechanism where associated objects or collections are loaded from the database only when they are actually needed.
  • With lazy loading, Hibernate initializes the associated objects or collections with proxy objects (placeholders) initially, and the real data is loaded from the database when an attempt is made to access the associated objects or collections.
  • Lazy loading helps in optimizing performance by fetching only the required data from the database, reducing the amount of data loaded into memory, and minimizing unnecessary database calls.
  • Lazy loading is suitable for scenarios where associated objects or collections are not always needed and can be loaded on demand.
  1. Eager Loading:
  • Eager loading is a mechanism where associated objects or collections are loaded from the database immediately along with the main object.
  • With eager loading, Hibernate fetches all the associated objects or collections from the database in a single query, ensuring that all the required data is available upfront.
  • Eager loading can lead to performance issues, especially in scenarios where a large number of associated objects or collections are fetched unnecessarily, resulting in increased memory consumption and slower application performance.
  • Eager loading is suitable for scenarios where associated objects or collections are almost always needed and can be loaded eagerly to minimize the number of database calls.

Default loading behavior in Hibernate:-

By default, Hibernate uses lazy loading for almost all types of associations to improve performance. Here’s a more accurate breakdown:

  • Single-Valued Associations (e.g., many-to-one, one-to-one):
    • Lazy Loading: By default, Hibernate loads these associations lazily. This means the associated objects are fetched only when accessed for the first time.
  • Collection-Valued Associations (e.g., one-to-many, many-to-many):
    • Lazy Loading: For collections, the default behavior is lazy loading, meaning the elements are fetched when the collection is accessed.
    • Exception: Some older versions of Hibernate eagerly load lists and arrays by default, but this isn’t standard in newer versions.

In short:

  • For sets and maps, lazy loading is always the default.
  • For lists and arrays, lazy loading is typically the default, especially in newer Hibernate versions.

It’s important for developers to consider the loading behavior carefully when designing their Hibernate mappings, taking into account factors such as performance, memory consumption, and application requirements. Hibernate provides flexibility in configuring lazy loading and eager loading behavior through mapping annotations or XML configurations, allowing developers to customize the loading strategy based on specific use cases.

  1. First Level Cache
  2. Second Level Cache
  3. Query Cache

First Level Cache:-

  • Hibernate caches query data to make our application faster and improve performance.
  • The idea behind cache is to reduce the number of database queries.
  • Hibernate first-level cache is associated with the Session object.
  • Hibernate first level cache is enabled by default and there is no way to disable it.
  • Still, Hibernate provides methods through which we can delete selected objects from the cache or clear the cache completely.
  • Any object cached in a session will not be visible to other sessions and when the session is closed, all the cached objects will also be lost.

Second Level Cache:-

  • Hibernate second-level cache is disabled by default but we can enable it through configuration.
  • Currently, EHCache and Infinispan provide the implementation for Hibernate Second level cache and we can use them.

Query Cache:- Query level cache only works with second level cache, it never works with first level cache.

Configure Hibernate Second Level Cache using EHCache

1. Add hibernate-ehcahe dependency in your maven project, if it is not maven then add corresponding jars.

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-ehcache</atifactId>
</dependency>

2. Add the below properties in the hibernate configuration file.

<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="net.sf.ehcache.configurationResourceName">/knowprogramehcache.xml</property>

3. Create an EHCache configuration file, a sample file knowprogramehcache.xml is as below:-

Hibernate Second Level Cache Configuration File Sample

4. Annotate entity beans with @Cache annotation and caching strategy to use.

import org.hibernate.annotations.Cache;
import org.hibernate.annotations. CacheConcurrencyStrategy;
@Entity
@Table(name = "ADDRESS")
@Cache (usage=CacheConcurrencyStrategy.READ_ONLY, region="employee") 
public class Address {
}

Here the region field value is the same as used in the XML file.

Second-level caching in Hibernate is a mechanism that allows caching at the session factory level, which means that multiple sessions can share the cached data. This can significantly improve performance by reducing the number of database queries. Here’s how you can achieve second-level caching in Hibernate:

Configure Cache Provider: First, you need to configure a cache provider in your Hibernate configuration file (hibernate.cfg.xml or persistence.xml). Hibernate supports several cache providers such as Ehcache, Infinispan, and Hazelcast. You need to specify the cache provider properties such as cache region factory and cache provider class.

<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>

Entity Configuration: For each entity that you want to cache, you need to annotate it with @Cache annotation or configure caching in an XML mapping file.

@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class YourEntity {
    // Entity mapping
}

Enable Second-Level Caching: Make sure to enable second-level caching for each entity in the Hibernate mapping file.

<class name="com.example.YourEntity" table="your_entity" >
    <cache usage="read-write"/>
    <!-- Other mappings -->
</class>

Query Caching: Hibernate also supports query caching, which caches the results of queries. You need to enable query caching in the Hibernate configuration and cache the queries explicitly.

Query query = session.createQuery("from YourEntity where condition = :condition");
query.setParameter("condition", value);
query.setCacheable(true); // Enable query caching
List<YourEntity> entities = query.list();

Manage Cache: You can manage the cache programmatically using the SessionFactory object.

SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) sessionFactory;
SessionFactoryImpl.CacheImpl cache = (SessionFactoryImpl.CacheImpl) sessionFactoryImpl.getCache();
cache.evictEntityRegion(YourEntity.class);
cache.evictQueryRegion("queryName");

By following these steps, you can enable and configure second-level caching in Hibernate, which can significantly improve the performance of your application by reducing the number of database queries. However, you should be cautious while using caching as it can lead to stale data if not managed properly.

The Query Cache works alongside the Second-Level Cache. It caches the result set of queries, not the entities themselves. The advantage of Query Caching in Hibernate is that it significantly boosts performance by reducing database hits. Here’s how:-

  1. Speed: Frequently executed queries fetch results from the cache instead of the database, speeding up response times.
  2. Efficiency: Minimizes repetitive database operations, freeing up resources and reducing server load.
  3. Scalability: Enhances scalability by allowing more efficient use of the database, making the application more responsive under heavy load.
  4. Cost: Decreases database usage, potentially lowering operational costs in cloud-hosted environments where database queries incur costs.

First-Level Cache (Session Cache):

  • It’s the default cache associated with the Hibernate session.
  • It caches objects within the scope of a session and is not shared across sessions.

Second-Level Cache (SessionFactory Cache):

  • It’s an optional cache shared among all sessions.
  • It can cache entities, collections, and query results.
  • It requires explicit configuration and integration with a third-party cache provider like Ehcache, Infinispan, etc.

Query Cache:

  • The Query Cache works alongside the Second-Level Cache.
  • It caches the result set of queries, not the entities themselves.

If you enjoyed this post, share it with your friends. Do you want to share more information about the topic discussed above or do you find anything incorrect? Let us know in the comments. Thank you!

Leave a Comment

Your email address will not be published. Required fields are marked *