文章目录
【Java设计模式】数据访问对象模式:简化数据库交互
一、概述
数据访问对象(DAO)设计模式旨在将应用程序的业务逻辑与持久层(通常是数据库或任何其他存储机制)分离。通过使用DAO,应用程序可以访问和操作数据,而无需依赖于特定的数据库实现细节。
二、详细解释及实际示例
- 实际示例:
- 想象一个图书馆系统,其中主应用程序管理图书借阅、用户账户和库存。在这种情况下,数据访问对象(DAO)模式将用于将数据库操作(如获取图书详细信息、更新用户记录和检查库存)与管理借阅和账户的业务逻辑分开。例如,会有一个
BookDAO
类负责与图书相关的所有数据库交互,如通过ISBN检索图书或更新其可用性状态。这种抽象允许图书馆系统的主应用程序代码专注于业务规则和工作流程,而BookDAO
处理复杂的SQL查询和数据管理。这种分离使系统更容易维护和测试,因为数据源或业务逻辑的更改可以独立管理。
- 想象一个图书馆系统,其中主应用程序管理图书借阅、用户账户和库存。在这种情况下,数据访问对象(DAO)模式将用于将数据库操作(如获取图书详细信息、更新用户记录和检查库存)与管理借阅和账户的业务逻辑分开。例如,会有一个
- 通俗解释:
- 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<>();
// 使用映射实现接口
}
@Slf4j
@RequiredArgsConstructor
public class DbCustomerDao implements CustomerDao {
private final DataSource dataSource;
// 使用数据源实现接口
}
最后,这是我们如何使用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中的实际应用
- 需要数据库交互的企业应用程序。
- 需要数据访问适应多种存储类型(关系数据库、XML文件、平面文件等)的应用程序。
- 提供通用数据访问功能的框架。
六、数据访问对象模式的优点和权衡
优点:
- 解耦:将数据访问逻辑与业务逻辑分离,增强了模块化和清晰度。
- 可重用性:DAO可以在应用程序的不同部分甚至不同项目中重用。
- 可测试性:通过允许业务逻辑与数据访问逻辑分开测试,简化了测试。
- 灵活性:使切换底层存储机制更容易,对应用程序代码的影响最小。
权衡:
- 层复杂性:为应用程序引入了额外的层,这可能会增加复杂性和开发时间。
- 开销:对于简单的应用程序,DAO模式可能会引入不必要的开销。
- 学习曲线:开发人员可能需要时间来理解和有效地实现该模式,特别是在复杂的项目中。