The DAO Design Pattern: A Practical Approach
In modern software architecture, maintaining a clean separation between business logic and data access is crucial. This is where the DAO (Data Access Object) Design Pattern comes into play. The DAO pattern provides a systematic way to access data from a persistent storage mechanism, such as a database, while ensuring that the core logic remains unaffected by changes in how or where the data is stored.
Key Components of the DAO Pattern
The DAO pattern can be broken down into three main components:
- Model: Represents the data structure that flows between different layers of the application.
- Interface: Provides a contract for the persistence logic, offering flexibility for future changes.
- Concrete Implementation: A specific implementation of the persistence logic defined by the interface.
Applying the DAO Pattern
In this post, we will implement a simplified DAO pattern with an example centered on managing books. We will walk through the following key components:
- The Book model – This model will represent individual books.
- The BookDao interface – Defines the operations that can be performed on books, like retrieving or saving them.
- The BookDaoImpl class – A concrete implementation of the BookDao interface that simulates database interaction.
Let’s dive into the code to see how this works.
Step 1: The Model – Book Class
The model object represents the data that will be transferred between different layers of the application. In our example, this is the Books
class, which has two properties: isbn
(to uniquely identify a book) and bookName
.
package com.example.model;
public class Books {
private int isbn;
private String bookName;
public Books() {
}
public Books(int isbn, String bookName) {
this.isbn = isbn;
this.bookName = bookName;
}
// Getter and Setter methods
public int getIsbn() {
return isbn;
}
public void setIsbn(int isbn) {
this.isbn = isbn;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
}
This class provides a basic structure with an identifier (isbn
) and the book’s name. The getter and setter methods allow for encapsulated access to these fields.
Step 2: The Interface – BookDao
Next, we define the BookDao
interface. This serves as a contract for any implementation class, ensuring that specific data operations (like fetching, saving, or deleting books) are implemented in a uniform way.
package com.example.dao;
import com.example.model.Books;
import java.util.List;
public interface BookDao {
List getAllBooks();
Books getBookByIsbn(int isbn);
void saveBook(Books book);
void deleteBook(Books book);
}
Here, the interface defines four key methods:
getAllBooks
: Retrieve a list of all books.getBookByIsbn
: Find a book by its ISBN.saveBook
: Add a new book to the system.deleteBook
: Remove an existing book.
Step 3: The Implementation – BookDaoImpl
Now, we implement the persistence logic in the BookDaoImpl
class. In this example, a simple in-memory list is used to simulate a database.
package com.example.daoimpl;
import com.example.dao.BookDao;
import com.example.model.Books;
import java.util.ArrayList;
import java.util.List;
public class BookDaoImpl implements BookDao {
// Simulating a database with a list
private List books;
public BookDaoImpl() {
books = new ArrayList<>();
books.add(new Books(1, "Java"));
books.add(new Books(2, "Python"));
books.add(new Books(3, "Android"));
}
@Override
public List getAllBooks() {
return books;
}
@Override
public Books getBookByIsbn(int isbn) {
return books.get(isbn);
}
@Override
public void saveBook(Books book) {
books.add(book);
}
@Override
public void deleteBook(Books book) {
books.remove(book);
}
}
In the above class:
- The
books
list simulates a simple storage mechanism. - The
getAllBooks
method retrieves all the books in this list. - The
getBookByIsbn
method retrieves a specific book by its ISBN. saveBook
adds a new book to the collection.deleteBook
removes an existing book from the list.
Step 4: Using the DAO in the Main Program
Finally, we can test our DAO implementation in a simple main
method. This is where we interact with the DAO and perform various operations like fetching, updating, or adding new books.
package com.example;
import com.example.dao.BookDao;
import com.example.daoimpl.BookDaoImpl;
import com.example.model.Books;
public class AccessBook {
public static void main(String[] args) {
BookDao bookDao = new BookDaoImpl();
// Fetching all books
for (Books book : bookDao.getAllBooks()) {
System.out.println("Book ISBN: " + book.getIsbn());
System.out.println("Book Name: " + book.getBookName());
}
// Updating a book
Books book = bookDao.getBookByIsbn(1);
book.setBookName("Advanced Java");
bookDao.saveBook(book);
System.out.println("Updated Book Name: " + book.getBookName());
}
}
In this example, we:
- Fetch and print all available books.
- Update a book’s name and save it back to the collection.
Benefits of the DAO Pattern
The DAO pattern offers several benefits:
- Decoupling: The data access logic is completely decoupled from the business logic. This means that changes in the database or data source do not affect the business layer.
- Flexibility: The use of interfaces allows the flexibility to switch between different data sources (e.g., from MySQL to MongoDB) without affecting the core application logic.
- Testability: With the persistence logic isolated, it becomes easier to test individual components. Mocking interfaces, for example, can simplify unit testing.
Conclusion
The DAO design pattern is a simple yet powerful tool for structuring applications with clear separation between business logic and data access. By leveraging interfaces and concrete implementations, you ensure both flexibility and maintainability in your projects. Whether your application grows in complexity or switches to a different database, the DAO pattern helps in keeping your codebase robust and adaptable.