Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Completed solution #2

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.bobocode;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
* {@link AccountDbInitializer} provides an API that allow to initialize (create) an Account table in the database
Expand All @@ -28,6 +30,20 @@ public AccountDbInitializer(DataSource dataSource) {
* @throws SQLException
*/
public void init() throws SQLException {
throw new UnsupportedOperationException("It's your job to make it work!"); // todo
try (Connection connection = dataSource.getConnection()) {
Statement statement = connection.createStatement();
statement.execute("CREATE TABLE account(" +
"id BIGINT," +
" email VARCHAR(255) NOT NULL," +
" first_name VARCHAR(255) NOT NULL," +
" last_name VARCHAR(255) NOT NULL," +
" gender VARCHAR(255) NOT NULL," +
" birthday DATE NOT NULL," +
" balance DECIMAL(19,4)," +
" creation_time TIMESTAMP NOT NULL DEFAULT now()," +
" CONSTRAINT account_pk PRIMARY KEY (id)," +
" CONSTRAINT account_email_uq UNIQUE (email)" +
");");
}
}
}
179 changes: 174 additions & 5 deletions product-dao/src/main/java/com/bobocode/dao/ProductDaoImpl.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package com.bobocode.dao;

import com.bobocode.exception.DaoOperationException;
import com.bobocode.model.Product;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class ProductDaoImpl implements ProductDao {
private static final String INSERT_SQL = "INSERT INTO products(name, producer, price, expiration_date) VALUES (?, ?, ?, ?);";
private static final String SELECT_ALL_SQL = "SELECT * FROM products;";
private static final String SELECT_BY_ID_SQL = "SELECT * FROM products WHERE id = ?;";
private static final String UPDATE_BY_ID_SLQ = "UPDATE products SET name = ?, producer = ?, price = ?, expiration_date = ? WHERE id = ?;";
private static final String REMOVE_BY_ID_SQL = "DELETE FROM products WHERE id = ?;";

private DataSource dataSource;

public ProductDaoImpl(DataSource dataSource) {
Expand All @@ -14,27 +24,186 @@ public ProductDaoImpl(DataSource dataSource) {

@Override
public void save(Product product) {
throw new UnsupportedOperationException("None of these methods will work unless you implement them!");// todo
Objects.requireNonNull(product);
try (Connection connection = dataSource.getConnection()) { // try-with-resources will automatically close connection
saveProduct(product, connection);
} catch (SQLException e) {
throw new DaoOperationException(String.format("Error saving product: %s", product), e);
}
}

private void saveProduct(Product product, Connection connection) throws SQLException {
PreparedStatement insertStatement = prepareInsertStatement(product, connection);
insertStatement.executeUpdate();
Long id = fetchGeneratedId(insertStatement);
product.setId(id);
}

private PreparedStatement prepareInsertStatement(Product product, Connection connection) {
try {
PreparedStatement insertStatement = connection.prepareStatement(INSERT_SQL,
PreparedStatement.RETURN_GENERATED_KEYS); // this parameter will configure query to ask db for a generated keys
fillProductStatement(product, insertStatement);
return insertStatement;
} catch (SQLException e) {
throw new DaoOperationException(String.format("Cannot prepare statement for product: %s", product), e);
}
}

private void fillProductStatement(Product product, PreparedStatement updateStatement) throws SQLException {
updateStatement.setString(1, product.getName());
updateStatement.setString(2, product.getProducer());
updateStatement.setBigDecimal(3, product.getPrice());
updateStatement.setDate(4, Date.valueOf(product.getExpirationDate()));
}

private Long fetchGeneratedId(PreparedStatement insertStatement) throws SQLException {
// this method allows to retrieve IDs that were generated by the database during insert statement
ResultSet generatedKeys = insertStatement.getGeneratedKeys();
if (generatedKeys.next()) { // you need to call next() because cursor is located before the first row
return generatedKeys.getLong(1);
} else { // if next() returned false it means that database didn't return any IDs
throw new DaoOperationException("Can not obtain product ID");
}
}

@Override
public List<Product> findAll() {
throw new UnsupportedOperationException("None of these methods will work unless you implement them!");// todo
try (Connection connection = dataSource.getConnection()) {
return findAllProducts(connection);
} catch (SQLException e) {
throw new DaoOperationException("Error finding all products", e);
}
}

private List<Product> findAllProducts(Connection connection) throws SQLException {
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(SELECT_ALL_SQL);
return collectToList(resultSet);
}

private List<Product> collectToList(ResultSet resultSet) throws SQLException {
List<Product> products = new ArrayList<>();
while (resultSet.next()) {
Product product = parseRow(resultSet);
products.add(product);
}
return products;
}

private Product parseRow(ResultSet resultSet) {
try {
return createFromResultSet(resultSet);
} catch (SQLException e) {
throw new DaoOperationException("Cannot parse row to create product instance", e);
}
}

private Product createFromResultSet(ResultSet resultSet) throws SQLException {
Product product = new Product();
product.setId(resultSet.getLong("id"));
product.setName(resultSet.getString("name"));
product.setProducer(resultSet.getString("producer"));
product.setPrice(resultSet.getBigDecimal("price"));
product.setExpirationDate(resultSet.getDate("expiration_date").toLocalDate());
product.setCreationTime(resultSet.getTimestamp("creation_time").toLocalDateTime());
return product;
}

@Override
public Product findOne(Long id) {
throw new UnsupportedOperationException("None of these methods will work unless you implement them!");// todo
Objects.requireNonNull(id);
try (Connection connection = dataSource.getConnection()) {
return findProductById(id, connection);
} catch (SQLException e) {
throw new DaoOperationException(String.format("Error finding product by id = %d", id), e);
}
}

private Product findProductById(Long id, Connection connection) throws SQLException {
PreparedStatement selectByIdStatement = prepareSelectByIdStatement(id, connection);
ResultSet resultSet = selectByIdStatement.executeQuery();
if (resultSet.next()) {// we need to call next() since cursor is located before the first line
return parseRow(resultSet);
} else { // if next() returned false it means that database returned an empty response
throw new DaoOperationException(String.format("Product with id = %d does not exist", id));
}
}

private PreparedStatement prepareSelectByIdStatement(Long id, Connection connection) {
try {
PreparedStatement selectByIdStatement = connection.prepareStatement(SELECT_BY_ID_SQL);
selectByIdStatement.setLong(1, id);
return selectByIdStatement;
} catch (SQLException e) {
throw new DaoOperationException(String.format("Cannot prepare select by id statement for id = %d", id), e);
}
}

@Override
public void update(Product product) {
throw new UnsupportedOperationException("None of these methods will work unless you implement them!");// todo
Objects.requireNonNull(product);
try (Connection connection = dataSource.getConnection()) {
updateProduct(product, connection);
} catch (SQLException e) {
throw new DaoOperationException(String.format("Error updating product: %s", product), e);
}
}

private void updateProduct(Product product, Connection connection) throws SQLException {
checkIdIsNotNull(product);
PreparedStatement updateStatement = prepareUpdateStatement(product, connection);
executeUpdateById(updateStatement, product.getId());
}

private void executeUpdateById(PreparedStatement insertStatement, Long productId) throws SQLException {
int rowsAffected = insertStatement.executeUpdate(); // returns number of rows that were changed
if (rowsAffected == 0) {
throw new DaoOperationException(String.format("Product with id = %d does not exist", productId));
}
}

private PreparedStatement prepareUpdateStatement(Product product, Connection connection) {
try {
PreparedStatement updateStatement = connection.prepareStatement(UPDATE_BY_ID_SLQ);
fillProductStatement(product, updateStatement);
updateStatement.setLong(5, product.getId());
return updateStatement;
} catch (Exception e) {
throw new DaoOperationException(String.format("Cannot prepare update statement for product: %s", product), e);
}
}

@Override
public void remove(Product product) {
throw new UnsupportedOperationException("None of these methods will work unless you implement them!");// todo
Objects.requireNonNull(product);
try (Connection connection = dataSource.getConnection()) {
removeProduct(product, connection);
} catch (SQLException e) {
throw new DaoOperationException(String.format("Error removing product by id = %d", product.getId()), e);
}
}

private void removeProduct(Product product, Connection connection) throws SQLException {
checkIdIsNotNull(product);
PreparedStatement removeStatement = prepareRemoveStatement(product, connection);
executeUpdateById(removeStatement, product.getId());
}

private PreparedStatement prepareRemoveStatement(Product product, Connection connection) {
try {
PreparedStatement removeStatement = connection.prepareStatement(REMOVE_BY_ID_SQL);
removeStatement.setLong(1, product.getId());
return removeStatement;
} catch (SQLException e) {
throw new DaoOperationException(String.format("Cannot prepare statement for product: %s", product), e);
}
}

private void checkIdIsNotNull(Product product) {
if (product.getId() == null) {
throw new DaoOperationException("Product id cannot be null");
}
}
}

4 changes: 2 additions & 2 deletions product-dao/src/test/java/com/bobocode/ProductDaoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ void testUpdateNotStored() {
fail("Exception was't thrown");
} catch (Exception e) {
assertEquals(DaoOperationException.class, e.getClass());
assertEquals("Cannot find a product without ID", e.getMessage());
assertEquals("Product id cannot be null", e.getMessage());
}
}

Expand Down Expand Up @@ -256,7 +256,7 @@ void testRemoveNotStored() {
fail("Exception was't thrown");
} catch (Exception e) {
assertEquals(DaoOperationException.class, e.getClass());
assertEquals("Cannot find a product without ID", e.getMessage());
assertEquals("Product id cannot be null", e.getMessage());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,23 @@ should have a column that stores primary key from a parent table, which is a for

*/

-- TODO: implement the SQL according to the description
CREATE TABLE IF NOT EXISTS users (
id BIGINT,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
birthday DATE NOT NULL,
CONSTRAINT users_PK PRIMARY KEY (id),
CONSTRAINT users_email_AK UNIQUE (email)
);

CREATE TABLE IF NOT EXISTS profiles (
user_id BIGINT,
city VARCHAR(255),
job_position VARCHAR(255),
company VARCHAR(255),
education VARCHAR(255),
CONSTRAINT profiles_PK PRIMARY KEY (user_id),
CONSTRAINT profiles_users_FK FOREIGN KEY (user_id) REFERENCES users
);

Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,31 @@ A sales group can consists of more than one broker, while each broker can be ass

*/

-- TODO: write SQL script to create a database tables according to the requirements
CREATE TABLE IF NOT EXISTS broker (
id BIGINT,
username VARCHAR(255) NOT NULL,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
CONSTRAINT PK_broker PRIMARY KEY (id),
CONSTRAINT UQ_broker_username UNIQUE (username)
);


CREATE TABLE IF NOT EXISTS sales_group (
id BIGINT,
name VARCHAR(255) NOT NULL,
transaction_type VARCHAR(255) NOT NULL,
max_transaction_amount INT NOT NULL,
CONSTRAINT PK_sales_group PRIMARY KEY (id),
CONSTRAINT UQ_sales_group_name UNIQUE (name)
);


CREATE TABLE IF NOT EXISTS broker_sales_group (
broker_id BIGINT NOT NULL,
sales_group_id BIGINT NOT NULL,
CONSTRAINT PK_broker_sales_group PRIMARY KEY (broker_id, sales_group_id),
CONSTRAINT FK_broker_sales_group_broker FOREIGN KEY (broker_id) REFERENCES broker,
CONSTRAINT FK_broker_sales_group_sales_group FOREIGN KEY (sales_group_id) REFERENCES sales_group
);