深入理解 Java 中的数据访问对象(Data Access Object)
简介
在 Java 开发中,数据访问对象(Data Access Object,简称 DAO)模式是一种用于抽象和封装对数据源(如数据库、文件系统等)访问的设计模式。它将业务逻辑与数据访问逻辑分离,使得代码更易于维护、测试和扩展。通过使用 DAO 模式,开发者可以专注于业务逻辑的实现,而将数据访问的细节隐藏在 DAO 层中。
目录
- 基础概念
- 使用方法
- 定义 DAO 接口
- 实现 DAO 接口
- 在业务逻辑中使用 DAO
- 常见实践
- 数据库连接管理
- 异常处理
- 事务管理
- 最佳实践
- 代码复用与模块化
- 缓存机制
- 日志记录
- 小结
- 参考资料
基础概念
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 模式,结合常见实践和最佳实践,可以开发出高效、稳定的企业级应用程序。
参考资料
- 《Effective Java》
- 《Java EE Design Patterns》
- Oracle 官方文档
- HikariCP 官方文档