Merge pull request #407 from iluwatar/refactor-dao

Solves #404 Refactor dao
This commit is contained in:
Ilkka Seppälä 2016-03-28 19:44:07 +03:00
commit d20145c05d
16 changed files with 873 additions and 267 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -1,45 +1,71 @@
<?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.Customer" project="dao"
file="/dao/src/main/java/com/iluwatar/Customer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="176" y="337"/>
<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="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="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>
<class id="2" language="java" name="com.iluwatar.CustomerDaoImpl" project="dao"
file="/dao/src/main/java/com/iluwatar/CustomerDaoImpl.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="540" y="334"/>
<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>
</class>
<interface id="3" language="java" name="com.iluwatar.CustomerDao" project="dao"
file="/dao/src/main/java/com/iluwatar/CustomerDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="536" y="131"/>
<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>
<realization id="4">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="3"/>
</realization>
<association id="5">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="6" name="customers"/>
<multiplicity id="7" minimum="0" maximum="2147483647"/>
<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="1" navigable="true"/>
<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"/>

View File

@ -44,6 +44,18 @@
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>de.bechte.junit</groupId>
<artifactId>junit-hierarchicalcontextrunner</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -20,49 +20,101 @@
* 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.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
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. The below example demonstrates basic CRUD operations: select,
* add, update, and delete.
*
* <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.
*
*
*/
public class App {
private static final String DB_URL = "jdbc:h2:~/dao:customerdb";
private static Logger log = Logger.getLogger(App.class);
/**
* Program entry point.
*
* @param args command line args.
* @throws Exception if any error occurs.
*/
public static void main(final String[] args) {
final CustomerDao customerDao = new CustomerDaoImpl(generateSampleCustomers());
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
log.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
public static void main(final String[] args) throws Exception {
final CustomerDao inMemoryDao = new InMemoryCustomerDao();
performOperationsUsing(inMemoryDao);
final DataSource dataSource = createDataSource();
createSchema(dataSource);
final CustomerDao dbDao = new DbCustomerDao(dataSource);
performOperationsUsing(dbDao);
deleteSchema(dataSource);
}
private static void deleteSchema(DataSource dataSource) throws SQLException {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
}
}
private static void createSchema(DataSource dataSource) throws SQLException {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
}
}
private static DataSource createDataSource() {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(DB_URL);
return dataSource;
}
private static void performOperationsUsing(final CustomerDao customerDao) throws Exception {
addCustomers(customerDao);
log.info("customerDao.getAllCustomers(): ");
try (Stream<Customer> customerStream = customerDao.getAll()) {
customerStream.forEach((customer) -> log.info(customer));
}
log.info("customerDao.getCustomerById(2): " + customerDao.getById(2));
final Customer customer = new Customer(4, "Dan", "Danson");
customerDao.addCustomer(customer);
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customerDao.add(customer);
log.info("customerDao.getAllCustomers(): " + customerDao.getAll());
customer.setFirstName("Daniel");
customer.setLastName("Danielson");
customerDao.updateCustomer(customer);
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customerDao.deleteCustomer(customer);
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customerDao.update(customer);
log.info("customerDao.getAllCustomers(): ");
try (Stream<Customer> customerStream = customerDao.getAll()) {
customerStream.forEach((cust) -> log.info(cust));
}
customerDao.delete(customer);
log.info("customerDao.getAllCustomers(): " + customerDao.getAll());
}
private static void addCustomers(CustomerDao customerDao) throws Exception {
for (Customer customer : generateSampleCustomers()) {
customerDao.add(customer);
}
}
/**

View File

@ -20,11 +20,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.dao;
/**
*
* Customer
* A customer POJO that represents the data that will be read from the data source.
*
*/
public class Customer {
@ -34,7 +34,7 @@ public class Customer {
private String lastName;
/**
* Constructor
* Creates an instance of customer.
*/
public Customer(final int id, final String firstName, final String lastName) {
this.id = id;
@ -73,12 +73,12 @@ public class Customer {
}
@Override
public boolean equals(final Object o) {
public boolean equals(final Object that) {
boolean isEqual = false;
if (this == o) {
if (this == that) {
isEqual = true;
} else if (o != null && getClass() == o.getClass()) {
final Customer customer = (Customer) o;
} else if (that != null && getClass() == that.getClass()) {
final Customer customer = (Customer) that;
if (getId() == customer.getId()) {
isEqual = true;
}

View File

@ -20,24 +20,62 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.dao;
import java.util.List;
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.
*
* CustomerDao
*
* <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 {
List<Customer> getAllCustomers();
/**
* @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;
/**
* @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;
Customer getCustomerById(int id);
/**
* @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;
void addCustomer(Customer 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;
void updateCustomer(Customer customer);
void deleteCustomer(Customer 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;
}

View File

@ -1,84 +0,0 @@
/**
* The MIT License
* Copyright (c) 2014 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.List;
/**
*
* The 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, database schema, etc.
*
*/
public class CustomerDaoImpl implements CustomerDao {
// Represents the DB structure for our example so we don't have to managed it ourselves
// Note: Normally this would be in the form of an actual database and not part of the Dao Impl.
private List<Customer> customers;
public CustomerDaoImpl(final List<Customer> customers) {
this.customers = customers;
}
@Override
public List<Customer> getAllCustomers() {
return customers;
}
@Override
public Customer getCustomerById(final int id) {
Customer customer = null;
for (final Customer cus : getAllCustomers()) {
if (cus.getId() == id) {
customer = cus;
break;
}
}
return customer;
}
@Override
public void addCustomer(final Customer customer) {
if (getCustomerById(customer.getId()) == null) {
customers.add(customer);
}
}
@Override
public void updateCustomer(final Customer customer) {
if (getAllCustomers().contains(customer)) {
final int index = getAllCustomers().indexOf(customer);
getAllCustomers().set(index, customer);
}
}
@Override
public void deleteCustomer(final Customer customer) {
getAllCustomers().remove(customer);
}
}

View File

@ -0,0 +1,9 @@
package com.iluwatar.dao;
public interface CustomerSchemaSql {
String CREATE_SCHEMA_SQL = "CREATE TABLE CUSTOMERS (ID NUMBER, FNAME VARCHAR(100), "
+ "LNAME VARCHAR(100))";
String DELETE_SCHEMA_SQL = "DROP TABLE CUSTOMERS";
}

View File

@ -0,0 +1,183 @@
/**
* The MIT License
* Copyright (c) 2014 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;
/**
* An implementation of {@link CustomerDao} that persists customers in RDBMS.
*
*/
public class DbCustomerDao implements CustomerDao {
private final DataSource dataSource;
/**
* Creates an instance of {@link DbCustomerDao} which uses provided <code>dataSource</code>
* to store and retrieve customer information.
*
* @param dataSource a non-null dataSource.
*/
public DbCustomerDao(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* @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 {
Connection connection;
try {
connection = getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS");
ResultSet resultSet = statement.executeQuery();
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);
}
}
}, false).onClose(() -> mutedClose(connection));
} catch (SQLException e) {
throw new Exception(e.getMessage(), e);
}
}
private Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
private void mutedClose(Connection connection) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
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 {
try (Connection connection = getConnection();
PreparedStatement statement =
connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) {
statement.setInt(1, id);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return Optional.of(createCustomer(resultSet));
} else {
return Optional.empty();
}
} catch (SQLException ex) {
throw new Exception(ex.getMessage(), ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean add(Customer customer) throws Exception {
if (getById(customer.getId()).isPresent()) {
return false;
}
try (Connection connection = getConnection();
PreparedStatement 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 Exception(ex.getMessage(), ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean update(Customer customer) throws Exception {
try (Connection connection = getConnection();
PreparedStatement 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 Exception(ex.getMessage(), ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean delete(Customer customer) throws Exception {
try (Connection connection = getConnection();
PreparedStatement statement =
connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) {
statement.setInt(1, customer.getId());
return statement.executeUpdate() > 0;
} catch (SQLException ex) {
throw new Exception(ex.getMessage(), ex);
}
}
}

View File

@ -0,0 +1,73 @@
/**
* The MIT License
* Copyright (c) 2014 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 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;
}
}

View File

@ -20,18 +20,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.dao;
import org.junit.Test;
import java.io.IOException;
/**
* Tests that DAO example runs without errors.
*/
public class AppTest {
@Test
public void test() throws IOException {
public void test() throws Exception {
String[] args = {};
App.main(args);
}

View File

@ -1,121 +0,0 @@
/**
* The MIT License
* Copyright (c) 2014 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.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
public class CustomerDaoImplTest {
private CustomerDaoImpl impl;
private List<Customer> customers;
private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger");
@Before
public void setUp() {
customers = new ArrayList<>();
customers.add(CUSTOMER);
impl = new CustomerDaoImpl(customers);
}
@Test
public void deleteExistingCustomer() {
assertEquals(1, impl.getAllCustomers().size());
impl.deleteCustomer(CUSTOMER);
assertTrue(impl.getAllCustomers().isEmpty());
}
@Test
public void deleteNonExistingCustomer() {
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
impl.deleteCustomer(nonExistingCustomer);
assertEquals(1, impl.getAllCustomers().size());
}
@Test
public void updateExistingCustomer() {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
impl.updateCustomer(customer);
final Customer cust = impl.getCustomerById(CUSTOMER.getId());
assertEquals(newFirstname, cust.getFirstName());
assertEquals(newLastname, cust.getLastName());
}
@Test
public void updateNonExistingCustomer() {
final int nonExistingId = getNonExistingCustomerId();
final String newFirstname = "Douglas";
final String newLastname = "MacArthur";
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
impl.updateCustomer(customer);
assertNull(impl.getCustomerById(nonExistingId));
final Customer existingCustomer = impl.getCustomerById(CUSTOMER.getId());
assertEquals(CUSTOMER.getFirstName(), existingCustomer.getFirstName());
assertEquals(CUSTOMER.getLastName(), existingCustomer.getLastName());
}
@Test
public void addCustomer() {
final Customer newCustomer = new Customer(3, "George", "Patton");
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
}
@Test
public void addAlreadyAddedCustomer() {
final Customer newCustomer = new Customer(3, "George", "Patton");
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
}
@Test
public void getExistinCustomerById() {
assertEquals(CUSTOMER, impl.getCustomerById(CUSTOMER.getId()));
}
@Test
public void getNonExistinCustomerById() {
final int nonExistingId = getNonExistingCustomerId();
assertNull(impl.getCustomerById(nonExistingId));
}
/**
* 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;
}
}

View File

@ -20,6 +20,7 @@
* 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.Assert.assertEquals;

View File

@ -0,0 +1,247 @@
package com.iluwatar.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.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 java.sql.Statement;
import java.util.stream.Stream;
import javax.sql.DataSource;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
/**
* Tests {@link DbCustomerDao}.
*/
@RunWith(HierarchicalContextRunner.class)
public class DbCustomerDaoTest {
private static final String DB_URL = "jdbc:h2:~/dao:customerdb";
private DbCustomerDao dao;
private Customer existingCustomer = new Customer(1, "Freddy", "Krueger");
/**
* Creates customers schema.
* @throws SQLException if there is any error while creating schema.
*/
@Before
public void createSchema() throws SQLException {
try (Connection connection = DriverManager.getConnection(DB_URL);
Statement statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
}
}
/**
* Represents the scenario where DB connectivity is present.
*/
public class ConnectionSuccess {
/**
* Setup for connection success scenario.
* @throws Exception if any error occurs.
*/
@Before
public void setUp() throws Exception {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(DB_URL);
dao = new DbCustomerDao(dataSource);
boolean result = dao.add(existingCustomer);
assertTrue(result);
}
/**
* Represents the scenario when DAO operations are being performed on a non existing customer.
*/
public class NonExistingCustomer {
@Test
public void addingShouldResultInSuccess() throws Exception {
try (Stream<Customer> allCustomers = dao.getAll()) {
assumeTrue(allCustomers.count() == 1);
}
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
boolean result = dao.add(nonExistingCustomer);
assertTrue(result);
assertCustomerCountIs(2);
assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get());
}
@Test
public void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
boolean result = dao.delete(nonExistingCustomer);
assertFalse(result);
assertCustomerCountIs(1);
}
@Test
public void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
final int nonExistingId = getNonExistingCustomerId();
final String newFirstname = "Douglas";
final String newLastname = "MacArthur";
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
boolean result = dao.update(customer);
assertFalse(result);
assertFalse(dao.getById(nonExistingId).isPresent());
}
@Test
public void retrieveShouldReturnNoCustomer() throws Exception {
assertFalse(dao.getById(getNonExistingCustomerId()).isPresent());
}
}
/**
* Represents a scenario where DAO operations are being performed on an already existing
* customer.
*
*/
public class ExistingCustomer {
@Test
public void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception {
Customer existingCustomer = new Customer(1, "Freddy", "Krueger");
boolean result = dao.add(existingCustomer);
assertFalse(result);
assertCustomerCountIs(1);
assertEquals(existingCustomer, dao.getById(existingCustomer.getId()).get());
}
@Test
public void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception {
boolean result = dao.delete(existingCustomer);
assertTrue(result);
assertCustomerCountIs(0);
assertFalse(dao.getById(existingCustomer.getId()).isPresent());
}
@Test
public void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws Exception {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
final Customer customer = new Customer(existingCustomer.getId(), newFirstname, newLastname);
boolean result = dao.update(customer);
assertTrue(result);
final Customer 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.
*
*/
public class ConnectivityIssue {
private static final String EXCEPTION_CAUSE = "Connection not available";
@Rule public ExpectedException exception = ExpectedException.none();
/**
* setup a connection failure scenario.
* @throws SQLException if any error occurs.
*/
@Before
public void setUp() throws SQLException {
dao = new DbCustomerDao(mockedDatasource());
exception.expect(Exception.class);
exception.expectMessage(EXCEPTION_CAUSE);
}
private DataSource mockedDatasource() throws SQLException {
DataSource mockedDataSource = mock(DataSource.class);
Connection mockedConnection = mock(Connection.class);
SQLException exception = new SQLException(EXCEPTION_CAUSE);
doThrow(exception).when(mockedConnection).prepareStatement(Mockito.anyString());
doReturn(mockedConnection).when(mockedDataSource).getConnection();
return mockedDataSource;
}
@Test
public void addingACustomerFailsWithExceptionAsFeedbackToClient() throws Exception {
dao.add(new Customer(2, "Bernard", "Montgomery"));
}
@Test
public void deletingACustomerFailsWithExceptionAsFeedbackToTheClient() throws Exception {
dao.delete(existingCustomer);
}
@Test
public void updatingACustomerFailsWithFeedbackToTheClient() throws Exception {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
dao.update(new Customer(existingCustomer.getId(), newFirstname, newLastname));
}
@Test
public void retrievingACustomerByIdFailsWithExceptionAsFeedbackToClient() throws Exception {
dao.getById(existingCustomer.getId());
}
@Test
public void retrievingAllCustomersFailsWithExceptionAsFeedbackToClient() throws Exception {
dao.getAll();
}
}
/**
* Delete customer schema for fresh setup per test.
* @throws SQLException if any error occurs.
*/
@After
public void deleteSchema() throws SQLException {
try (Connection connection = DriverManager.getConnection(DB_URL);
Statement statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
}
}
private void assertCustomerCountIs(int count) throws Exception {
try (Stream<Customer> allCustomers = dao.getAll()) {
assertTrue(allCustomers.count() == 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;
}
}

View File

@ -0,0 +1,164 @@
/**
* The MIT License
* Copyright (c) 2014 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.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
/**
* Tests {@link InMemoryCustomerDao}.
*/
@RunWith(HierarchicalContextRunner.class)
public class InMemoryCustomerDaoTest {
private InMemoryCustomerDao dao;
private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger");
@Before
public void setUp() {
dao = new InMemoryCustomerDao();
assertTrue(dao.add(CUSTOMER));
}
/**
* Represents the scenario when the DAO operations are being performed on a non existent
* customer.
*/
public class NonExistingCustomer {
@Test
public void addingShouldResultInSuccess() throws Exception {
try (Stream<Customer> allCustomers = dao.getAll()) {
assumeTrue(allCustomers.count() == 1);
}
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
boolean result = dao.add(nonExistingCustomer);
assertTrue(result);
assertCustomerCountIs(2);
assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get());
}
@Test
public void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
boolean result = dao.delete(nonExistingCustomer);
assertFalse(result);
assertCustomerCountIs(1);
}
@Test
public void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
final int nonExistingId = getNonExistingCustomerId();
final String newFirstname = "Douglas";
final String newLastname = "MacArthur";
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
boolean result = dao.update(customer);
assertFalse(result);
assertFalse(dao.getById(nonExistingId).isPresent());
}
@Test
public void retrieveShouldReturnNoCustomer() throws Exception {
assertFalse(dao.getById(getNonExistingCustomerId()).isPresent());
}
}
/**
* Represents the scenario when the DAO operations are being performed on an already existing
* customer.
*/
public class ExistingCustomer {
@Test
public void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception {
boolean result = dao.add(CUSTOMER);
assertFalse(result);
assertCustomerCountIs(1);
assertEquals(CUSTOMER, dao.getById(CUSTOMER.getId()).get());
}
@Test
public void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception {
boolean result = dao.delete(CUSTOMER);
assertTrue(result);
assertCustomerCountIs(0);
assertFalse(dao.getById(CUSTOMER.getId()).isPresent());
}
@Test
public void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws Exception {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
boolean result = dao.update(customer);
assertTrue(result);
final Customer cust = dao.getById(CUSTOMER.getId()).get();
assertEquals(newFirstname, cust.getFirstName());
assertEquals(newLastname, cust.getLastName());
}
@Test
public void retriveShouldReturnTheCustomer() {
Optional<Customer> 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 (Stream<Customer> allCustomers = dao.getAll()) {
assertTrue(allCustomers.count() == count);
}
}
}

View File

@ -50,6 +50,7 @@
<guava.version>19.0</guava.version>
<systemrules.version>1.15.1</systemrules.version>
<mockito.version>1.10.19</mockito.version>
<hierarchical-junit-runner-version>4.12.1</hierarchical-junit-runner-version>
</properties>
<modules>
<module>abstract-factory</module>
@ -196,6 +197,12 @@
<version>${systemrules.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.bechte.junit</groupId>
<artifactId>junit-hierarchicalcontextrunner</artifactId>
<version>${hierarchical-junit-runner-version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>