The DAO (Data Access Object) pattern is a structural pattern used to separate the persistence logic from the business logic. It provides an abstraction layer for data access and ensures that changes in the underlying database do not affect the business logic.
Steps to Implement DAO Pattern in Java
1. Define the Entity Class
This class represents the table structure.
import java.io.Serializable;
public class User implements Serializable {
private Long id;
private String userName;
private String firstName;
private String lastName;
public User() {}
public User(Long id, String userName, String firstName, String lastName) {
this.id = id;
this.userName = userName;
this.firstName = firstName;
this.lastName = lastName;
}
// ... setters and getters
}
2. Create a generic DAO Interface
This interface defines the database operations.
public interface Operations<T> {
T findOne(final Long id);
List<T> findAll();
void create(final T entity);
T update(final T entity);
void delete(final T entity);
void deleteById(final long entityId);
}
2.1 Create User DAO Interface
Into this interface will be declared user entities' special operation
import java.util.List;
public interface UserDao extends Operations<User> {
List<User> selectAllPageable(int fromRow, int rows);
int[] batchInsert(List<User> userList);
int[] batchUpdate(List<User> userList);
}
3. Abstract implementation of DAO Interface
This step can be optional because from Java 8, into interfaces we can have default implemented methods, but I prefer to have an abstract class here
public abstract class AbstractDAO<T extends Serializable> implements Operations<T> {
private final DataSource dataSource;
public AbstractDAO(DataSource dataSource) {
this.dataSource = dataSource;
}
protected Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
@Override
public T findOne(Long id) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public List<T> findAll() {
throw new UnsupportedOperationException("Not supported yet.");
}
public void create(final T entity) {
throw new UnsupportedOperationException("Not supported yet.");
}
public T update(final T entity) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void delete(final T entity) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void deleteById(final long entityId) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
4 Implement DAO Interface (JDBC Example)
This class contains actual JDBC logic to interact with the database.
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class UserDaoImpl extends AbstractDAO<User> implements UserDao {
private static final Logger logger = LogManager.getLogger(UserDaoImpl.class);
public UserDaoImpl(DataSource dataSource) {
super(dataSource);
}
@Override
public User findOne(Long id) {
String sql = "select u.* from users u where u.id = " + id;
User user = null;
try (Statement statement = getConnection().createStatement();
ResultSet result = statement.executeQuery(sql)) {
while (result.next()) {
user =
new User(
result.getLong("id"),
result.getString("user_name"),
result.getString("first_name"),
result.getString("last_name"));
}
} catch (SQLException e) {
logger.error(e);
}
return user;
}
@Override
public User findByUsername(String userName) {
return null;
}
@Override
public List<User> selectAllPageable(int fromRow, int rows) {
List<User> users = new ArrayList<>(rows);
String sql = "SELECT * FROM users u LIMIT" + fromRow + "," + rows;
try (Statement statement = getConnection().createStatement();
ResultSet result = statement.executeQuery(sql)) {
while (result.next()) {
users.add(
new User(
result.getLong("id"),
result.getString("user_name"),
result.getString("first_name"),
result.getString("last_name")));
}
} catch (SQLException e) {
logger.error(e);
}
return users;
}
}
4. Create a Test Class
This will test the DAO implementation.
public class UserDaoImplTest {
private UserDaoImpl userDao;
@Before
public void setUp() throws Exception {
// About this DataSourcesConfiguration we will have other article
DataSourcesConfiguration dataSourcesConfiguration = new DataSourcesConfiguration();
userDao = new UserDaoImpl(dataSourcesConfiguration.getDataSources());
}
@Test
public void findOneTest() {
User user = userDao.findOne(1l);
assertNotNull(user);
assert user.getId().equals(1l);
}
@Test
public void selectAllPageableTest() {
List<User> userList = userDao.selectAllPageable(0, 6);
assert !userList.isEmpty();
assert userList.size() == 6;
List<User> userList2 = userDao.selectAllPageable(0, 8);
assert !userList2.isEmpty();
assert userList2.size() == 8;
}
@Test
public void create() {
User user = new User();
user.setUserName("This email address is being protected from spambots. You need JavaScript enabled to view it. ");
userDao.create(user);
User userCreated = userDao.findByUsername("This email address is being protected from spambots. You need JavaScript enabled to view it. ");
assertEquals(userCreated.getUserName(),"This email address is being protected from spambots. You need JavaScript enabled to view it. " );
}
@Test
public void update() {
User user = userDao.findByUsername("This email address is being protected from spambots. You need JavaScript enabled to view it. ");
user.setUserName("This email address is being protected from spambots. You need JavaScript enabled to view it. ");
User updated = userDao.update(user);
assertEquals(updated.getUserName(), "This email address is being protected from spambots. You need JavaScript enabled to view it. ");
}
@Test
public void delete() {
userDao.deleteByUserName("This email address is being protected from spambots. You need JavaScript enabled to view it. ");
assertNull(userDao.findByUsername("This email address is being protected from spambots. You need JavaScript enabled to view it. "));
}
}
Advantages of Using DAO Pattern
- Separation of Concerns – Keeps data access logic separate from business logic.
- Easier Maintenance – Changes in the database schema require updates only in the DAO layer.
- Scalability – Easier to migrate to a different database (e.g., switching from MySQL to PostgreSQL).
- Reusability – DAO classes can be reused across multiple projects or applications.
Enhancements
- Use Spring JDBC or JPA (Hibernate) to reduce boilerplate code.
- Implement DAO Factory Pattern to manage multiple DAO objects.
- Use Connection Pooling (e.g., HikariCP) for better performance.
- Implement Logging (SLF4J, Log4j) for error tracking.