| 
									
										
										
										
											2021-02-08 23:37:48 +08:00
										 |  |  |  | --- | 
					
						
							|  |  |  |  | layout: pattern | 
					
						
							|  |  |  |  | title: Data Access Object | 
					
						
							|  |  |  |  | folder: dao | 
					
						
							| 
									
										
										
										
											2021-08-01 22:55:54 +08:00
										 |  |  |  | permalink: /patterns/dao/ | 
					
						
							| 
									
										
										
										
											2021-02-08 23:37:48 +08:00
										 |  |  |  | categories: Architectural | 
					
						
							| 
									
										
										
										
											2021-05-19 10:49:05 -06:00
										 |  |  |  | language: zh | 
					
						
							| 
									
										
										
										
											2021-02-08 23:37:48 +08:00
										 |  |  |  | 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<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 | 
					
						
							|  |  |  |  |   ... | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 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 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ## 类图
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-01 22:55:54 +08:00
										 |  |  |  |  | 
					
						
							| 
									
										
										
										
											2021-02-08 23:37:48 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ## 适用性
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 在以下情况下,请使用数据访问对象:: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | * 当您要巩固如何访问数据层时。 | 
					
						
							|  |  |  |  | * 当您要避免编写多个数据检索/持久层时。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ## 鸣谢
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | * [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) |