Skip to content
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
Expand Up @@ -22,4 +22,24 @@ 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,30 @@ 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
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.bobocode.util.ExerciseNotCompletedException;

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 @@ -30,6 +32,20 @@ public AccountDbInitializer(DataSource dataSource) {
* @throws SQLException
*/
public void init() throws SQLException {
throw new ExerciseNotCompletedException(); // todo
try (var connection = dataSource.getConnection()) {
var 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)" +
");");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
package com.bobocode.dao;

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

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

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 final DataSource dataSource;

public ProductDaoImpl(DataSource dataSource) {
Expand All @@ -15,27 +26,185 @@ public ProductDaoImpl(DataSource dataSource) {

@Override
public void save(Product product) {
throw new ExerciseNotCompletedException();// todo
Objects.requireNonNull(product);
try (var connection = dataSource.getConnection()) {
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 {
var insertStatement = prepareInsertStatement(product, connection);
insertStatement.executeUpdate();
product.setId(fetchGeneratedId(insertStatement));
}

private PreparedStatement prepareInsertStatement(Product product, Connection connection) {
try {
var 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 ExerciseNotCompletedException();// 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 {
var statement = connection.createStatement();
var resultSet = statement.executeQuery(SELECT_ALL_SQL);
return collectToList(resultSet);
}

private List<Product> collectToList(ResultSet resultSet) throws SQLException {
var products = new ArrayList<Product>();
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 {
var 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 ExerciseNotCompletedException();// todo
Objects.requireNonNull(id);
try (var 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 {
var selectByIdStatement = prepareSelectByIdStatement(id, connection);
var 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 {
var 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 ExerciseNotCompletedException();// 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);
var updateStatement = prepareUpdateStatement(product, connection);
executeUpdateById(updateStatement, product.getId());
}

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

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 {
var 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 ExerciseNotCompletedException();// todo
Objects.requireNonNull(product);
try (var 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);
var removeStatement = prepareRemoveStatement(product, connection);
executeUpdateById(removeStatement, product.getId());
}

private PreparedStatement prepareRemoveStatement(Product product, Connection connection) {
try {
var 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);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bobocode.model;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand All @@ -15,12 +16,19 @@
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "movie")
public class Movie {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "name", nullable = false)
private String name;

@Column(name = "director", nullable = false)
private String director;

@Column(name = "duration")
private Integer durationSeconds;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
<persistence-unit name="TuttiFrutti">
<class>com.bobocode.model.Song</class>

<!--todo: configure a persistence unit name: TuttiFrutti-->
<!--todo: add class Song to this persistence unit-->
<!--todo: configure a hibernate connection url for in-memory H2 database-->
<!--database name: tutti_frutti_db-->
<!--database additional properties: DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false-->
<!--todo: configure a hibernate connection username: little_richard-->
<!--todo: configure a hibernate connection password: rock_n_roll_is_alive-->
<!--todo: configure a hibernate SQL dialect for H2 database-->
<!--todo: configure hibernate generate DDL and create database-->

<properties>
<property name="hibernate.connection.url" value="jdbc:h2:mem:tutti_frutti_db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.username" value="little_richard"/>
<property name="hibernate.connection.password" value="rock_n_roll_is_alive"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ public QueryHelper(EntityManagerFactory entityManagerFactory) {
* @return query result specified by type T
*/
public <T> T readWithinTx(Function<EntityManager, T> entityManagerConsumer) {
throw new ExerciseNotCompletedException(); // todo:
var entityManager = entityManagerFactory.createEntityManager();
entityManager.unwrap(Session.class).setDefaultReadOnly(true);
entityManager.getTransaction().begin();
try {
T result = entityManagerConsumer.apply(entityManager);
entityManager.getTransaction().commit();
return result;
} catch (Exception e) {
entityManager.getTransaction().rollback();
throw new QueryHelperException("Transaction is rolled back.", e);
} finally {
entityManager.close();
}
}
}
Loading