Hibernate
Jashwanth Jampala
Software Engineer @ People Tech Group | Java Full-Stack Developer | Java , Spring Boot, Microservices, Python, React, SQL, NoSQL, AWS | Open to New Opportunities
In modern Java development, managing database interactions efficiently is crucial. Spring Data JPA, a powerful abstraction over JPA, uses Hibernate internally as its default ORM provider to simplify persistence logic. This article explores persistence from JDBC to Hibernate, dives deep into Hibernate’s features—including relationships—and ties it all together with Spring Data JPA, covering every essential concept with examples and interview-ready insights.
Persistence Logic: The Foundation
Persistence logic refers to code handling CRUD (Create, Read, Update, Delete) operations between an application and a database. In Java, we have:
- Technology: JDBC (Java Database Connectivity)
- Frameworks: ORM tools like Hibernate, JPA, Spring ORM, and Spring Data JPA (the current “hotcake”).
JDBC: The Baseline and Its Limitations
JDBC is foundational but has drawbacks:
1. Non-Portable: SQL queries are database-specific, clashing with Java’s “Write Once, Run Anywhere” (WORA).
2. Boilerplate Code: Repetitive steps like connection setup, statement creation, and resource closure bloat your codebase.
- Example: Connection con = DriverManager.getConnection(url, user, pass); PreparedStatement ps = con.prepareStatement(query);
3. SQLException: A single checked exception forces try-catch or throws clauses.
4. No Detailed Exceptions: Lacks a hierarchy for specific issues.
5. Non-Serializable ResultSet: Can’t send over networks; requires manual POJO mapping.
6. Resource Management: Closing connections risks NullPointerException without careful checks (e.g., if (con != null) con.close(); in a finally block).
7. No Object Support: Queries expect values, not objects, limiting OOP.
8. Weak Transaction Management: Only local transactions, no global support.
9. Positional Parameters Only: insert into student values(?,?,?)—no named parameters like :name.
10. SQL Expertise Required: Developers must master SQL syntax.
11. No Versioning/Timestamping: No built-in tracking of record changes.
Enter ORM and Hibernate
Object-Relational Mapping (ORM) bridges Java objects and database tables, solving JDBC’s woes. Hibernate, built on the Java Persistence API (JPA), maps classes to tables via annotations or XML.
Spring Data JPA: Hibernate Under the Hood
Spring Data JPA abstracts Hibernate further, reducing boilerplate with repository interfaces. It leverages Hibernate’s features internally while adding declarative query methods.
Example Repository:
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
Employee findByName(String name); // Auto-generated query
}
Usage:
@Autowired
EmployeeRepository repo;
Employee emp = new Employee("Alice");
repo.save(emp); // Hibernate handles persistence
Hibernate Core Components
1. Model Class (Entity):
Rules: Package, public, @Entity, @Id, private fields, no-arg constructor, getters/setters, optional @Version or @Temporal.
Example:
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "ename")
private String name;
@Version
private int version; // Tracks updates
public Employee() {}
public Employee(String name) { this.name = name; }
}
2. Mapping:
Annotations: @Table, @Column, @Id.
XML:
<hibernate-mapping>
<class name="com.example.Employee" table="employees">
<id name="id" column="eid"/>
<property name="name" column="ename"/>
</class>
</hibernate-mapping>
3. Configuration File (`hibernate.cfg.xml`):
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/testdb</property>
<property name="connection.username">root</property>
<property name="connection.password">password</property>
<!-- Dialect: DB-specific SQL generation -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<mapping class="com.example.Employee"/>
</session-factory>
</hibernate-configuration>
Dialect: Translates Hibernate operations to DB-specific SQL (e.g., MySQLDialect, OracleDialect).
Programmatic Alternative:
Configuration cfg = new Configuration()
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect")
.addAnnotatedClass(Employee.class);
4. Test Class Flow:
- Create SessionFactory → Open Session → Begin Transaction (if non-select) → Perform operation → Commit/Rollback → Close.
CRUD Operations: Methods & Rules
1. Create:
- save(): Returns ID, supports generators.
- persist(): Void, JPA-compliant, no generator support.
- Example:
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = new Employee("Bob");
session.save(emp); // SQL: insert into employees (ename) values ('Bob')
tx.commit();
2. Read:
- get(): Eager loading, returns null if not found.
- load(): Lazy loading, throws ObjectNotFoundException.
- Example:
Employee emp = session.get(Employee.class, 1); // Immediate DB hit
Employee proxy = session.load(Employee.class, 1); // Proxy until name accessed
3. Update:
- update(): Direct update, assumes record exists.
- saveOrUpdate(): Insert if new, update if exists.
- merge(): Updates loaded object.
- Example:
Employee emp = session.get(Employee.class, 1);
emp.setName("Bob Updated");
session.merge(emp); // SQL: update employees set ename='Bob Updated' where eid=1
4. Delete:
- delete(): Removes by ID.
- Example:
Employee emp = session.get(Employee.class, 1);
session.delete(emp); // SQL: delete from employees where eid=1
Relationships in Hibernate
1. @Embeddable:
- Embeds a class as a composite value within an entity.
- Example:
@Embeddable
public class Address {
private String city;
private String country;
// Getters, setters
}
@Entity
public class Employee {
@Id
private Integer id;
private String name;
@Embedded
private Address address;
// Constructors, getters, setters
}
2. OneToOne:
- One entity linked uniquely to another.
- Example:
@Entity
public class Employee {
@Id
private Integer id;
private String name;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "passport_id")
private Passport passport;
}
@Entity
public class Passport {
@Id
private Integer id;
private String number;
@OneToOne(mappedBy = "passport")
private Employee employee;
}
3. OneToMany & ManyToOne:
- One entity has many of another; the reverse is ManyToOne.
- Example:
@Entity
public class Department {
@Id
private Integer id;
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<Employee> employees;
}
@Entity
public class Employee {
@Id
private Integer id;
private String name;
@ManyToOne
@JoinColumn(name = "dept_id")
private Department department;
}
4. ManyToMany:
- Multiple entities linked to multiple others via a join table.
- Example:
@Entity
public class Student {
@Id
private Integer id;
private String name;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
private List<Course> courses;
}
@Entity
public class Course {
@Id
private Integer id;
private String title;
@ManyToMany(mappedBy = "courses")
private List<Student> students;
}
Querying in Hibernate
1. HQL (Hibernate Query Language):
- Object-oriented, works with entities, not tables.
- Example:
Query<Employee> query = session.createQuery("from Employee e where e.name = :name", Employee.class);
query.setParameter("name", "Bob");
List<Employee> employees = query.list(); // SQL: select * from employees where ename='Bob'
2. Native SQL Queries:
- Use raw SQL when HQL isn’t enough.
- Example:
Query<Employee> query = session.createNativeQuery("select * from employees where ename = :name", Employee.class);
query.setParameter("name", "Bob");
List<Employee> employees = query.getResultList();
3. Stored Procedures:
- Execute database stored procedures.
- Example (Assuming a procedure getEmployeeById):
Query<Employee> query = session.createStoredProcedureQuery("getEmployeeById", Employee.class)
.registerStoredProcedureParameter("empId", Integer.class, ParameterMode.IN)
.setParameter("empId", 1);
Employee emp = query.getSingleResult();
Advanced Features
1. Date & Time:
- JDBC: Manual conversion of java.sql.*.
- Hibernate: Native java.time.* support (e.g., LocalDate, LocalDateTime), no conversions needed with @Temporal for older java.util.Date.
- Example:
@Entity
public class Person {
@Id
private int id;
private LocalDate dob;
// Setters: person.setDob(LocalDate.of(1990, 1, 1));
}
2. Caching:
- First-Level Cache (L1): Default, tied to Session. Stores objects until commit().
- Example: session.get(Employee.class, 1) caches in L1.
- Methods: evict(obj), clear().
- Second-Level Cache (L2): Global, tied toSessionFactory, configurable with EHCache.
- Setup:
- ehcache.xml:
<ehcache>
<defaultCache maxElementsInMemory="100" timeToLiveSeconds="30"/>
</ehcache>
- hibernate.cfg.xml:
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
- Entity:
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Employee { ... }
- Example:
Employee emp = session.get(Employee.class, 1); // Cached in L1 & L2
3. Versioning:
- @Version: Tracks modifications with a numeric column.
- Example:
@Version
private int version; // Increments on update
4. Timestamping:
- @CreationTimestamp, @UpdateTimestamp: Logs record creation and last update.
- Example:
@CreationTimestamp
private LocalDateTime created;
@UpdateTimestamp
private LocalDateTime updated;
5. Generators:
- Hibernate: identity, increment, sequence.
- JPA: GenerationType.IDENTITY, AUTO.
- Custom: Implement IdentifierGenerator.
- Example:
public class CustomGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) {
return "FIRST" + String.format("%04d", new Random().nextInt(10000));
}
}
@GenericGenerator(name = "custom", strategy = "com.example.CustomGenerator")
@GeneratedValue(generator = "custom")
private String id;
6. LOBs:
- @Lob: For large objects like byte[] (photos) or char[] (resumes).
- Example:
@Lob
private byte[] photo;
7. Transaction Management:
- Local: session.beginTransaction().
- Global: Requires JTA (not native in JDBC).
Here is my Hibernate project: [Github]