Hibernate Development Guide
Hibernate is one of the most popular Object-Relational Mapping (ORM) frameworks for Java applications. This guide demonstrates how to leverage Hibernate's powerful features with Tacnode's PostgreSQL compatibility to build robust, data-driven applications.
Prerequisites
Before starting, ensure you have:
- Java 8+ (Java 11+ recommended)
- Maven or Gradle for dependency management
- A Tacnode database with appropriate access credentials
Create Database and Table
Set up your database structure in Tacnode:
-- Create the database
CREATE DATABASE example;
-- Connect to the database and create a table
\c example;
CREATE TABLE customer (
id BIGINT NOT NULL,
name TEXT NOT NULL,
email TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
Project Setup
Maven Dependencies
Add the required dependencies to your pom.xml
:
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<hibernate.version>5.6.15.Final</hibernate.version>
<postgresql.version>42.5.0</postgresql.version>
</properties>
<dependencies>
<!-- Hibernate Core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- PostgreSQL JDBC Driver -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<!-- JPA API -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<!-- Lombok for cleaner code (optional) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<!-- SLF4J for logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.36</version>
</dependency>
</dependencies>
Entity Class Definition
Create a Customer entity that maps to your database table:
package io.tacnode.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "customer")
public class Customer {
@Id
@Column(name = "id")
private Long id;
@Column(name = "name", nullable = false, length = 255)
private String name;
@Column(name = "email", nullable = false, length = 255)
private String email;
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
// Constructor for required fields
public Customer(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
@PreUpdate
public void preUpdate() {
this.updatedAt = LocalDateTime.now();
}
}
Hibernate Configuration
Create hibernate.cfg.xml
in your src/main/resources
directory:
<!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>
<!-- Database connection settings -->
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQL10Dialect</property>
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.url">jdbc:postgresql://your-cluster.tacnode.io:5432/example</property>
<property name="hibernate.connection.username">your_username</property>
<property name="hibernate.connection.password">your_password</property>
<!-- Connection pool settings -->
<property name="hibernate.connection.pool_size">10</property>
<property name="hibernate.connection.autocommit">false</property>
<!-- Performance and debugging settings -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<!-- Schema generation (for development only) -->
<property name="hibernate.hbm2ddl.auto">validate</property>
<!-- Entity mappings -->
<mapping class="io.tacnode.model.Customer" />
</session-factory>
</hibernate-configuration>
CRUD Operations Implementation
Hibernate Utility Class
Create a utility class to manage SessionFactory:
package io.tacnode.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration()
.configure("hibernate.cfg.xml")
.buildSessionFactory();
} catch (Exception e) {
System.err.println("SessionFactory creation failed: " + e);
throw new ExceptionInInitializerError(e);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
if (sessionFactory != null) {
sessionFactory.close();
}
}
}
Data Access Object (DAO)
Create a DAO class for Customer operations:
package io.tacnode.dao;
import io.tacnode.model.Customer;
import io.tacnode.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import java.util.List;
import java.util.Optional;
public class CustomerDAO {
public void save(Customer customer) {
Transaction transaction = null;
try (Session session = HibernateUtil.getSessionFactory().openSession()) {
transaction = session.beginTransaction();
session.save(customer);
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException("Error saving customer", e);
}
}
public Optional<Customer> findById(Long id) {
try (Session session = HibernateUtil.getSessionFactory().openSession()) {
Customer customer = session.get(Customer.class, id);
return Optional.ofNullable(customer);
} catch (Exception e) {
throw new RuntimeException("Error finding customer by id: " + id, e);
}
}
public List<Customer> findAll() {
try (Session session = HibernateUtil.getSessionFactory().openSession()) {
Query<Customer> query = session.createQuery("FROM Customer", Customer.class);
return query.getResultList();
} catch (Exception e) {
throw new RuntimeException("Error finding all customers", e);
}
}
public List<Customer> findByNamePattern(String namePattern) {
try (Session session = HibernateUtil.getSessionFactory().openSession()) {
Query<Customer> query = session.createQuery(
"FROM Customer c WHERE c.name LIKE :pattern", Customer.class);
query.setParameter("pattern", "%" + namePattern + "%");
return query.getResultList();
} catch (Exception e) {
throw new RuntimeException("Error finding customers by name pattern", e);
}
}
public void update(Customer customer) {
Transaction transaction = null;
try (Session session = HibernateUtil.getSessionFactory().openSession()) {
transaction = session.beginTransaction();
session.update(customer);
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException("Error updating customer", e);
}
}
public void delete(Long id) {
Transaction transaction = null;
try (Session session = HibernateUtil.getSessionFactory().openSession()) {
transaction = session.beginTransaction();
Customer customer = session.get(Customer.class, id);
if (customer != null) {
session.delete(customer);
}
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException("Error deleting customer with id: " + id, e);
}
}
}
Main Application
Create a main class to demonstrate CRUD operations:
package io.tacnode;
import io.tacnode.dao.CustomerDAO;
import io.tacnode.model.Customer;
import io.tacnode.util.HibernateUtil;
import java.util.List;
import java.util.Optional;
public class CustomerApplication {
private static final CustomerDAO customerDAO = new CustomerDAO();
public static void main(String[] args) {
try {
// Create sample customers
System.out.println("=== Creating Customers ===");
createSampleCustomers();
// Read all customers
System.out.println("\n=== Reading All Customers ===");
readAllCustomers();
// Update a customer
System.out.println("\n=== Updating Customer ===");
updateCustomer();
// Read updated customer
System.out.println("\n=== Reading Updated Customer ===");
readCustomerById(2L);
// Delete a customer
System.out.println("\n=== Deleting Customer ===");
deleteCustomer(1L);
// Read remaining customers
System.out.println("\n=== Reading Remaining Customers ===");
readAllCustomers();
// Search customers by name
System.out.println("\n=== Searching Customers by Name ===");
searchCustomersByName("Emma");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.shutdown();
}
}
private static void createSampleCustomers() {
Customer customer1 = new Customer(1L, "Jacob Emily", "jacob.emily@tacnode.io");
Customer customer2 = new Customer(2L, "Michael Emma", "michael.emma@tacnode.io");
customerDAO.save(customer1);
customerDAO.save(customer2);
System.out.println("Created customer: " + customer1.getName());
System.out.println("Created customer: " + customer2.getName());
}
private static void readAllCustomers() {
List<Customer> customers = customerDAO.findAll();
customers.forEach(customer ->
System.out.println("Customer: " + customer.getName() +
" | Email: " + customer.getEmail() +
" | Created: " + customer.getCreatedAt()));
}
private static void readCustomerById(Long id) {
Optional<Customer> customer = customerDAO.findById(id);
if (customer.isPresent()) {
Customer c = customer.get();
System.out.println("Found customer: " + c.getName() +
" | Email: " + c.getEmail() +
" | Updated: " + c.getUpdatedAt());
} else {
System.out.println("Customer with ID " + id + " not found");
}
}
private static void updateCustomer() {
Optional<Customer> customerOpt = customerDAO.findById(2L);
if (customerOpt.isPresent()) {
Customer customer = customerOpt.get();
customer.setEmail("michael.emma@gmail.com");
customer.setName("Michael Emma Johnson");
customerDAO.update(customer);
System.out.println("Updated customer: " + customer.getName());
}
}
private static void deleteCustomer(Long id) {
customerDAO.delete(id);
System.out.println("Deleted customer with ID: " + id);
}
private static void searchCustomersByName(String namePattern) {
List<Customer> customers = customerDAO.findByNamePattern(namePattern);
System.out.println("Found " + customers.size() + " customers matching '" + namePattern + "':");
customers.forEach(customer ->
System.out.println(" - " + customer.getName() + " (" + customer.getEmail() + ")"));
}
}
Sample Output
When you run the application, you should see output similar to:
=== Creating Customers ===
Created customer: Jacob Emily
Created customer: Michael Emma
=== Reading All Customers ===
Customer: Jacob Emily | Email: jacob.emily@tacnode.io | Created: 2024-01-15T10:30:45
Customer: Michael Emma | Email: michael.emma@tacnode.io | Created: 2024-01-15T10:30:45
=== Updating Customer ===
Updated customer: Michael Emma Johnson
=== Reading Updated Customer ===
Found customer: Michael Emma Johnson | Email: michael.emma@gmail.com | Updated: 2024-01-15T10:31:15
=== Deleting Customer ===
Deleted customer with ID: 1
=== Reading Remaining Customers ===
Customer: Michael Emma Johnson | Email: michael.emma@gmail.com | Created: 2024-01-15T10:30:45
=== Searching Customers by Name ===
Found 1 customers matching 'Emma':
- Michael Emma Johnson (michael.emma@gmail.com)
Best Practices
Performance Optimization
-
Use Connection Pooling:
<property name="hibernate.connection.pool_size">20</property> <property name="hibernate.c3p0.min_size">5</property> <property name="hibernate.c3p0.max_size">20</property> <property name="hibernate.c3p0.timeout">300</property>
-
Enable Second-Level Cache:
<property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.region.factory_class"> org.hibernate.cache.ehcache.EhCacheRegionFactory </property>
-
Batch Processing:
<property name="hibernate.jdbc.batch_size">20</property> <property name="hibernate.order_inserts">true</property> <property name="hibernate.order_updates">true</property>
Error Handling
Always implement proper transaction management:
public void saveWithProperErrorHandling(Customer customer) {
Transaction transaction = null;
Session session = null;
try {
session = HibernateUtil.getSessionFactory().openSession();
transaction = session.beginTransaction();
session.save(customer);
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException("Error saving customer", e);
} finally {
if (session != null) {
session.close();
}
}
}
Security Considerations
-
Use Parameterized Queries:
Query query = session.createQuery("FROM Customer WHERE email = :email"); query.setParameter("email", emailAddress);
-
Validate Input Data:
@Column(name = "email", nullable = false, length = 255) @Email @NotBlank private String email;
This comprehensive guide demonstrates how to build robust Java applications using Hibernate with Tacnode. The PostgreSQL compatibility ensures that all Hibernate features work seamlessly, providing you with a powerful and scalable database solution.