AlgoDaily

Database Integration with Hibernate

Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.

Persistence Bridge

Why this lesson matters

In Building a RESTful Web Service, our API could accept requests and return responses, but the data was still temporary. Real projects need data to survive restarts, scale beyond one JVM, and support queries.

Today we add the missing production layer: Hibernate, a Java ORM that maps objects to database rows.

By the end, you should be able to explain and design this path:

HTTP Request → Controller → Service → Repository → Hibernate → Database

We will stay focused on database integration. Later, the real-time chat project can reuse this persistence foundation without re-learning it from scratch.

Persistence Bridge

Hibernate Mental Model

ORM in one sentence

Hibernate lets you work with Java objects, while it handles most of the SQL needed to store those objects in relational tables.

Instead of thinking only in rows:

users table
+----+----------+----------------+
| id | username | email          |
+----+----------+----------------+

You also think in objects:

User{id=1, username="maya", email="maya@app.com"}

Hibernate sits between both worlds:

Java Entity  ←→  Hibernate Session/EntityManager  ←→  SQL Database
Key terms
  • Entity: a Java class mapped to a database table
  • Primary key: the identity of a row/object
  • Session / EntityManager: unit that tracks and persists entities
  • Transaction: boundary where changes succeed or fail together
  • Repository / DAO: your project-facing persistence abstraction

This matches the layered structure you used in the REST lesson: controllers should not directly talk to Hibernate.

Hibernate Mental Model

Entity Mapping Shape

What entity mapping means

In a real Hibernate project, you usually annotate classes with JPA annotations such as @Entity, @Table, @Id, and @Column.

The idea is simple: describe how a Java class corresponds to a table.

The standalone example below uses tiny custom annotations so you can run it without installing Hibernate. It demonstrates the same mapping concept: class metadata becomes table metadata.

```java
import java.lang.annotation.*;
import java.lang.reflect.*;

public class EntityMappingDemo {
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @interface EntityTable {
        String name();
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface Column {
        String name();
        boolean id() default false;
    }

    @EntityTable(name = "users")
    static class User {
        @Column(name = "id", id = true)
        private Long id;

        @Column(name = "username")
        private String username;

        @Column(name = "email")
        private String email;
    }

    public static void main(String[] args) {
        Class<User> entityClass = User.class;
        EntityTable table = entityClass.getAnnotation(EntityTable.class);

        System.out.println("Entity: " + entityClass.getSimpleName());
        System.out.println("Table : " + table.name());
        System.out.println("Columns:");

        for (Field field : entityClass.getDeclaredFields()) {
            Column column = field.getAnnotation(Column.class);
            if (column != null) {
                String marker = column.id() ? " primary key" : "";
                System.out.println("- " + field.getName() + " -> " + column.name() + marker);
            }
        }
    }
}
```

Configuration Pieces

What Hibernate needs to connect

A Hibernate integration is not just annotations. It also needs runtime configuration.

Typical project pieces:

src/main/java
  com.example.user
    User.java
    UserRepository.java
    UserService.java

src/main/resources
  hibernate.cfg.xml
  or application.properties
Core settings
Setting Purpose JDBC URL Which database to connect to Username/password Database credentials Dialect SQL flavor, such as PostgreSQL or MySQL Entity packages/classes Which models Hibernate should map Schema strategy Whether Hibernate validates, updates, or creates tables
Common schema strategy values
  • validate: check mappings against existing schema
  • update: attempt to update schema automatically
  • create: recreate schema, usually unsafe for real data
  • none: do nothing automatically

For serious projects, prefer controlled migrations later. For learning projects, update is often convenient.

Configuration Pieces

Best Layer Boundary

In the layered architecture from the REST lesson, which layer should usually contain direct Hibernate calls such as persist, find, or query execution?

Repository Boundary

Keep Hibernate behind an interface

A repository gives the rest of your project a stable API.

Your service layer should ask for operations like:

save(user)
findById(id)
findByEmail(email)

It should not care whether the implementation uses Hibernate, JDBC, an in-memory map, or a test double.

The example below is executable and uses memory instead of Hibernate, but the boundary is the important part. In a real project, the implementation would call Hibernate inside save and findById.

```java
import java.util.*;

public class RepositoryBoundaryDemo {
    static class User {
        private final long id;
        private final String email;

        User(long id, String email) {
            this.id = id;
            this.email = email;
        }

        long getId() { return id; }
        String getEmail() { return email; }

        @Override
        public String toString() {
            return "User{id=" + id + ", email='" + email + "'}";
        }
    }

    interface UserRepository {
        void save(User user);
        Optional<User> findById(long id);
    }

    static class InMemoryUserRepository implements UserRepository {
        private final Map<Long, User> users = new HashMap<>();

        public void save(User user) {
            users.put(user.getId(), user);
            System.out.println("Saved user " + user.getId());
        }

        public Optional<User> findById(long id) {
            return Optional.ofNullable(users.get(id));
        }
    }

    public static void main(String[] args) {
        UserRepository repository = new InMemoryUserRepository();
        repository.save(new User(101, "dev@example.com"));

        User found = repository.findById(101).orElseThrow();
        System.out.println("Found: " + found);
    }
}
```

Transaction Boundary

Transactions protect consistency

A transaction groups database work into one unit:

begin → change data → commit

If something goes wrong:

begin → change data → error → rollback

This matters because real operations often touch multiple rows or tables. For example, creating an order might insert an order row, insert order items, and reduce inventory. You do not want only half of that work saved.

In Hibernate, the transaction boundary usually lives in the service layer or application framework configuration, while the repository performs persistence operations inside that boundary.

The demo below models commit and rollback behavior with plain Java so the lifecycle is visible.

```java
public class TransactionBoundaryDemo {
    interface Work {
        void run();
    }

    static class TransactionRunner {
        void inTransaction(String name, Work work) {
            System.out.println("BEGIN " + name);
            try {
                work.run();
                System.out.println("COMMIT " + name);
            } catch (RuntimeException ex) {
                System.out.println("ROLLBACK " + name + " because: " + ex.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        TransactionRunner tx = new TransactionRunner();

        tx.inTransaction("create-user", () -> {
            System.out.println("Insert user row");
            System.out.println("Insert profile row");
        });

        tx.inTransaction("create-order", () -> {
            System.out.println("Insert order row");
            throw new RuntimeException("inventory unavailable");
        });
    }
}
```

Session Truth

True or false: a Hibernate Session or JPA EntityManager should usually be treated as a short-lived unit of work rather than a global singleton shared across the entire application.

Mapping Blank

Fill in the blank: In Hibernate, a Java class mapped to a database table is commonly called an ______.

Integration Checklist

A practical Hibernate integration checklist

When adding Hibernate to a Java project, move in this order:

  1. Choose the database

    • Example: PostgreSQL, MySQL, H2 for local demos
  2. Add dependencies

    • Hibernate ORM
    • JDBC driver for your database
    • JPA API if your stack requires it
  3. Create entities

    • Use clear IDs
    • Keep constructors simple
    • Avoid putting controller or HTTP logic in entities
  4. Configure Hibernate

    • JDBC URL
    • credentials
    • dialect
    • entity scanning
    • schema strategy
  5. Build repositories

    • Hide Hibernate calls behind interfaces
    • Return domain objects or Optional<T> for lookups
  6. Use transactions intentionally

    • Commit complete work
    • Roll back failed work
  7. Verify with a smoke run

    • Insert one row
    • Read it back
    • Log the generated SQL if useful

Design takeaway

Hibernate is not just a library you attach to a project. It changes the architecture by giving your domain model persistence, while your repository boundary keeps that persistence from leaking everywhere.

Next, this foundation becomes useful when a real-time project needs users, messages, and durable state.

Integration Checklist

Open interactive view →