Hibernate is the de facto JPA implementation, providing powerful ORM capabilities, caching, and query facilities beyond the JPA specification.

Session / EntityManager

In JPA terms, Hibernate’s Session corresponds to EntityManager:

  // JPA standard
EntityManager em = emf.createEntityManager();

// Hibernate native (avoid in portable code)
Session session = em.unwrap(Session.class);
  

Mapping Annotations

  @Entity
@Table(name = "employees", indexes = {
    @Index(name = "idx_dept", columnList = "department_id")
})
@NamedQuery(name = "Employee.findByDept",
            query = "SELECT e FROM Employee e WHERE e.department.id = :deptId")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq")
    @SequenceGenerator(name = "emp_seq", sequenceName = "employee_seq", allocationSize = 50)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Enumerated(EnumType.STRING)
    private EmployeeStatus status;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id")
    private Department department;

    @Version
    private Long version;  // optimistic locking
}
  

Fetch Types

Type Behavior Use when
LAZY Load on access Default for @OneToMany, @ManyToMany
EAGER Load immediately Default for @ManyToOne, @OneToOne
  // Avoid LazyInitializationException — fetch within transaction
@Transactional(readOnly = true)
public EmployeeDto getEmployee(Long id) {
    Employee emp = employeeRepository.findById(id).orElseThrow();
    // Access lazy collection within transaction
    int orderCount = emp.getOrders().size();
    return toDto(emp);
}
  

Hibernate Configuration (Spring Boot)

  spring:
  jpa:
    hibernate:
      ddl-auto: validate  # none | validate | update | create | create-drop
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
        jdbc:
          batch_size: 25
          order_inserts: true
          order_updates: true
        format_sql: true
        use_sql_comments: true
    open-in-view: false  # disable OSIV anti-pattern
  

Batch Operations

  @Transactional
public void bulkInsert(List<Employee> employees) {
    for (int i = 0; i < employees.size(); i++) {
        entityManager.persist(employees.get(i));
        if (i % 25 == 0) {
            entityManager.flush();
            entityManager.clear();  // prevent memory buildup
        }
    }
}
  

Enable batching:

  spring.jpa.properties.hibernate.jdbc.batch_size: 25
spring.jpa.properties.hibernate.order_inserts: true
  

Second-Level Cache

  @Entity
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product { }

// Query cache
@QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
List<Product> findByCategory(String category);
  
  spring.jpa.properties.hibernate.cache.use_second_level_cache: true
spring.jpa.properties.hibernate.cache.region.factory_class:
  org.hibernate.cache.jcache.JCacheRegionFactory
  

Best Practices

  • Set open-in-view: false to prevent lazy loading outside transactions
  • Use validate ddl-auto in production; manage schema with Flyway/Liquibase
  • Enable JDBC batching for bulk inserts/updates
  • Use @Version for optimistic locking on concurrent updates
  • Prefer FetchType.LAZY and fetch joins for controlled loading