mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-25 21:58:29 +00:00
refactor: rename and delete some patterns (#2970)
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: Data Access Object
|
||||
category: Structural
|
||||
language: en
|
||||
tag:
|
||||
- Data access
|
||||
- Layered architecture
|
||||
- Persistence
|
||||
---
|
||||
|
||||
## Also known as
|
||||
|
||||
* Data Access Layer
|
||||
* DAO
|
||||
|
||||
## Intent
|
||||
|
||||
The Data Access Object (DAO) design pattern aims to separate the application's business logic from the persistence layer, typically a database or any other storage mechanism. By using DAOs, the application can access and manipulate data without being dependent on the specific database implementation details.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> There's a set of customers that need to be persisted to database. Additionally, we need the whole set of CRUD (create/read/update/delete) operations, so we can operate on customers easily.
|
||||
|
||||
In plain words
|
||||
|
||||
> DAO is an interface we provide over the base persistence mechanism.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In computer software, a data access object (DAO) is a pattern that provides an abstract interface to some type of database or other persistence mechanism.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Walking through our customers example, here's the basic `Customer` entity.
|
||||
|
||||
```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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the `CustomerDao` interface and two different implementations for it. `InMemoryCustomerDao` keeps a simple map of customers in memory while `DBCustomerDao` is the real RDBMS implementation.
|
||||
|
||||
```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
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class DbCustomerDao implements CustomerDao {
|
||||
|
||||
private final DataSource
|
||||
dataSource;
|
||||
|
||||
public DbCustomerDao(
|
||||
DataSource dataSource) {
|
||||
this.dataSource =
|
||||
dataSource;
|
||||
}
|
||||
|
||||
// implement the interface using the data source
|
||||
}
|
||||
```
|
||||
|
||||
Finally, here's how we use our DAO to manage customers.
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
The program output:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
|
||||
Use the Data Access Object in any of the following situations:
|
||||
|
||||
* There is a need to abstract and encapsulate all access to the data source.
|
||||
* The application needs to support multiple types of databases or storage mechanisms without significant code changes.
|
||||
* You want to keep the database access clean and simple, and separate from business logic.
|
||||
|
||||
## Tutorials
|
||||
|
||||
* [The DAO Pattern in Java](https://www.baeldung.com/java-dao-pattern)
|
||||
* [Data Access Object Pattern](https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm)
|
||||
|
||||
## Known Uses
|
||||
|
||||
* Enterprise applications that require database interaction.
|
||||
* Applications requiring data access to be adaptable to multiple storage types (relational databases, XML files, flat files, etc.).
|
||||
* Frameworks providing generic data access functionalities.
|
||||
|
||||
## Consequences
|
||||
|
||||
Benefits:
|
||||
|
||||
* Decoupling: Separates the data access logic from the business logic, enhancing modularity and clarity.
|
||||
* Reusability: DAOs can be reused across different parts of the application or even in different projects.
|
||||
* Testability: Simplifies testing by allowing business logic to be tested separately from the data access logic.
|
||||
* Flexibility: Makes it easier to switch underlying storage mechanisms with minimal impact on the application code.
|
||||
|
||||
Trade-offs:
|
||||
|
||||
* Layer Complexity: Introduces additional layers to the application, which can increase complexity and development time.
|
||||
* Overhead: For simple applications, the DAO pattern might introduce more overhead than necessary.
|
||||
* Learning Curve: Developers might need time to understand and implement the pattern effectively, especially in complex projects.
|
||||
|
||||
## Related Patterns
|
||||
|
||||
* [Service Layer](https://java-design-patterns.com/patterns/service-layer/): Often used in conjunction with the DAO pattern to define application's boundaries and its set of available operations.
|
||||
* [Factory](https://java-design-patterns.com/patterns/factory/): Can be used to instantiate DAOs dynamically, providing flexibility in the choice of implementation.
|
||||
* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): Helps in abstracting the creation of DAOs, especially when supporting multiple databases or storage mechanisms.
|
||||
* [Strategy](https://java-design-patterns.com/patterns/strategy/): Might be employed to change the data access strategy at runtime, depending on the context.
|
||||
|
||||
## Credits
|
||||
|
||||
* [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)
|
||||
* [Core J2EE Patterns: Best Practices and Design Strategies](https://amzn.to/49u3r91)
|
||||
* [Patterns of Enterprise Application Architecture](https://amzn.to/3U5cxEI)
|
||||
* [Expert One-on-One J2EE Design and Development](https://amzn.to/3vK3pfq)
|
||||
* [Professional Java Development with the Spring Framework](https://amzn.to/49tANF0)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.1.8" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
|
||||
associations="true" dependencies="false" nesting-relationships="true">
|
||||
<class id="1" language="java" name="com.iluwatar.dao.App" project="dao"
|
||||
file="/dao/src/main/java/com/iluwatar/dao/App.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="910" y="191"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="false" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="false" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<interface id="2" language="java" name="com.iluwatar.dao.CustomerDao" project="dao"
|
||||
file="/dao/src/main/java/com/iluwatar/dao/CustomerDao.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="536" y="187"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</interface>
|
||||
<class id="3" language="java" name="com.iluwatar.dao.InMemoryCustomerDao" project="dao"
|
||||
file="/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="289" y="455"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="false" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="4" language="java" name="com.iluwatar.dao.DBCustomerDao" project="dao"
|
||||
file="/dao/src/main/java/com/iluwatar/dao/DBCustomerDao.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="751" y="456"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="false" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="false" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<interface id="5" language="java" name="javax.sql.DataSource" project="dao"
|
||||
file="/opt/Softwares/Eclipses/MARS/eclipse/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="1083" y="459"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</interface>
|
||||
<association id="6">
|
||||
<end type="SOURCE" refId="4" navigable="false">
|
||||
<attribute id="7" name="dataSource"/>
|
||||
<multiplicity id="8" minimum="0" maximum="1"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="5" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<realization id="9">
|
||||
<end type="SOURCE" refId="3"/>
|
||||
<end type="TARGET" refId="2"/>
|
||||
</realization>
|
||||
<dependency id="10">
|
||||
<end type="SOURCE" refId="1"/>
|
||||
<end type="TARGET" refId="2"/>
|
||||
</dependency>
|
||||
<realization id="11">
|
||||
<end type="SOURCE" refId="4"/>
|
||||
<end type="TARGET" refId="2"/>
|
||||
</realization>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
||||
@@ -0,0 +1,68 @@
|
||||
@startuml
|
||||
package com.iluwatar.dao {
|
||||
class App {
|
||||
- ALL_CUSTOMERS : String {static}
|
||||
- DB_URL : String {static}
|
||||
- log : Logger {static}
|
||||
+ App()
|
||||
- addCustomers(customerDao : CustomerDao) {static}
|
||||
- createDataSource() : DataSource {static}
|
||||
- createSchema(dataSource : DataSource) {static}
|
||||
- deleteSchema(dataSource : DataSource) {static}
|
||||
+ generateSampleCustomers() : List<Customer> {static}
|
||||
+ main(args : String[]) {static}
|
||||
- performOperationsUsing(customerDao : CustomerDao) {static}
|
||||
}
|
||||
class Customer {
|
||||
- firstName : String
|
||||
- id : int
|
||||
- lastName : String
|
||||
+ Customer(id : int, firstName : String, lastName : String)
|
||||
+ equals(that : Object) : boolean
|
||||
+ getFirstName() : String
|
||||
+ getId() : int
|
||||
+ getLastName() : String
|
||||
+ hashCode() : int
|
||||
+ setFirstName(firstName : String)
|
||||
+ setId(id : int)
|
||||
+ setLastName(lastName : String)
|
||||
+ toString() : String
|
||||
}
|
||||
interface CustomerDao {
|
||||
+ add(Customer) : boolean {abstract}
|
||||
+ delete(Customer) : boolean {abstract}
|
||||
+ getAll() : Stream<Customer> {abstract}
|
||||
+ getById(int) : Optional<Customer> {abstract}
|
||||
+ update(Customer) : boolean {abstract}
|
||||
}
|
||||
class CustomerSchemaSql {
|
||||
+ CREATE_SCHEMA_SQL : String {static}
|
||||
+ DELETE_SCHEMA_SQL : String {static}
|
||||
- CustomerSchemaSql()
|
||||
}
|
||||
class DbCustomerDao {
|
||||
- LOGGER : Logger {static}
|
||||
- dataSource : DataSource
|
||||
+ DbCustomerDao(dataSource : DataSource)
|
||||
+ add(customer : Customer) : boolean
|
||||
- createCustomer(resultSet : ResultSet) : Customer
|
||||
+ delete(customer : Customer) : boolean
|
||||
+ getAll() : Stream<Customer>
|
||||
+ getById(id : int) : Optional<Customer>
|
||||
- getConnection() : Connection
|
||||
- mutedClose(connection : Connection, statement : PreparedStatement, resultSet : ResultSet)
|
||||
+ update(customer : Customer) : boolean
|
||||
}
|
||||
class InMemoryCustomerDao {
|
||||
- idToCustomer : Map<Integer, Customer>
|
||||
+ InMemoryCustomerDao()
|
||||
+ add(customer : Customer) : boolean
|
||||
+ delete(customer : Customer) : boolean
|
||||
+ getAll() : Stream<Customer>
|
||||
+ getById(id : int) : Optional<Customer>
|
||||
+ update(customer : Customer) : boolean
|
||||
}
|
||||
}
|
||||
DbCustomerDao ..|> CustomerDao
|
||||
InMemoryCustomerDao ..|> CustomerDao
|
||||
@enduml
|
||||
@@ -0,0 +1,69 @@
|
||||
@startuml
|
||||
package com.iluwatar.dao {
|
||||
class App {
|
||||
- ALL_CUSTOMERS : String {static}
|
||||
- DB_URL : String {static}
|
||||
- LOGGER : Logger {static}
|
||||
+ App()
|
||||
- addCustomers(customerDao : CustomerDao) {static}
|
||||
- createDataSource() : DataSource {static}
|
||||
- createSchema(dataSource : DataSource) {static}
|
||||
- deleteSchema(dataSource : DataSource) {static}
|
||||
+ generateSampleCustomers() : List<Customer> {static}
|
||||
+ main(args : String[]) {static}
|
||||
- performOperationsUsing(customerDao : CustomerDao) {static}
|
||||
}
|
||||
class Customer {
|
||||
- firstName : String
|
||||
- id : int
|
||||
- lastName : String
|
||||
+ Customer(id : int, firstName : String, lastName : String)
|
||||
# canEqual(other : Object) : boolean
|
||||
+ equals(o : Object) : boolean
|
||||
+ getFirstName() : String
|
||||
+ getId() : int
|
||||
+ getLastName() : String
|
||||
+ hashCode() : int
|
||||
+ setFirstName(firstName : String)
|
||||
+ setId(id : int)
|
||||
+ setLastName(lastName : String)
|
||||
+ toString() : String
|
||||
}
|
||||
interface CustomerDao {
|
||||
+ add(Customer) : boolean {abstract}
|
||||
+ delete(Customer) : boolean {abstract}
|
||||
+ getAll() : Stream<Customer> {abstract}
|
||||
+ getById(int) : Optional<Customer> {abstract}
|
||||
+ update(Customer) : boolean {abstract}
|
||||
}
|
||||
class CustomerSchemaSql {
|
||||
+ CREATE_SCHEMA_SQL : String {static}
|
||||
+ DELETE_SCHEMA_SQL : String {static}
|
||||
- CustomerSchemaSql()
|
||||
}
|
||||
class DbCustomerDao {
|
||||
- LOGGER : Logger {static}
|
||||
- dataSource : DataSource
|
||||
+ DbCustomerDao(dataSource : DataSource)
|
||||
+ add(customer : Customer) : boolean
|
||||
- createCustomer(resultSet : ResultSet) : Customer
|
||||
+ delete(customer : Customer) : boolean
|
||||
+ getAll() : Stream<Customer>
|
||||
+ getById(id : int) : Optional<Customer>
|
||||
- getConnection() : Connection
|
||||
- mutedClose(connection : Connection, statement : PreparedStatement, resultSet : ResultSet)
|
||||
+ update(customer : Customer) : boolean
|
||||
}
|
||||
class InMemoryCustomerDao {
|
||||
- idToCustomer : Map<Integer, Customer>
|
||||
+ InMemoryCustomerDao()
|
||||
+ add(customer : Customer) : boolean
|
||||
+ delete(customer : Customer) : boolean
|
||||
+ getAll() : Stream<Customer>
|
||||
+ getById(id : int) : Optional<Customer>
|
||||
+ update(customer : Customer) : boolean
|
||||
}
|
||||
}
|
||||
DbCustomerDao ..|> CustomerDao
|
||||
InMemoryCustomerDao ..|> CustomerDao
|
||||
@enduml
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
|
||||
The MIT License
|
||||
Copyright © 2014-2022 Ilkka Seppälä
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.26.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>data-access-object</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.iluwatar.dao.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import javax.sql.DataSource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.h2.jdbcx.JdbcDataSource;
|
||||
|
||||
/**
|
||||
* Data Access Object (DAO) is an object that provides an abstract interface to some type of
|
||||
* database or other persistence mechanism. By mapping application calls to the persistence layer,
|
||||
* DAO provide some specific data operations without exposing details of the database. This
|
||||
* isolation supports the Single responsibility principle. It separates what data accesses the
|
||||
* application needs, in terms of domain-specific objects and data types (the public interface of
|
||||
* the DAO), from how these needs can be satisfied with a specific DBMS.
|
||||
*
|
||||
* <p>With the DAO pattern, we can use various method calls to retrieve/add/delete/update data
|
||||
* without directly interacting with the data source. The below example demonstrates basic CRUD
|
||||
* operations: select, add, update, and delete.
|
||||
*/
|
||||
@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(): ";
|
||||
|
||||
/**
|
||||
* Program entry point.
|
||||
*
|
||||
* @param args command line args.
|
||||
* @throws Exception if any error occurs.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate customers.
|
||||
*
|
||||
* @return list of customers.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* Custom exception.
|
||||
*/
|
||||
public class CustomException extends Exception {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CustomException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* A customer POJO that represents the data that will be read from the data source.
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@AllArgsConstructor
|
||||
public class Customer {
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
private int id;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* In an application the Data Access Object (DAO) is a part of Data access layer. It is an object
|
||||
* that provides an interface to some type of persistence mechanism. By mapping application calls to
|
||||
* the persistence layer, DAO provides some specific data operations without exposing details of the
|
||||
* database. This isolation supports the Single responsibility principle. It separates what data
|
||||
* accesses the application needs, in terms of domain-specific objects and data types (the public
|
||||
* interface of the DAO), from how these needs can be satisfied with a specific DBMS, database
|
||||
* schema, etc.
|
||||
*
|
||||
* <p>Any change in the way data is stored and retrieved will not change the client code as the
|
||||
* client will be using interface and need not worry about exact source.
|
||||
*
|
||||
* @see InMemoryCustomerDao
|
||||
* @see DbCustomerDao
|
||||
*/
|
||||
public interface CustomerDao {
|
||||
|
||||
/**
|
||||
* Get all customers.
|
||||
*
|
||||
* @return all the customers as a stream. The stream may be lazily or eagerly evaluated based on
|
||||
* the implementation. The stream must be closed after use.
|
||||
* @throws Exception if any error occurs.
|
||||
*/
|
||||
Stream<Customer> getAll() throws Exception;
|
||||
|
||||
/**
|
||||
* Get customer as Optional by id.
|
||||
*
|
||||
* @param id unique identifier of the customer.
|
||||
* @return an optional with customer if a customer with unique identifier <code>id</code> exists,
|
||||
* empty optional otherwise.
|
||||
* @throws Exception if any error occurs.
|
||||
*/
|
||||
Optional<Customer> getById(int id) throws Exception;
|
||||
|
||||
/**
|
||||
* Add a customer.
|
||||
*
|
||||
* @param customer the customer to be added.
|
||||
* @return true if customer is successfully added, false if customer already exists.
|
||||
* @throws Exception if any error occurs.
|
||||
*/
|
||||
boolean add(Customer customer) throws Exception;
|
||||
|
||||
/**
|
||||
* Update a customer.
|
||||
*
|
||||
* @param customer the customer to be updated.
|
||||
* @return true if customer exists and is successfully updated, false otherwise.
|
||||
* @throws Exception if any error occurs.
|
||||
*/
|
||||
boolean update(Customer customer) throws Exception;
|
||||
|
||||
/**
|
||||
* Delete a customer.
|
||||
*
|
||||
* @param customer the customer to be deleted.
|
||||
* @return true if customer exists and is successfully deleted, false otherwise.
|
||||
* @throws Exception if any error occurs.
|
||||
*/
|
||||
boolean delete(Customer customer) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
/**
|
||||
* Customer Schema SQL Class.
|
||||
*/
|
||||
public final class CustomerSchemaSql {
|
||||
|
||||
private CustomerSchemaSql() {
|
||||
}
|
||||
|
||||
public static final String CREATE_SCHEMA_SQL =
|
||||
"CREATE TABLE CUSTOMERS (ID NUMBER, FNAME VARCHAR(100), "
|
||||
+ "LNAME VARCHAR(100))";
|
||||
|
||||
public static final String DELETE_SCHEMA_SQL = "DROP TABLE CUSTOMERS";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Optional;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import javax.sql.DataSource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* An implementation of {@link CustomerDao} that persists customers in RDBMS.
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class DbCustomerDao implements CustomerDao {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
/**
|
||||
* Get all customers as Java Stream.
|
||||
*
|
||||
* @return a lazily populated stream of customers. Note the stream returned must be closed to free
|
||||
* all the acquired resources. The stream keeps an open connection to the database till it is
|
||||
* complete or is closed manually.
|
||||
*/
|
||||
@Override
|
||||
public Stream<Customer> getAll() throws Exception {
|
||||
try {
|
||||
var connection = getConnection();
|
||||
var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); // NOSONAR
|
||||
var resultSet = statement.executeQuery(); // NOSONAR
|
||||
return StreamSupport.stream(new Spliterators.AbstractSpliterator<Customer>(Long.MAX_VALUE,
|
||||
Spliterator.ORDERED) {
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super Customer> action) {
|
||||
try {
|
||||
if (!resultSet.next()) {
|
||||
return false;
|
||||
}
|
||||
action.accept(createCustomer(resultSet));
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e); // NOSONAR
|
||||
}
|
||||
}
|
||||
}, false).onClose(() -> mutedClose(connection, statement, resultSet));
|
||||
} catch (SQLException e) {
|
||||
throw new CustomException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
private void mutedClose(Connection connection, PreparedStatement statement, ResultSet resultSet) {
|
||||
try {
|
||||
resultSet.close();
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.info("Exception thrown " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Customer createCustomer(ResultSet resultSet) throws SQLException {
|
||||
return new Customer(resultSet.getInt("ID"),
|
||||
resultSet.getString("FNAME"),
|
||||
resultSet.getString("LNAME"));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Optional<Customer> getById(int id) throws Exception {
|
||||
|
||||
ResultSet resultSet = null;
|
||||
|
||||
try (var connection = getConnection();
|
||||
var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) {
|
||||
|
||||
statement.setInt(1, id);
|
||||
resultSet = statement.executeQuery();
|
||||
if (resultSet.next()) {
|
||||
return Optional.of(createCustomer(resultSet));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
} finally {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean add(Customer customer) throws Exception {
|
||||
if (getById(customer.getId()).isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (var connection = getConnection();
|
||||
var statement = connection.prepareStatement("INSERT INTO CUSTOMERS VALUES (?,?,?)")) {
|
||||
statement.setInt(1, customer.getId());
|
||||
statement.setString(2, customer.getFirstName());
|
||||
statement.setString(3, customer.getLastName());
|
||||
statement.execute();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean update(Customer customer) throws Exception {
|
||||
try (var connection = getConnection();
|
||||
var statement =
|
||||
connection
|
||||
.prepareStatement("UPDATE CUSTOMERS SET FNAME = ?, LNAME = ? WHERE ID = ?")) {
|
||||
statement.setString(1, customer.getFirstName());
|
||||
statement.setString(2, customer.getLastName());
|
||||
statement.setInt(3, customer.getId());
|
||||
return statement.executeUpdate() > 0;
|
||||
} catch (SQLException ex) {
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean delete(Customer customer) throws Exception {
|
||||
try (var connection = getConnection();
|
||||
var statement = connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) {
|
||||
statement.setInt(1, customer.getId());
|
||||
return statement.executeUpdate() > 0;
|
||||
} catch (SQLException ex) {
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* An in memory implementation of {@link CustomerDao}, which stores the customers in JVM memory and
|
||||
* data is lost when the application exits.
|
||||
* <br>
|
||||
* This implementation is useful as temporary database or for testing.
|
||||
*/
|
||||
public class InMemoryCustomerDao implements CustomerDao {
|
||||
|
||||
private final Map<Integer, Customer> idToCustomer = new HashMap<>();
|
||||
|
||||
/**
|
||||
* An eagerly evaluated stream of customers stored in memory.
|
||||
*/
|
||||
@Override
|
||||
public Stream<Customer> getAll() {
|
||||
return idToCustomer.values().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Customer> getById(final int id) {
|
||||
return Optional.ofNullable(idToCustomer.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(final Customer customer) {
|
||||
if (getById(customer.getId()).isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
idToCustomer.put(customer.getId(), customer);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(final Customer customer) {
|
||||
return idToCustomer.replace(customer.getId(), customer) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(final Customer customer) {
|
||||
return idToCustomer.remove(customer.getId()) != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
/**
|
||||
* Tests that DAO example runs without errors.
|
||||
*/
|
||||
class AppTest {
|
||||
|
||||
/**
|
||||
* Issue: Add at least one assertion to this test case.
|
||||
* Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
|
||||
* throws an exception.
|
||||
*/
|
||||
|
||||
@Test
|
||||
void shouldExecuteDaoWithoutException() {
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests {@link Customer}.
|
||||
*/
|
||||
class CustomerTest {
|
||||
|
||||
private Customer customer;
|
||||
private static final int ID = 1;
|
||||
private static final String FIRSTNAME = "Winston";
|
||||
private static final String LASTNAME = "Churchill";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
customer = new Customer(ID, FIRSTNAME, LASTNAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAndSetId() {
|
||||
final var newId = 2;
|
||||
customer.setId(newId);
|
||||
assertEquals(newId, customer.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAndSetFirstname() {
|
||||
final var newFirstname = "Bill";
|
||||
customer.setFirstName(newFirstname);
|
||||
assertEquals(newFirstname, customer.getFirstName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAndSetLastname() {
|
||||
final var newLastname = "Clinton";
|
||||
customer.setLastName(newLastname);
|
||||
assertEquals(newLastname, customer.getLastName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void notEqualWithDifferentId() {
|
||||
final var newId = 2;
|
||||
final var otherCustomer = new Customer(newId, FIRSTNAME, LASTNAME);
|
||||
assertNotEquals(customer, otherCustomer);
|
||||
assertNotEquals(customer.hashCode(), otherCustomer.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithSameObjectValues() {
|
||||
final var otherCustomer = new Customer(ID, FIRSTNAME, LASTNAME);
|
||||
assertEquals(customer, otherCustomer);
|
||||
assertEquals(customer.hashCode(), otherCustomer.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithSameObjects() {
|
||||
assertEquals(customer, customer);
|
||||
assertEquals(customer.hashCode(), customer.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToString() {
|
||||
assertEquals(String.format("Customer(id=%s, firstName=%s, lastName=%s)",
|
||||
customer.getId(), customer.getFirstName(), customer.getLastName()), customer.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import javax.sql.DataSource;
|
||||
import org.h2.jdbcx.JdbcDataSource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
/**
|
||||
* Tests {@link DbCustomerDao}.
|
||||
*/
|
||||
class DbCustomerDaoTest {
|
||||
|
||||
private static final String DB_URL = "jdbc:h2:mem:dao;DB_CLOSE_DELAY=-1";
|
||||
private DbCustomerDao dao;
|
||||
private final Customer existingCustomer = new Customer(1, "Freddy", "Krueger");
|
||||
|
||||
/**
|
||||
* Creates customers schema.
|
||||
*
|
||||
* @throws SQLException if there is any error while creating schema.
|
||||
*/
|
||||
@BeforeEach
|
||||
void createSchema() throws SQLException {
|
||||
try (var connection = DriverManager.getConnection(DB_URL);
|
||||
var statement = connection.createStatement()) {
|
||||
statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the scenario where DB connectivity is present.
|
||||
*/
|
||||
@Nested
|
||||
class ConnectionSuccess {
|
||||
|
||||
/**
|
||||
* Setup for connection success scenario.
|
||||
*
|
||||
* @throws Exception if any error occurs.
|
||||
*/
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
var dataSource = new JdbcDataSource();
|
||||
dataSource.setURL(DB_URL);
|
||||
dao = new DbCustomerDao(dataSource);
|
||||
var result = dao.add(existingCustomer);
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the scenario when DAO operations are being performed on a non-existing customer.
|
||||
*/
|
||||
@Nested
|
||||
class NonExistingCustomer {
|
||||
|
||||
@Test
|
||||
void addingShouldResultInSuccess() throws Exception {
|
||||
try (var allCustomers = dao.getAll()) {
|
||||
assumeTrue(allCustomers.count() == 1);
|
||||
}
|
||||
|
||||
final var nonExistingCustomer = new Customer(2, "Robert", "Englund");
|
||||
var result = dao.add(nonExistingCustomer);
|
||||
assertTrue(result);
|
||||
|
||||
assertCustomerCountIs(2);
|
||||
assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
|
||||
final var nonExistingCustomer = new Customer(2, "Robert", "Englund");
|
||||
var result = dao.delete(nonExistingCustomer);
|
||||
|
||||
assertFalse(result);
|
||||
assertCustomerCountIs(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
|
||||
final var nonExistingId = getNonExistingCustomerId();
|
||||
final var newFirstname = "Douglas";
|
||||
final var newLastname = "MacArthur";
|
||||
final var customer = new Customer(nonExistingId, newFirstname, newLastname);
|
||||
var result = dao.update(customer);
|
||||
|
||||
assertFalse(result);
|
||||
assertFalse(dao.getById(nonExistingId).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveShouldReturnNoCustomer() throws Exception {
|
||||
assertFalse(dao.getById(getNonExistingCustomerId()).isPresent());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a scenario where DAO operations are being performed on an already existing
|
||||
* customer.
|
||||
*/
|
||||
@Nested
|
||||
class ExistingCustomer {
|
||||
|
||||
@Test
|
||||
void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception {
|
||||
var existingCustomer = new Customer(1, "Freddy", "Krueger");
|
||||
var result = dao.add(existingCustomer);
|
||||
|
||||
assertFalse(result);
|
||||
assertCustomerCountIs(1);
|
||||
assertEquals(existingCustomer, dao.getById(existingCustomer.getId()).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception {
|
||||
var result = dao.delete(existingCustomer);
|
||||
|
||||
assertTrue(result);
|
||||
assertCustomerCountIs(0);
|
||||
assertFalse(dao.getById(existingCustomer.getId()).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws
|
||||
Exception {
|
||||
final var newFirstname = "Bernard";
|
||||
final var newLastname = "Montgomery";
|
||||
final var customer = new Customer(existingCustomer.getId(), newFirstname, newLastname);
|
||||
var result = dao.update(customer);
|
||||
|
||||
assertTrue(result);
|
||||
|
||||
final var cust = dao.getById(existingCustomer.getId()).get();
|
||||
assertEquals(newFirstname, cust.getFirstName());
|
||||
assertEquals(newLastname, cust.getLastName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a scenario where DB connectivity is not present due to network issue, or DB service
|
||||
* unavailable.
|
||||
*/
|
||||
@Nested
|
||||
class ConnectivityIssue {
|
||||
|
||||
private static final String EXCEPTION_CAUSE = "Connection not available";
|
||||
|
||||
/**
|
||||
* setup a connection failure scenario.
|
||||
*
|
||||
* @throws SQLException if any error occurs.
|
||||
*/
|
||||
@BeforeEach
|
||||
void setUp() throws SQLException {
|
||||
dao = new DbCustomerDao(mockedDatasource());
|
||||
}
|
||||
|
||||
private DataSource mockedDatasource() throws SQLException {
|
||||
var mockedDataSource = mock(DataSource.class);
|
||||
var mockedConnection = mock(Connection.class);
|
||||
var exception = new SQLException(EXCEPTION_CAUSE);
|
||||
doThrow(exception).when(mockedConnection).prepareStatement(Mockito.anyString());
|
||||
doReturn(mockedConnection).when(mockedDataSource).getConnection();
|
||||
return mockedDataSource;
|
||||
}
|
||||
|
||||
@Test
|
||||
void addingACustomerFailsWithExceptionAsFeedbackToClient() {
|
||||
assertThrows(Exception.class, () -> dao.add(new Customer(2, "Bernard", "Montgomery")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingACustomerFailsWithExceptionAsFeedbackToTheClient() {
|
||||
assertThrows(Exception.class, () -> dao.delete(existingCustomer));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updatingACustomerFailsWithFeedbackToTheClient() {
|
||||
final var newFirstname = "Bernard";
|
||||
final var newLastname = "Montgomery";
|
||||
assertThrows(Exception.class, () -> dao.update(new Customer(existingCustomer.getId(), newFirstname, newLastname)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrievingACustomerByIdFailsWithExceptionAsFeedbackToClient() {
|
||||
assertThrows(Exception.class, () -> dao.getById(existingCustomer.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrievingAllCustomersFailsWithExceptionAsFeedbackToClient() {
|
||||
assertThrows(Exception.class, () -> dao.getAll());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete customer schema for fresh setup per test.
|
||||
*
|
||||
* @throws SQLException if any error occurs.
|
||||
*/
|
||||
@AfterEach
|
||||
void deleteSchema() throws SQLException {
|
||||
try (var connection = DriverManager.getConnection(DB_URL);
|
||||
var statement = connection.createStatement()) {
|
||||
statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertCustomerCountIs(int count) throws Exception {
|
||||
try (var allCustomers = dao.getAll()) {
|
||||
assertEquals(count, allCustomers.count());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An arbitrary number which does not correspond to an active Customer id.
|
||||
*
|
||||
* @return an int of a customer id which doesn't exist
|
||||
*/
|
||||
private int getNonExistingCustomerId() {
|
||||
return 999;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.dao;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests {@link InMemoryCustomerDao}.
|
||||
*/
|
||||
class InMemoryCustomerDaoTest {
|
||||
|
||||
private InMemoryCustomerDao dao;
|
||||
private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger");
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
dao = new InMemoryCustomerDao();
|
||||
assertTrue(dao.add(CUSTOMER));
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the scenario when the DAO operations are being performed on a non-existent
|
||||
* customer.
|
||||
*/
|
||||
@Nested
|
||||
class NonExistingCustomer {
|
||||
|
||||
@Test
|
||||
void addingShouldResultInSuccess() throws Exception {
|
||||
try (var allCustomers = dao.getAll()) {
|
||||
assumeTrue(allCustomers.count() == 1);
|
||||
}
|
||||
|
||||
final var nonExistingCustomer = new Customer(2, "Robert", "Englund");
|
||||
var result = dao.add(nonExistingCustomer);
|
||||
assertTrue(result);
|
||||
|
||||
assertCustomerCountIs(2);
|
||||
assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
|
||||
final var nonExistingCustomer = new Customer(2, "Robert", "Englund");
|
||||
var result = dao.delete(nonExistingCustomer);
|
||||
|
||||
assertFalse(result);
|
||||
assertCustomerCountIs(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateShouldBeFailureAndNotAffectExistingCustomers() {
|
||||
final var nonExistingId = getNonExistingCustomerId();
|
||||
final var newFirstname = "Douglas";
|
||||
final var newLastname = "MacArthur";
|
||||
final var customer = new Customer(nonExistingId, newFirstname, newLastname);
|
||||
var result = dao.update(customer);
|
||||
|
||||
assertFalse(result);
|
||||
assertFalse(dao.getById(nonExistingId).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void retrieveShouldReturnNoCustomer() throws Exception {
|
||||
assertFalse(dao.getById(getNonExistingCustomerId()).isPresent());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the scenario when the DAO operations are being performed on an already existing
|
||||
* customer.
|
||||
*/
|
||||
@Nested
|
||||
class ExistingCustomer {
|
||||
|
||||
@Test
|
||||
void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception {
|
||||
var result = dao.add(CUSTOMER);
|
||||
|
||||
assertFalse(result);
|
||||
assertCustomerCountIs(1);
|
||||
assertEquals(CUSTOMER, dao.getById(CUSTOMER.getId()).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception {
|
||||
var result = dao.delete(CUSTOMER);
|
||||
|
||||
assertTrue(result);
|
||||
assertCustomerCountIs(0);
|
||||
assertFalse(dao.getById(CUSTOMER.getId()).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws
|
||||
Exception {
|
||||
final var newFirstname = "Bernard";
|
||||
final var newLastname = "Montgomery";
|
||||
final var customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
|
||||
var result = dao.update(customer);
|
||||
|
||||
assertTrue(result);
|
||||
|
||||
final var cust = dao.getById(CUSTOMER.getId()).get();
|
||||
assertEquals(newFirstname, cust.getFirstName());
|
||||
assertEquals(newLastname, cust.getLastName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void retriveShouldReturnTheCustomer() {
|
||||
var optionalCustomer = dao.getById(CUSTOMER.getId());
|
||||
|
||||
assertTrue(optionalCustomer.isPresent());
|
||||
assertEquals(CUSTOMER, optionalCustomer.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An arbitrary number which does not correspond to an active Customer id.
|
||||
*
|
||||
* @return an int of a customer id which doesn't exist
|
||||
*/
|
||||
private int getNonExistingCustomerId() {
|
||||
return 999;
|
||||
}
|
||||
|
||||
private void assertCustomerCountIs(int count) throws Exception {
|
||||
try (var allCustomers = dao.getAll()) {
|
||||
assertEquals(count, allCustomers.count());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user