Hibernate ORM
Hibernate is the de facto standard ORM library in the Java ecosystem. As an Object-Relational Mapping (O/RM) tool, it elegantly connects object-oriented Java objects with relational databases. Standardized as part of Jakarta EE (formerly Java EE), it provides flexible data access through HQL (Hibernate Query Language) and Criteria API.
GitHub Overview
hibernate/hibernate-orm
Hibernate's core Object/Relational Mapping functionality
Topics
Star History
Library
Hibernate ORM
Overview
Hibernate is the de facto standard ORM library in the Java ecosystem. As an Object-Relational Mapping (O/RM) tool, it elegantly connects object-oriented Java objects with relational databases. Standardized as part of Jakarta EE (formerly Java EE), it provides flexible data access through HQL (Hibernate Query Language) and Criteria API.
Details
Hibernate ORM 7.0, released in May 2025, is the latest version supporting Jakarta Persistence 3.2 specification. With over 20 years of development history, this mature library supports major RDBMSs including Oracle, MySQL, PostgreSQL, and SQL Server. It's widely used from standalone applications to enterprise environments.
Key Features
- Jakarta Persistence 3.2 Support: Compliant with the latest JPA specification
- HQL (Hibernate Query Language): SQL-like query language
- Criteria API: Programmatic query construction
- Mapping Capabilities: Automatic mapping between Java classes and database tables
- Caching Features: Performance optimization through first and second-level caching
- Transaction Management: Support for both declarative and programmatic approaches
Pros and Cons
Pros
- Most established ORM in Java ecosystem (20+ years of development)
- Standardized as Jakarta EE with built-in support in many application servers
- Significant reduction in coding compared to JDBC
- Database abstraction reduces vendor dependencies
- External configuration separates from application code
- Comprehensive documentation and mature community
Cons
- High learning curve requiring deep knowledge of RDB and SQL
- Performance tuning difficult for complex queries
- ORM anti-pattern criticism (causes anemic domain models)
- Complex configuration with high barrier for beginners
- Strict runtime requirements (Java 11+, Jakarta EE migration)
- More boilerplate code compared to newer ORMs
References
Code Examples
Basic Setup
<!-- Maven dependencies -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>7.0.0.Final</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
Entity Definition
import jakarta.persistence.*;
import java.time.LocalDate;
import java.util.List;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
@Column(name = "email", unique = true, nullable = false)
private String email;
@Column(name = "full_name")
private String fullName;
@Column(name = "birth_date")
private LocalDate birthDate;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private UserStatus status;
// One-to-Many relationship
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Book> books;
// Constructors
public User() {}
public User(String username, String email, String fullName) {
this.username = username;
this.email = email;
this.fullName = fullName;
this.status = UserStatus.ACTIVE;
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
// Other getters and setters...
}
@Entity
@Table(name = "books")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "isbn", unique = true, length = 13)
private String isbn;
@Column(name = "title", nullable = false)
private String title;
@Column(name = "description", columnDefinition = "TEXT")
private String description;
@Column(name = "publication_date")
private LocalDate publicationDate;
// Many-to-One relationship
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private User author;
// Constructors and getters/setters...
}
public enum UserStatus {
ACTIVE, INACTIVE, SUSPENDED
}
Hibernate Configuration
// persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="myApp">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.example.entity.User</class>
<class>com.example.entity.Book</class>
<properties>
<!-- Database connection settings -->
<property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/myapp"/>
<property name="jakarta.persistence.jdbc.user" value="root"/>
<property name="jakarta.persistence.jdbc.password" value="password"/>
<!-- Hibernate settings -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<!-- Connection pool settings -->
<property name="hibernate.connection.pool_size" value="10"/>
<property name="hibernate.c3p0.min_size" value="5"/>
<property name="hibernate.c3p0.max_size" value="20"/>
<property name="hibernate.c3p0.timeout" value="1800"/>
<property name="hibernate.c3p0.max_statements" value="50"/>
</properties>
</persistence-unit>
</persistence>
Using EntityManagerFactory and EntityManager
import jakarta.persistence.*;
import java.util.List;
public class HibernateExample {
private static EntityManagerFactory entityManagerFactory;
public static void main(String[] args) {
// Initialize EntityManagerFactory
entityManagerFactory = Persistence.createEntityManagerFactory("myApp");
try {
// Execute CRUD operations
createUser();
readUsers();
updateUser();
deleteUser();
} finally {
// Resource cleanup
entityManagerFactory.close();
}
}
// Create user
public static void createUser() {
EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
User user = new User("john_doe", "[email protected]", "John Doe");
user.setBirthDate(LocalDate.of(1990, 5, 15));
Book book = new Book();
book.setIsbn("9780123456789");
book.setTitle("Introduction to Hibernate");
book.setDescription("Learn the basics of Hibernate");
book.setPublicationDate(LocalDate.now());
book.setAuthor(user);
em.persist(user);
em.persist(book);
transaction.commit();
System.out.println("User created with ID: " + user.getId());
} catch (Exception e) {
if (transaction.isActive()) {
transaction.rollback();
}
e.printStackTrace();
} finally {
em.close();
}
}
// Read users
public static void readUsers() {
EntityManager em = entityManagerFactory.createEntityManager();
try {
// Get all users
List<User> users = em.createQuery("SELECT u FROM User u", User.class)
.getResultList();
System.out.println("Total users: " + users.size());
for (User user : users) {
System.out.println("User: " + user.getUsername() + " (" + user.getEmail() + ")");
}
// Find by ID
User user = em.find(User.class, 1L);
if (user != null) {
System.out.println("Found user: " + user.getFullName());
}
} finally {
em.close();
}
}
}
Using HQL Queries
public class HQLExamples {
// Basic HQL query
public List<User> findActiveUsers(EntityManager em) {
return em.createQuery(
"SELECT u FROM User u WHERE u.status = :status ORDER BY u.username",
User.class)
.setParameter("status", UserStatus.ACTIVE)
.getResultList();
}
// Query with JOIN
public List<User> findUsersWithBooks(EntityManager em) {
return em.createQuery(
"SELECT DISTINCT u FROM User u JOIN u.books b WHERE b.publicationDate > :date",
User.class)
.setParameter("date", LocalDate.now().minusYears(1))
.getResultList();
}
// Aggregation query
public Long countBooksByAuthor(EntityManager em, Long authorId) {
return em.createQuery(
"SELECT COUNT(b) FROM Book b WHERE b.author.id = :authorId",
Long.class)
.setParameter("authorId", authorId)
.getSingleResult();
}
// Native SQL query
public List<Object[]> getUserStats(EntityManager em) {
return em.createNativeQuery(
"SELECT u.username, COUNT(b.id) as book_count " +
"FROM users u LEFT JOIN books b ON u.id = b.author_id " +
"GROUP BY u.id, u.username " +
"ORDER BY book_count DESC")
.getResultList();
}
// Named Query
@NamedQuery(
name = "User.findByEmailDomain",
query = "SELECT u FROM User u WHERE u.email LIKE CONCAT('%', :domain)"
)
// Define in entity class
public List<User> findUsersByEmailDomain(EntityManager em, String domain) {
return em.createNamedQuery("User.findByEmailDomain", User.class)
.setParameter("domain", domain)
.getResultList();
}
}
Using Criteria API
import jakarta.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
public class CriteriaExamples {
// Dynamic query construction
public List<User> findUsersByCriteria(EntityManager em, String username, String email, UserStatus status) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (username != null && !username.isEmpty()) {
predicates.add(cb.like(user.get("username"), "%" + username + "%"));
}
if (email != null && !email.isEmpty()) {
predicates.add(cb.like(user.get("email"), "%" + email + "%"));
}
if (status != null) {
predicates.add(cb.equal(user.get("status"), status));
}
query.select(user)
.where(predicates.toArray(new Predicate[0]))
.orderBy(cb.asc(user.get("username")));
return em.createQuery(query).getResultList();
}
// Complex query with JOIN
public List<User> findUsersWithRecentBooks(EntityManager em, int months) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
Join<User, Book> book = user.join("books");
LocalDate cutoffDate = LocalDate.now().minusMonths(months);
query.select(user)
.distinct(true)
.where(cb.greaterThan(book.get("publicationDate"), cutoffDate))
.orderBy(cb.desc(book.get("publicationDate")));
return em.createQuery(query).getResultList();
}
// Aggregation query
public Object[] getUserBookStatistics(EntityManager em) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<User> user = query.from(User.class);
Join<User, Book> book = user.join("books", JoinType.LEFT);
query.multiselect(
user.get("username"),
cb.count(book.get("id")),
cb.max(book.get("publicationDate"))
)
.groupBy(user.get("id"), user.get("username"))
.having(cb.greaterThan(cb.count(book.get("id")), 0L))
.orderBy(cb.desc(cb.count(book.get("id"))));
return em.createQuery(query).getResultList().toArray(new Object[0][]);
}
}
Transaction Management
public class TransactionExamples {
// Programmatic transaction
public void transferBooks(EntityManager em, Long fromUserId, Long toUserId, List<Long> bookIds) {
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
User fromUser = em.find(User.class, fromUserId);
User toUser = em.find(User.class, toUserId);
if (fromUser == null || toUser == null) {
throw new IllegalArgumentException("User not found");
}
for (Long bookId : bookIds) {
Book book = em.find(Book.class, bookId);
if (book != null && book.getAuthor().getId().equals(fromUserId)) {
book.setAuthor(toUser);
em.merge(book);
}
}
transaction.commit();
System.out.println("Book transfer completed");
} catch (Exception e) {
if (transaction.isActive()) {
transaction.rollback();
}
System.err.println("Transaction error: " + e.getMessage());
throw e;
}
}
// Batch processing
public void batchInsertUsers(EntityManager em, List<User> users) {
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
int batchSize = 50;
for (int i = 0; i < users.size(); i++) {
em.persist(users.get(i));
if (i % batchSize == 0 && i > 0) {
em.flush();
em.clear();
}
}
transaction.commit();
} catch (Exception e) {
if (transaction.isActive()) {
transaction.rollback();
}
throw e;
}
}
}
Cache Usage
// Entity-level cache
@Entity
@Table(name = "users")
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
// Entity definition...
}
// Query cache
public List<User> getCachedActiveUsers(EntityManager em) {
return em.createQuery("SELECT u FROM User u WHERE u.status = :status", User.class)
.setParameter("status", UserStatus.ACTIVE)
.setHint("org.hibernate.cacheable", true)
.getResultList();
}
// Second-level cache configuration (hibernate.cfg.xml)
```xml
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.jcache.JCacheRegionFactory
</property>