On this page
Hibernate Basics
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: falseto prevent lazy loading outside transactions - Use
validateddl-auto in production; manage schema with Flyway/Liquibase - Enable JDBC batching for bulk inserts/updates
- Use
@Versionfor optimistic locking on concurrent updates - Prefer
FetchType.LAZYand fetch joins for controlled loading