Hibernate Configuration: XML vs Annotations vs Java Config Explained

Illustration for Hibernate Configuration: XML vs Annotations vs Java Config Explained
By Last updated:

Hibernate is one of the most widely used Object-Relational Mapping (ORM) frameworks in Java. To use it effectively, developers need to configure Hibernate properly. Over time, Hibernate has evolved to support three main configuration approaches:

  1. XML-based configuration (traditional)
  2. Annotation-based configuration (modern and concise)
  3. Java-based configuration (programmatic and flexible)

Understanding the differences between these approaches is crucial for designing maintainable and production-ready applications. In this guide, we’ll compare XML vs Annotations vs Java Config, provide complete examples, discuss best practices, and highlight pitfalls.


Hibernate Configuration Approaches

1. XML-Based Configuration

XML was the earliest method for setting up Hibernate. It uses hibernate.cfg.xml for global settings and .hbm.xml for entity mappings.

hibernate.cfg.xml

<hibernate-configuration>
  <session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/testdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">root</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
    <property name="hibernate.hbm2ddl.auto">update</property>
    <property name="hibernate.show_sql">true</property>
    <mapping resource="student.hbm.xml"/>
  </session-factory>
</hibernate-configuration>

student.hbm.xml

<class name="com.example.Student" table="students">
    <id name="id" column="id">
        <generator class="identity"/>
    </id>
    <property name="name" column="student_name"/>
</class>

Pros:

  • Centralized and explicit.
  • Good for legacy projects.

Cons:

  • Verbose.
  • Hard to maintain in large projects.

2. Annotation-Based Configuration

Introduced with JPA, annotation-based configuration moves mappings directly into Java classes.

import jakarta.persistence.*;

@Entity
@Table(name = "students")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "student_name", nullable = false)
    private String name;

    public Student() {}
    public Student(String name) { this.name = name; }

    // Getters and Setters
}

Updated hibernate.cfg.xml (only core configs)

<hibernate-configuration>
  <session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/testdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">root</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
    <mapping class="com.example.Student"/>
  </session-factory>
</hibernate-configuration>

Pros:

  • Cleaner and easier to maintain.
  • Standardized with JPA.

Cons:

  • Clutters entity class with mapping metadata.

3. Java-Based Configuration (Programmatic)

Java-based configuration uses Hibernate’s Configuration API or frameworks like Spring Boot.

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    private static final SessionFactory sessionFactory;

    static {
        try {
            sessionFactory = new Configuration()
                    .setProperty("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver")
                    .setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/testdb")
                    .setProperty("hibernate.connection.username", "root")
                    .setProperty("hibernate.connection.password", "root")
                    .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect")
                    .addAnnotatedClass(Student.class)
                    .buildSessionFactory();
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

Pros:

  • Flexible and type-safe.
  • No external XML required.

Cons:

  • Configuration scattered across code.
  • Less readable for non-developers.

CRUD Operations (Works Across Configurations)

Create

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

Student student = new Student("Alice");
session.persist(student);

tx.commit();
session.close();

Read

Session session = HibernateUtil.getSessionFactory().openSession();
Student student = session.get(Student.class, 1L);
System.out.println(student.getName());
session.close();

Update

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

Student student = session.get(Student.class, 1L);
student.setName("Updated Alice");
session.update(student);

tx.commit();
session.close();

Delete

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

Student student = session.get(Student.class, 1L);
session.remove(student);

tx.commit();
session.close();

Querying in Hibernate

HQL

List<Student> students = session.createQuery("FROM Student WHERE name = :name", Student.class)
        .setParameter("name", "Alice")
        .list();

Criteria API

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> root = cq.from(Student.class);
cq.select(root).where(cb.equal(root.get("name"), "Alice"));
List<Student> results = session.createQuery(cq).getResultList();

Native SQL

List<Object[]> results = session.createNativeQuery("SELECT * FROM students").list();

Caching and Performance Considerations

  • Lazy Loading (default) → Loads when needed. Like ordering food only when hungry.
  • Eager Loading → Fetches everything upfront.
  • First-Level Cache → Session-based.
  • Second-Level Cache → Application-wide.
  • Query Cache → Speeds up repeated queries.

Real-World Integration with Spring Boot

Spring Boot prefers annotation and Java config:

spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

Repository:

public interface StudentRepository extends JpaRepository<Student, Long> {}

Anti-Patterns and Pitfalls

  • Using eager fetching everywhere → performance issues.
  • Overusing cascade delete → unintended data loss.
  • Mixing XML and annotations inconsistently.

Best Practices

  • Prefer annotations for new projects.
  • Use Java-based config with Spring Boot.
  • Keep XML only in legacy systems.
  • Always enable lazy fetching unless eager is justified.

📌 Hibernate Version Notes

Hibernate 5.x

  • Relies on javax.persistence.
  • XML was more common.
  • Legacy Criteria API.

Hibernate 6.x

  • Migrated to jakarta.persistence.
  • Enhanced SQL support.
  • Modernized bootstrap and query API.

Conclusion and Key Takeaways

  • Hibernate supports XML, annotations, and Java-based configuration.
  • XML is legacy, annotations are widely used, Java config offers flexibility.
  • CRUD operations and queries remain consistent regardless of configuration style.
  • Hibernate 6 modernizes configuration with Jakarta compliance.
  • Choose configuration style based on project type and team preference.

FAQ: Expert Hibernate Questions

  1. What’s the difference between Hibernate and JPA?
    JPA is a specification; Hibernate is a powerful implementation.

  2. How does Hibernate caching improve performance?
    By reducing database hits using session-level and second-level caches.

  3. What are the drawbacks of eager fetching?
    Loads unnecessary data, slows performance.

  4. How do I solve the N+1 select problem?
    Use JOIN FETCH or batch fetching.

  5. Can I use Hibernate without Spring?
    Yes, Hibernate runs standalone with XML or Java config.

  6. What’s the best inheritance mapping strategy?
    Depends: JOINED for normalization, SINGLE_TABLE for speed.

  7. How does Hibernate handle composite keys?
    With @EmbeddedId or @IdClass.

  8. How is Hibernate 6 different from Hibernate 5?
    Hibernate 6 uses jakarta.persistence, has enhanced SQL and query APIs.

  9. Is Hibernate suitable for microservices?
    Yes, but prefer DTOs and stateless design.

  10. When should I not use Hibernate?
    Avoid in high-performance analytics or batch ETL jobs.