Java 中的数据访问对象模式:简化数据库交互
约 5 分钟
也称为
- 数据访问层
- DAO
数据访问对象设计模式的意图
数据访问对象 (DAO) 设计模式旨在将应用程序的业务逻辑与持久层(通常是数据库或其他存储机制)分离。通过使用 DAO,应用程序可以访问和操作数据,而无需依赖特定数据库实现的细节。
数据访问对象模式的详细说明,包括现实世界中的示例
现实世界中的示例
想象一个图书馆系统,其中主应用程序管理图书借阅、用户帐户和库存。在这种情况下,数据访问对象 (DAO) 模式将用于将数据库操作(例如获取图书详细信息、更新用户记录和检查库存)与管理借阅和帐户的业务逻辑分离。例如,将有一个
BookDAO
类负责与图书相关的所有数据库交互,例如通过其 ISBN 检索图书或更新其可用性状态。这种抽象允许图书馆系统的应用程序代码专注于业务规则和工作流,而BookDAO
处理复杂的 SQL 查询和数据管理。这种分离使系统更容易维护和测试,因为对数据源或业务逻辑的更改可以独立管理。
通俗地说
DAO 是我们在基本持久化机制之上提供的接口。
维基百科说
在计算机软件中,数据访问对象 (DAO) 是一种模式,它为某种类型的数据库或其他持久化机制提供抽象接口。
Java 中 DAO 模式编程示例
有一组客户需要持久化到数据库。此外,我们需要完整的 CRUD(创建/读取/更新/删除)操作集,这样我们就可以轻松地对客户进行操作。
遍历我们的客户示例,这是基本的 Customer
实体。
@Setter
@Getter
@ToString
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@AllArgsConstructor
public class Customer {
@EqualsAndHashCode.Include
private int id;
private String firstName;
private String lastName;
}
以下是 CustomerDao
接口及其两种不同的实现。InMemoryCustomerDao
在内存中保留客户的简单映射,而 DBCustomerDao
是真正的 RDBMS 实现。
public interface CustomerDao {
Stream<Customer> getAll() throws Exception;
Optional<Customer> getById(int id) throws Exception;
boolean add(Customer customer) throws Exception;
boolean update(Customer customer) throws Exception;
boolean delete(Customer customer) throws Exception;
}
public class InMemoryCustomerDao implements CustomerDao {
private final Map<Integer, Customer> idToCustomer = new HashMap<>();
// implement the interface using the map
}
@Slf4j
@RequiredArgsConstructor
public class DbCustomerDao implements CustomerDao {
private final DataSource dataSource;
// implement the interface using the data source
}
最后,以下是我们如何使用 DAO 来管理客户。
@Slf4j
public class App {
private static final String DB_URL = "jdbc:h2:mem:dao;DB_CLOSE_DELAY=-1";
private static final String ALL_CUSTOMERS = "customerDao.getAllCustomers(): ";
public static void main(final String[] args) throws Exception {
final var inMemoryDao = new InMemoryCustomerDao();
performOperationsUsing(inMemoryDao);
final var dataSource = createDataSource();
createSchema(dataSource);
final var dbDao = new DbCustomerDao(dataSource);
performOperationsUsing(dbDao);
deleteSchema(dataSource);
}
private static void deleteSchema(DataSource dataSource) throws SQLException {
try (var connection = dataSource.getConnection();
var statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
}
}
private static void createSchema(DataSource dataSource) throws SQLException {
try (var connection = dataSource.getConnection();
var statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
}
}
private static DataSource createDataSource() {
var dataSource = new JdbcDataSource();
dataSource.setURL(DB_URL);
return dataSource;
}
private static void performOperationsUsing(final CustomerDao customerDao) throws Exception {
addCustomers(customerDao);
LOGGER.info(ALL_CUSTOMERS);
try (var customerStream = customerDao.getAll()) {
customerStream.forEach(customer -> LOGGER.info(customer.toString()));
}
LOGGER.info("customerDao.getCustomerById(2): " + customerDao.getById(2));
final var customer = new Customer(4, "Dan", "Danson");
customerDao.add(customer);
LOGGER.info(ALL_CUSTOMERS + customerDao.getAll());
customer.setFirstName("Daniel");
customer.setLastName("Danielson");
customerDao.update(customer);
LOGGER.info(ALL_CUSTOMERS);
try (var customerStream = customerDao.getAll()) {
customerStream.forEach(cust -> LOGGER.info(cust.toString()));
}
customerDao.delete(customer);
LOGGER.info(ALL_CUSTOMERS + customerDao.getAll());
}
private static void addCustomers(CustomerDao customerDao) throws Exception {
for (var customer : generateSampleCustomers()) {
customerDao.add(customer);
}
}
public static List<Customer> generateSampleCustomers() {
final var customer1 = new Customer(1, "Adam", "Adamson");
final var customer2 = new Customer(2, "Bob", "Bobson");
final var customer3 = new Customer(3, "Carl", "Carlson");
return List.of(customer1, customer2, customer3);
}
}
程序输出
10:02:09.788 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers():
10:02:09.793 [main] INFO com.iluwatar.dao.App -- Customer(id=1, firstName=Adam, lastName=Adamson)
10:02:09.793 [main] INFO com.iluwatar.dao.App -- Customer(id=2, firstName=Bob, lastName=Bobson)
10:02:09.793 [main] INFO com.iluwatar.dao.App -- Customer(id=3, firstName=Carl, lastName=Carlson)
10:02:09.794 [main] INFO com.iluwatar.dao.App -- customerDao.getCustomerById(2): Optional[Customer(id=2, firstName=Bob, lastName=Bobson)]
10:02:09.794 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@4c3e4790
10:02:09.794 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers():
10:02:09.795 [main] INFO com.iluwatar.dao.App -- Customer(id=1, firstName=Adam, lastName=Adamson)
10:02:09.795 [main] INFO com.iluwatar.dao.App -- Customer(id=2, firstName=Bob, lastName=Bobson)
10:02:09.795 [main] INFO com.iluwatar.dao.App -- Customer(id=3, firstName=Carl, lastName=Carlson)
10:02:09.795 [main] INFO com.iluwatar.dao.App -- Customer(id=4, firstName=Daniel, lastName=Danielson)
10:02:09.795 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@5679c6c6
10:02:09.894 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers():
10:02:09.895 [main] INFO com.iluwatar.dao.App -- Customer(id=1, firstName=Adam, lastName=Adamson)
10:02:09.895 [main] INFO com.iluwatar.dao.App -- Customer(id=2, firstName=Bob, lastName=Bobson)
10:02:09.895 [main] INFO com.iluwatar.dao.App -- Customer(id=3, firstName=Carl, lastName=Carlson)
10:02:09.895 [main] INFO com.iluwatar.dao.App -- customerDao.getCustomerById(2): Optional[Customer(id=2, firstName=Bob, lastName=Bobson)]
10:02:09.896 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@23282c25
10:02:09.897 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers():
10:02:09.897 [main] INFO com.iluwatar.dao.App -- Customer(id=1, firstName=Adam, lastName=Adamson)
10:02:09.897 [main] INFO com.iluwatar.dao.App -- Customer(id=2, firstName=Bob, lastName=Bobson)
10:02:09.898 [main] INFO com.iluwatar.dao.App -- Customer(id=3, firstName=Carl, lastName=Carlson)
10:02:09.898 [main] INFO com.iluwatar.dao.App -- Customer(id=4, firstName=Daniel, lastName=Danielson)
10:02:09.898 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@f2f2cc1
数据访问对象模式的详细说明,包括现实世界中的示例

何时在 Java 中使用数据访问对象模式
在以下任何情况下使用数据访问对象
- 需要抽象和封装对数据源的所有访问。
- 应用程序需要支持多种类型的数据库或存储机制,而无需进行重大代码更改。
- 您希望保持数据库访问干净且简单,并与业务逻辑分离。
Java 中的数据访问对象模式教程
Java 中 DAO 模式的实际应用
- 需要数据库交互的企业应用程序。
- 需要数据访问适应多种存储类型(关系数据库、XML 文件、平面文件等)的应用程序。
- 提供通用数据访问功能的框架。
数据访问对象模式的优点和权衡
优点
- 解耦:将数据访问逻辑与业务逻辑分离,提高模块化和清晰度。
- 可重用性:DAO 可以跨应用程序的不同部分甚至在不同的项目中重用。
- 可测试性:通过允许将业务逻辑与数据访问逻辑分开测试,简化了测试。
- 灵活性:使以最小的影响切换底层存储机制变得更容易。
权衡
- 层级复杂度:为应用程序引入了额外的层级,这会增加复杂度和开发时间。
- 开销:对于简单的应用程序,DAO 模式可能会比必要时引入更多的开销。
- 学习曲线:开发人员可能需要时间才能有效地理解和实现该模式,尤其是在复杂的项目中。
相关的 Java 设计模式
- 抽象工厂:有助于抽象 DAO 的创建,尤其是在支持多种数据库或存储机制时。
- 工厂:可用于动态实例化 DAO,为实现选择提供灵活性。
- 服务层:通常与 DAO 模式一起使用,用于定义应用程序的边界及其可用的操作集。
- 策略:可能用于根据上下文在运行时更改数据访问策略。