diff --git a/pom.xml b/pom.xml index 683e84ec..2af7ddfd 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,13 @@ https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml + + + com.mysql + mysql-connector-j + 8.3.0 + + diff --git a/src/main/java/mate/academy/Main.java b/src/main/java/mate/academy/Main.java index 0058fbf9..44b6697a 100644 --- a/src/main/java/mate/academy/Main.java +++ b/src/main/java/mate/academy/Main.java @@ -1,7 +1,38 @@ package mate.academy; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import mate.academy.bookdao.BookDao; +import mate.academy.lib.Injector; +import mate.academy.model.Book; + public class Main { + private static final Injector injector = Injector.getInstance("mate.academy"); + public static void main(String[] args) { + BookDao bookDao = (BookDao) injector.getInstance(BookDao.class); + Book firstBook = Book.of("Effective Java", new BigDecimal(700)); + Book secondBook = Book.of("Clean Code", new BigDecimal(900)); + Book thirdBook = Book.of("Head of Java", new BigDecimal(600)); + + bookDao.create(firstBook); + bookDao.create(secondBook); + bookDao.create(thirdBook); + + List booksFromDb = bookDao.findAll(); + System.out.println(booksFromDb); + + Optional bookById = bookDao.findById(3L); + bookById.ifPresent(System.out::println); + + Book book = Book.of(3L, "Harry Potter", new BigDecimal(1000)); + Book updated = bookDao.update(book); + System.out.println(updated); + + bookDao.findById(3L).ifPresent(System.out::println); + boolean isBookDeleted = bookDao.deleteById(3L); + System.out.println(isBookDeleted); } } diff --git a/src/main/java/mate/academy/bookdao/BookDao.java b/src/main/java/mate/academy/bookdao/BookDao.java new file mode 100644 index 00000000..158e2c0d --- /dev/null +++ b/src/main/java/mate/academy/bookdao/BookDao.java @@ -0,0 +1,18 @@ +package mate.academy.bookdao; + +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/bookdao/BookDaoImpl.java b/src/main/java/mate/academy/bookdao/BookDaoImpl.java new file mode 100644 index 00000000..ca24eda2 --- /dev/null +++ b/src/main/java/mate/academy/bookdao/BookDaoImpl.java @@ -0,0 +1,122 @@ +package mate.academy.bookdao; + +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.dbconnection.ConnectionUtil; +import mate.academy.exception.DataProcessingException; +import mate.academy.lib.Dao; +import mate.academy.model.Book; + +@Dao +public class BookDaoImpl implements BookDao { + @Override + public Book create(Book book) { + String sql = "INSERT INTO books(title, price) VALUES(?, ?)"; + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement preparedStatement = connection + .prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { + if (storeBook(preparedStatement, book, false)) { + throw new RuntimeException("Book could not be created"); + } + ResultSet generatedKeys = preparedStatement.getGeneratedKeys(); + if (generatedKeys.next()) { + Long id = generatedKeys.getObject(1, Long.class); + book.setId(id); + } + } catch (SQLException e) { + throw new DataProcessingException("Can't add a book " + book, e); + } + return book; + } + + @Override + public Optional findById(Long id) { + String sql = "SELECT * FROM books WHERE id = ?"; + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement preparedStatement = connection + .prepareStatement(sql)) { + preparedStatement.setLong(1, id); + ResultSet resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + return Optional.of(mapToBook(resultSet)); + } + } catch (SQLException e) { + throw new DataProcessingException("Can't get book by id " + id, e); + } + return Optional.empty(); + } + + @Override + public List findAll() { + String sql = "SELECT * FROM books"; + List books = new ArrayList<>(); + try (Connection connection = ConnectionUtil.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) { + while (resultSet.next()) { + books.add(mapToBook(resultSet)); + } + } catch (SQLException e) { + throw new DataProcessingException("Can't get all books", e); + } + return books; + } + + @Override + public Book update(Book book) { + String sql = "UPDATE books SET title = ?, price = ? WHERE id = ?"; + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement preparedStatement = connection + .prepareStatement(sql)) { + if (storeBook(preparedStatement, book, true)) { + throw new RuntimeException("Book not found"); + } + } catch (SQLException e) { + throw new DataProcessingException("Can't update book " + book.getTitle(), e); + } + return book; + } + + @Override + public boolean deleteById(Long id) { + String sql = "DELETE FROM books WHERE id = ?"; + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setLong(1, id); + int affectedRow = statement.executeUpdate(); + if (affectedRow < 1) { + throw new RuntimeException("Book not found by id " + id); + } + } catch (SQLException e) { + throw new DataProcessingException("Can't delete book by " + id, e); + } + return true; + } + + private Book mapToBook(ResultSet resultSet) throws SQLException { + Long id = resultSet.getLong("id"); + String title = resultSet.getString("title"); + BigDecimal price = resultSet.getBigDecimal("price"); + return Book.of(id, title, price); + } + + private boolean storeBook(PreparedStatement preparedStatement, + Book book, boolean idPresent) throws SQLException { + if (idPresent) { + preparedStatement.setString(1, book.getTitle()); + preparedStatement.setBigDecimal(2, book.getPrice()); + preparedStatement.setLong(3, book.getId()); + return preparedStatement.executeUpdate() < 1; + } + preparedStatement.setString(1, book.getTitle()); + preparedStatement.setBigDecimal(2, book.getPrice()); + return preparedStatement.executeUpdate() < 1; + } +} diff --git a/src/main/java/mate/academy/dbconnection/ConnectionUtil.java b/src/main/java/mate/academy/dbconnection/ConnectionUtil.java new file mode 100644 index 00000000..ebea8c28 --- /dev/null +++ b/src/main/java/mate/academy/dbconnection/ConnectionUtil.java @@ -0,0 +1,27 @@ +package mate.academy.dbconnection; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +public class ConnectionUtil { + private static final String DB_URL = "jdbc:mysql://localhost:3306/test"; + private static final Properties DB_PROPERTIES; + + static { + DB_PROPERTIES = new Properties(); + DB_PROPERTIES.put("user", "root"); + DB_PROPERTIES.put("password", "databasepractice1!"); + + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Can't load a JDBC driver!", e); + } + } + + public static Connection getConnection() throws SQLException { + return DriverManager.getConnection(DB_URL, DB_PROPERTIES); + } +} 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..63b937dc --- /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 cause) { + super(message, cause); + } +} 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..f3380f0f --- /dev/null +++ b/src/main/java/mate/academy/model/Book.java @@ -0,0 +1,65 @@ +package mate.academy.model; + +import java.math.BigDecimal; + +public class Book { + private Long id; + private String title; + private BigDecimal price; + + private Book(Long id, String title, BigDecimal price) { + this.id = id; + this.title = title; + this.price = price; + } + + private Book(String title, BigDecimal price) { + this.title = title; + this.price = price; + } + + public static Book of(Long id, String title, BigDecimal price) { + return new Book(id, title, price); + } + + public static Book of(String title, BigDecimal price) { + return new Book(title, price); + } + + public void setId(Long id) { + this.id = id; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + + public Long getId() { + return id; + } + + public String getTitle() { + return title; + } + + public BigDecimal getPrice() { + return price; + } + + @Override + public String toString() { + return "Book{" + + "id=" + + id + + ", title='" + + title + + '\'' + + ", price=" + + price + + '}'; + } +} diff --git a/src/main/resources/init_db.sql b/src/main/resources/init_db.sql new file mode 100644 index 00000000..adf7fe11 --- /dev/null +++ b/src/main/resources/init_db.sql @@ -0,0 +1,6 @@ +CREATE TABLE books +( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + title VARCHAR(100) NOT NULL, + price DECIMAL NOT NULL +); \ No newline at end of file