跳转至

深入理解 Java 中的数据访问对象(Data Access Object)

简介

在 Java 开发中,数据访问对象(Data Access Object,简称 DAO)模式是一种用于抽象和封装对数据源(如数据库、文件系统等)访问的设计模式。它将业务逻辑与数据访问逻辑分离,使得代码更易于维护、测试和扩展。通过使用 DAO 模式,开发者可以专注于业务逻辑的实现,而将数据访问的细节隐藏在 DAO 层中。

目录

  1. 基础概念
  2. 使用方法
    • 定义 DAO 接口
    • 实现 DAO 接口
    • 在业务逻辑中使用 DAO
  3. 常见实践
    • 数据库连接管理
    • 异常处理
    • 事务管理
  4. 最佳实践
    • 代码复用与模块化
    • 缓存机制
    • 日志记录
  5. 小结
  6. 参考资料

基础概念

DAO 模式主要包含以下几个角色: - DAO 接口:定义了对特定数据对象的操作方法,如查询、插入、更新和删除等。它是 DAO 实现类和业务逻辑之间的契约。 - DAO 实现类:实现了 DAO 接口中定义的方法,负责实际的数据访问操作,与数据源进行交互。 - 数据对象(Data Object):代表从数据源中获取或要存储到数据源的数据结构,通常是一个 JavaBean。

使用方法

定义 DAO 接口

以操作用户数据为例,定义一个 UserDAO 接口:

public interface UserDAO {
    User findById(int id);
    void save(User user);
    void update(User user);
    void delete(User user);
}

实现 DAO 接口

假设使用 JDBC 操作数据库,实现 UserDAO 接口:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDAOImpl implements UserDAO {

    private Connection connection;

    public UserDAOImpl(Connection connection) {
        this.connection = connection;
    }

    @Override
    public User findById(int id) {
        String sql = "SELECT * FROM users WHERE id =?";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, id);
            try (ResultSet resultSet = statement.executeQuery()) {
                if (resultSet.next()) {
                    User user = new User();
                    user.setId(resultSet.getInt("id"));
                    user.setName(resultSet.getString("name"));
                    user.setEmail(resultSet.getString("email"));
                    return user;
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void save(User user) {
        String sql = "INSERT INTO users (name, email) VALUES (?,?)";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setString(1, user.getName());
            statement.setString(2, user.getEmail());
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void update(User user) {
        String sql = "UPDATE users SET name =?, email =? WHERE id =?";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setString(1, user.getName());
            statement.setString(2, user.getEmail());
            statement.setInt(3, user.getId());
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void delete(User user) {
        String sql = "DELETE FROM users WHERE id =?";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, user.getId());
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

在业务逻辑中使用 DAO

public class UserService {
    private UserDAO userDAO;

    public UserService(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    public User findUserById(int id) {
        return userDAO.findById(id);
    }

    public void saveUser(User user) {
        userDAO.save(user);
    }

    public void updateUser(User user) {
        userDAO.update(user);
    }

    public void deleteUser(User user) {
        userDAO.delete(user);
    }
}

常见实践

数据库连接管理

在 DAO 实现类中,需要管理数据库连接。可以使用数据库连接池(如 HikariCP)来提高性能和资源利用率。

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class DatabaseUtil {
    private static final HikariDataSource dataSource;

    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        config.setUsername("root");
        config.setPassword("password");
        dataSource = new HikariDataSource(config);
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

异常处理

在 DAO 实现类中,需要对数据库操作可能抛出的异常进行处理。可以将异常包装成自定义的业务异常,以便在业务逻辑层进行统一处理。

public class DataAccessException extends RuntimeException {
    public DataAccessException(String message) {
        super(message);
    }

    public DataAccessException(String message, Throwable cause) {
        super(message, cause);
    }
}

事务管理

在涉及多个数据库操作的业务场景中,需要进行事务管理。可以使用 JDBC 的 Connection 对象的 setAutoCommit(false)commit()rollback() 方法来实现。

public void saveAndUpdate(User user1, User user2) {
    Connection connection = null;
    try {
        connection = DatabaseUtil.getConnection();
        connection.setAutoCommit(false);

        userDAO.save(user1);
        userDAO.update(user2);

        connection.commit();
    } catch (SQLException e) {
        if (connection != null) {
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
        throw new DataAccessException("Database operation failed", e);
    } finally {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

最佳实践

代码复用与模块化

将通用的数据访问逻辑封装到基类或工具类中,以便在不同的 DAO 实现类中复用。例如,可以创建一个 BaseDAO 类,封装数据库连接获取、SQL 执行等通用方法。

缓存机制

对于频繁访问的数据,可以使用缓存机制(如 Ehcache、Redis)来提高性能。在 DAO 实现类中,可以在查询数据时先从缓存中获取,如果缓存中不存在再从数据源中查询,并将查询结果存入缓存。

日志记录

在 DAO 实现类中添加日志记录,以便在出现问题时能够快速定位和排查。可以使用日志框架(如 Log4j、SLF4J)记录数据库操作的相关信息,如 SQL 语句、参数、执行时间等。

小结

Data Access Object 模式在 Java 开发中是一种非常重要的设计模式,它将数据访问逻辑与业务逻辑分离,提高了代码的可维护性、可测试性和可扩展性。通过合理运用 DAO 模式,结合常见实践和最佳实践,可以开发出高效、稳定的企业级应用程序。

参考资料