4.4 Data Access Object
The Data Access Object (DAO) pattern encapsulates data-access logic behind an interface so the business layer no longer faces persistence details like SQL, JDBC, connections, or ORM. Business code just calls userDao.findById(42); whether MySQL, memory, or MongoDB is behind it, the code neither knows nor needs to.
The lab below switches the underlying data source (MySQL / in-memory / Mongo) — notice the business-layer findById() line never changes.
Separate interface from implementation
interface UserDao { // the business layer depends only on this
void save(User u);
Optional<User> findById(int id);
void delete(int id);
}
class JdbcUserDao implements UserDao { /* parameterized SQL */ }
class InMemoryUserDao implements UserDao { /* HashMap for tests */ }This separation brings two key benefits:
- Swappable data source: changing the implementation doesn't touch business code — JDBC in production, in-memory in tests.
- Testability: business logic can be injected with an in-memory DAO and unit-tested without a real database — fast and stable.
The lab below is a DAO CRUD bench that lets you manipulate data through the DAO's standard methods while seeing no SQL at all.
DAO, Repository, and this project
You may have heard of the Repository pattern, which is similar to DAO and often used interchangeably. A common distinction: a DAO is closer to "table/data-source" CRUD, while a Repository is closer to a "collection of domain objects" abstraction. In practice, both share the core: keep persistence details out of business logic.
Worth noting: the Leaflet project hosting this very tutorial embodies the DAO idea in its backend design. Its technical docs explicitly require no ORM, instead "concentrating queries in the repository/service layer, not scattering SQL across routes," with parameterized queries to prevent injection. That is the DAO pattern landing in real engineering — trading one clean data-access abstraction for maintainability, testability, and evolvability.