【Java】Data Access Objects (DAO) Pattern

Posted by 西维蜀黍 on 2023-08-23, Last Modified on 2023-08-23

Data Access Objects (DAO)

Code that depends on specific features of data resources ties together business logic with data access logic. This makes it difficult to replace or modify an application’s data resources.

The Data Access Object (or DAO) pattern:

  • separates a data resource’s client interface from its data access mechanisms
  • adapts a specific data resource’s access API to a generic client interface

The DAO pattern allows data access mechanisms to change independently of the code that uses the data.

Advantages of DAO pattern

There are many advantages for using DAO pattern. Let’s state some of them here:

  1. While changing a persistence mechanism, service layer doesn’t even have to know where the data comes from. For example, if you’re thinking of shifting from using MySQL to MongoDB, all changes are needed to be done in the DAO layer only.
  2. DAO pattern emphasis on the low coupling between different components of an application. So, the View layer have no dependency on DAO layer and only Service layer depends on it, even that with the interfaces and not from concrete implementation.
  3. As the persistence logic is completely separate, it is much easier to write Unit tests for individual components. For example, if you’re using JUnit and Mockito for testing frameworks, it will be easy to mock the individual components of your application.
  4. As we work with interfaces in DAO pattern, it also emphasizes the style of “work with interfaces instead of implementation” which is an excellent OOPs style of programming.

Demo

The Domain Class

As our application will work with users, we need to define just one class for implementing its domain model:

public class User {
    
    private String name;
    private String email;
    
    // constructors / standard setters / getters
}

The User class is just a plain container for user data, so it doesn’t implement any other behavior worth stressing.

Of course, the important design choice here is how to keep the application using this class isolated from any persistence mechanism that could be implemented.

And that’s exactly the issue that the DAO pattern attempts to address.

The DAO API

Let’s define a basic DAO layer so we can see how it can keep the domain model completely decoupled from the persistence layer.

Here’s the DAO API:

public interface Dao<T> {
    
    Optional<T> get(long id);
    
    List<T> getAll();
    
    void save(T t);
    
    void update(T t, String[] params);
    
    void delete(T t);
}

From a bird’s-eye view, it’s clear that the Dao interface defines an abstract API that performs CRUD operations on objects of type T.

Due to the high level of abstraction that the interface provides, it’s easy to create a concrete, fine-grained implementation that works with User objects.

The UserDao Class

Let’s define a user-specific implementation of the Dao interface:

public class UserDao implements Dao<User> {
    
    private List<User> users = new ArrayList<>();
    
    public UserDao() {
        users.add(new User("John", "john@domain.com"));
        users.add(new User("Susan", "susan@domain.com"));
    }
    
    @Override
    public Optional<User> get(long id) {
        return Optional.ofNullable(users.get((int) id));
    }
    
    @Override
    public List<User> getAll() {
        return users;
    }
    
    @Override
    public void save(User user) {
        users.add(user);
    }
    
    @Override
    public void update(User user, String[] params) {
        user.setName(Objects.requireNonNull(
          params[0], "Name cannot be null"));
        user.setEmail(Objects.requireNonNull(
          params[1], "Email cannot be null"));
        
        users.add(user);
    }
    
    @Override
    public void delete(User user) {
        users.remove(user);
    }
}

The UserDao class implements all the functionality required for fetching, updating and removing User objects.

Reference