diff --git a/pom.xml b/pom.xml index 526fbcf3..f985b04f 100644 --- a/pom.xml +++ b/pom.xml @@ -14,4 +14,12 @@ UTF-8 + + + mysql + mysql-connector-java + 8.0.33 + + + diff --git a/src/main/java/mate/academy/Main.java b/src/main/java/mate/academy/Main.java index 0058fbf9..a416fb4d 100644 --- a/src/main/java/mate/academy/Main.java +++ b/src/main/java/mate/academy/Main.java @@ -1,7 +1,39 @@ package mate.academy; +import java.math.BigDecimal; +import mate.academy.dao.BookDao; +import mate.academy.dao.BookDaoImpl; +import mate.academy.lib.Injector; +import mate.academy.model.Book; + public class Main { public static void main(String[] args) { + Injector injector = Injector.getInstance("mate.academy"); + BookDao bookDao = (BookDaoImpl) injector.getInstance(BookDao.class); + + Book cleanCodeBook = new Book(); + cleanCodeBook.setTitle("Clean Code"); + cleanCodeBook.setPrice(BigDecimal.valueOf(29.99)); + bookDao.create(cleanCodeBook); + + Book colorOfMagicBook = new Book(); + colorOfMagicBook.setTitle("The color of magic"); + colorOfMagicBook.setPrice(BigDecimal.valueOf(69.99)); + bookDao.create(colorOfMagicBook); + + System.out.println(bookDao.findById(1L)); + + colorOfMagicBook.setPrice(BigDecimal.valueOf(6.99)); + bookDao.update(colorOfMagicBook); + + System.out.println(bookDao.findAll()); + + bookDao.deleteById(colorOfMagicBook.getId()); + bookDao.findAll().stream() + .peek(b -> { + b.setPrice(b.getPrice().multiply(BigDecimal.valueOf(0.9))); + bookDao.update(b); + }).forEach(System.out::println); } } diff --git a/src/main/java/mate/academy/dao/BookDao.java b/src/main/java/mate/academy/dao/BookDao.java new file mode 100644 index 00000000..d60c063d --- /dev/null +++ b/src/main/java/mate/academy/dao/BookDao.java @@ -0,0 +1,17 @@ +package mate.academy.dao; + +import java.util.List; +import java.util.Optional; +import mate.academy.model.Book; + +public interface BookDao { + Book create(Book book); + + Optional findById(Long id); + + List findAll(); + + Book update(Book book); + + boolean deleteById(Long id); +} diff --git a/src/main/java/mate/academy/dao/BookDaoImpl.java b/src/main/java/mate/academy/dao/BookDaoImpl.java new file mode 100644 index 00000000..45761f3b --- /dev/null +++ b/src/main/java/mate/academy/dao/BookDaoImpl.java @@ -0,0 +1,129 @@ +package mate.academy.dao; + +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import mate.academy.exception.DataProcessingException; +import mate.academy.lib.Dao; +import mate.academy.model.Book; +import mate.academy.util.ConnectionUtil; + +@Dao +public class BookDaoImpl implements BookDao { + private static final int ZERO_UPDATES = 0; + private static final int ID_INDEX = 1; + private static final int TITLE_INDEX = 2; + private static final int PRICE_INDEX = 3; + private static final int TITLE_INDEX_FOR_UPDATE_OPERATION = 1; + private static final int PRICE_INDEX_FOR_UPDATE_OPERATION = 2; + private static final int ID_INDEX_FOR_UPDATE_OPERATION = 3; + private static final String ID_COLUMN = "books.id"; + private static final String TITLE_COLUMN = "books.title"; + private static final String PRICE_COLUMN = "books.price"; + private static final String NO_UPDATES_EXCEPTION_TEXT = "Expected to update at least 1 value, but updated 0 values."; + private static final String CREATING_BOOK_EXCEPTION_TEXT = "Can't save a book "; + private static final String FINDING_BOOK_BY_ID_EXCEPTION_TEXT = "Can't get a book by id = "; + private static final String GETTING_LIST_OF_ALL_BOOKS_EXCEPTION_TEXT = "Can't get a book list from db"; + private static final String UPDATING_BOOK_EXCEPTION_TEXT = "Can't update book "; + private static final String DELETING_BOOK_BY_ID_EXCEPTION_TEXT = "Can't delete a book by id = "; + + @Override + public Book create(Book book) { + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement("INSERT INTO books (id, title, price) VALUES (?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) { + + statement.setLong(ID_INDEX, book.getId()); + statement.setString(TITLE_INDEX, book.getTitle()); + statement.setBigDecimal(PRICE_INDEX, book.getPrice()); + + if (statement.executeUpdate() == ZERO_UPDATES) { + throw new RuntimeException(NO_UPDATES_EXCEPTION_TEXT); + } + ResultSet generatedKeys = statement.getGeneratedKeys(); + if (generatedKeys.next()) { + Integer id = generatedKeys.getObject(ID_INDEX, Integer.class); + book.setId(id); + } + } catch (SQLException e) { + throw new DataProcessingException(CREATING_BOOK_EXCEPTION_TEXT + book, e); + } + return book; + } + + @Override + public Optional findById(Long id) { + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT * FROM books WHERE id = ?")) { + + statement.setLong(ID_INDEX, id); + ResultSet resultSet = statement.executeQuery(); + if (resultSet.next()) { + return Optional.of(parseBookFromQuery(resultSet)); + } + return Optional.empty(); + } catch (SQLException e) { + throw new DataProcessingException(FINDING_BOOK_BY_ID_EXCEPTION_TEXT + id, e); + } + } + + @Override + public List findAll() { + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT * FROM books")) { + + ResultSet resultSet = statement.executeQuery(); + List books = new ArrayList<>(); + while (resultSet.next()) { + books.add(parseBookFromQuery(resultSet)); + } + return books; + } catch (SQLException e) { + throw new DataProcessingException(GETTING_LIST_OF_ALL_BOOKS_EXCEPTION_TEXT, e); + } + } + + @Override + public Book update(Book book) { + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement("UPDATE books SET title = ?, price = ? WHERE id = ?")) { + + statement.setString(TITLE_INDEX_FOR_UPDATE_OPERATION, book.getTitle()); + statement.setBigDecimal(PRICE_INDEX_FOR_UPDATE_OPERATION, book.getPrice()); + statement.setLong(ID_INDEX_FOR_UPDATE_OPERATION, book.getId()); + + if (statement.executeUpdate() == ZERO_UPDATES) { + throw new RuntimeException(NO_UPDATES_EXCEPTION_TEXT); + } + } catch (SQLException e) { + throw new DataProcessingException(UPDATING_BOOK_EXCEPTION_TEXT + book, e); + } + return null; + } + + @Override + public boolean deleteById(Long id) { + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement("DELETE FROM books WHERE id = ?")) { + + statement.setLong(ID_INDEX, id); + + return statement.executeUpdate() > ZERO_UPDATES; + } catch (SQLException e) { + throw new DataProcessingException(DELETING_BOOK_BY_ID_EXCEPTION_TEXT + id, e); + } + } + + private Book parseBookFromQuery(ResultSet resultSet) throws SQLException{ + Book book = new Book(); + book.setId(resultSet.getObject(ID_COLUMN, Long.class)); + book.setTitle(resultSet.getObject(TITLE_COLUMN, String.class)); + book.setPrice(resultSet.getObject(PRICE_COLUMN, BigDecimal.class)); + return book; + } +} diff --git a/src/main/java/mate/academy/exception/DataProcessingException.java b/src/main/java/mate/academy/exception/DataProcessingException.java new file mode 100644 index 00000000..27877dca --- /dev/null +++ b/src/main/java/mate/academy/exception/DataProcessingException.java @@ -0,0 +1,7 @@ +package mate.academy.exception; + +public class DataProcessingException extends RuntimeException{ + public DataProcessingException(String message, Throwable ex) { + super(message, ex); + } +} diff --git a/src/main/java/mate/academy/model/Book.java b/src/main/java/mate/academy/model/Book.java new file mode 100644 index 00000000..619de082 --- /dev/null +++ b/src/main/java/mate/academy/model/Book.java @@ -0,0 +1,42 @@ +package mate.academy.model; + +import java.math.BigDecimal; + +public class Book { + private long id; + private String title; + private BigDecimal price; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + + @Override + public String toString() { + return "Book{" + + "id=" + id + + ", title='" + title + '\'' + + ", price=" + price + + '}'; + } +} diff --git a/src/main/java/mate/academy/util/ConnectionUtil.java b/src/main/java/mate/academy/util/ConnectionUtil.java new file mode 100644 index 00000000..6205bab9 --- /dev/null +++ b/src/main/java/mate/academy/util/ConnectionUtil.java @@ -0,0 +1,26 @@ +package mate.academy.util; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +public class ConnectionUtil { + public static final String DB_URL = "jdbc:mysql://localhost:3306/book_schema"; + public static final Properties DB_PROPERTIES; + static { + DB_PROPERTIES = new Properties(); + DB_PROPERTIES.put("user", "root"); + DB_PROPERTIES.put("password", "qwerty123"); + + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Failed to load JDBC driver", e); + } + } + + public static Connection getConnection() throws SQLException { + return DriverManager.getConnection(DB_URL, DB_PROPERTIES); + } +} diff --git a/src/main/resources/init_db.sql b/src/main/resources/init_db.sql new file mode 100644 index 00000000..c7a89b5f --- /dev/null +++ b/src/main/resources/init_db.sql @@ -0,0 +1,7 @@ +CREATE DATABASE book_schema; +USE book_schema; +CREATE TABLE books ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `title` VARCHAR(255), + `price` DECIMAL, + PRIMARY KEY (`id`));