Java Omnibus
OOAD & Architecture / J2EE / Jakarta EE Patterns / Data Access Object / DAO Pattern
J2EE Integration Java Order Management
Integration

Data Access Object / DAO Pattern

DAO encapsulates persistence access so business logic and application services do not need to know SQL details, storage APIs, or row-mapping concerns.

Why This Topic Matters

Why it matters in enterprise Java

Order, customer, and shipment data must be loaded and saved, but the rest of the design should not be shaped by JDBC, SQL text, or storage-specific APIs.

Pattern Essentials

Intent, problem, and solution

Intent

What the pattern is trying to achieve

Abstract and encapsulate all access to the data source behind a focused contract.

Problem

What goes wrong without it

Persistence logic leaks into services, controllers, and domain code, causing tight coupling to storage details.

Solution

How the pattern answers the problem

Create a DAO or repository boundary that isolates retrieval and persistence responsibilities.

Modern Relevance

Why it still matters now

The pattern remains central in repository layers, persistence adapters, and any architecture that keeps storage concerns out of business logic.

Structure

How the collaboration works

  • Application or domain logic depends on a data access contract.
  • DAO implementation owns storage-specific interaction.
  • Mapping stays close to persistence infrastructure.
  • Business rules stay outside the persistence adapter.
Java Example

Java example in the Order Management domain

Java example: JDBC DAO for orders
package org.javaomnibus.ecommerce.persistence;

import java.util.Optional;

public interface OrderDao {
    Optional<OrderRecord> findById(String orderId);
    void save(OrderRecord order);
}

public final class JdbcOrderDao implements OrderDao {
    private final javax.sql.DataSource dataSource;

    public JdbcOrderDao(javax.sql.DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Optional<OrderRecord> findById(String orderId) {
        return Optional.empty();
    }

    @Override
    public void save(OrderRecord order) {
    }
}
Code Walkthrough

Step by step

  1. The interface defines the storage boundary the application cares about.
  2. The implementation owns JDBC or database details.
  3. Business services no longer need to embed persistence logic.
  4. This separation keeps persistence concerns from leaking upward.
Real-World Usage

Where this pattern shows up

  • JDBC-based data access layers
  • Repository-like abstractions over persistence frameworks
  • Explicit persistence adapters in hexagonal architectures
When Not To Use

Cases where another shape is better

  • As an extra pass-through layer that adds no meaningful abstraction over an existing repository boundary
  • Inside code that should stay persistence-ignorant and only depend on higher-level ports
Trade-Offs

Trade-offs and design pressure

  • DAO improves isolation but can feel redundant if it merely mirrors another abstraction one-for-one.
  • The right level of abstraction depends on how much persistence detail the application needs hidden.
Comparison Note

How this differs from nearby patterns

DAO is about storage access. DTO is about boundary data transfer. Session Facade and Application Service orchestrate business workflows around DAOs.

Common Misuse

Mistakes to avoid

  • Putting business logic into the DAO
  • Leaking SQL-specific data structures above the DAO boundary
  • Creating one DAO per table without thinking about the domain boundary
Related Pages

Related concepts


What To Read Next