--- layout: pattern title: Data Access Object folder: dao permalink: /patterns/dao/ categories: Architectural tags: - Data access --- ## 目的 对象为某种类型的数据库或其他持久性机制提供了抽象接口。 ## 解释 真实世界例子 > 有一组客户数据需要持久化到数据库中。 我们需要整个额外的增删改查操作以便操作客户数据。 通俗的说 > DAO是我们通过基本持久性机制提供的接口。 维基百科说 > 在计算机软件中,数据访问对象(DAO)是一种模式,可为某种类型的数据库或其他持久性机制提供抽象接口。 **程序示例** 通过我们的客户示例,下面是基本的`客户`实体。 ```java public class Customer { private int id; private String firstName; private String lastName; public Customer(int id, String firstName, String lastName) { this.id = id; this.firstName = firstName; this.lastName = lastName; } // getters and setters -> ... } ``` 这是`CustomerDao`接口及其两个不同的实现。 Here's the `CustomerDao` interface and two different implementations for it. `InMemoryCustomerDao` 将简单的客户数据映射保存在内存中 而`DBCustomerDao`是真正的RDBMS实现。 ```java public interface CustomerDao { Stream getAll() throws Exception; Optional 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 idToCustomer = new HashMap<>(); // implement the interface using the map ... } public class DbCustomerDao implements CustomerDao { private static final Logger LOGGER = LoggerFactory.getLogger(DbCustomerDao.class); private final DataSource dataSource; public DbCustomerDao(DataSource dataSource) { this.dataSource = dataSource; } // implement the interface using the data source ... ``` 最后,这是我们使用DAO管理客户数据的方式。 ```java final var dataSource = createDataSource(); createSchema(dataSource); final var customerDao = new DbCustomerDao(dataSource); addCustomers(customerDao); log.info(ALL_CUSTOMERS); try (var customerStream = customerDao.getAll()) { customerStream.forEach((customer) -> log.info(customer.toString())); } log.info("customerDao.getCustomerById(2): " + customerDao.getById(2)); final var customer = new Customer(4, "Dan", "Danson"); customerDao.add(customer); log.info(ALL_CUSTOMERS + customerDao.getAll()); customer.setFirstName("Daniel"); customer.setLastName("Danielson"); customerDao.update(customer); log.info(ALL_CUSTOMERS); try (var customerStream = customerDao.getAll()) { customerStream.forEach((cust) -> log.info(cust.toString())); } customerDao.delete(customer); log.info(ALL_CUSTOMERS + customerDao.getAll()); deleteSchema(dataSource); ``` 程序输出: ```java customerDao.getAllCustomers(): Customer{id=1, firstName='Adam', lastName='Adamson'} Customer{id=2, firstName='Bob', lastName='Bobson'} Customer{id=3, firstName='Carl', lastName='Carlson'} customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}] customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@7cef4e59 customerDao.getAllCustomers(): Customer{id=1, firstName='Adam', lastName='Adamson'} Customer{id=2, firstName='Bob', lastName='Bobson'} Customer{id=3, firstName='Carl', lastName='Carlson'} Customer{id=4, firstName='Daniel', lastName='Danielson'} customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@2db0f6b2 customerDao.getAllCustomers(): Customer{id=1, firstName='Adam', lastName='Adamson'} Customer{id=2, firstName='Bob', lastName='Bobson'} Customer{id=3, firstName='Carl', lastName='Carlson'} customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}] customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@12c8a2c0 customerDao.getAllCustomers(): Customer{id=1, firstName='Adam', lastName='Adamson'} Customer{id=2, firstName='Bob', lastName='Bobson'} Customer{id=3, firstName='Carl', lastName='Carlson'} Customer{id=4, firstName='Daniel', lastName='Danielson'} customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@6ec8211c ``` ## 类图 ![alt text](../../dao/etc/dao.png "Data Access Object") ## 适用性 在以下情况下,请使用数据访问对象:: * 当您要巩固如何访问数据层时。 * 当您要避免编写多个数据检索/持久层时。 ## 鸣谢 * [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)