diff --git a/pom.xml b/pom.xml index 683e84ec..067e5f75 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +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..c53a7e16 100644 --- a/src/main/java/mate/academy/Main.java +++ b/src/main/java/mate/academy/Main.java @@ -1,7 +1,34 @@ package mate.academy; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import mate.academy.dao.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 book = new Book(); + book.setTitle("Dune"); + book.setPrice(BigDecimal.valueOf(19.99)); + bookDao.create(book); + + // Testing other methods + Optional foundBook = bookDao.findById(book.getId()); + foundBook.ifPresent(System.out::println); + + List allBooks = bookDao.findAll(); + System.out.println("All Books: " + allBooks); + + book.setTitle("Lisan al gaib"); + bookDao.update(book); + System.out.println("Updated Book: " + book); + boolean deleted = bookDao.deleteById(book.getId()); + System.out.println("Deleted: " + deleted); } } diff --git a/src/main/java/mate/academy/connection/ConnectionUtil.java b/src/main/java/mate/academy/connection/ConnectionUtil.java new file mode 100644 index 00000000..11f906a3 --- /dev/null +++ b/src/main/java/mate/academy/connection/ConnectionUtil.java @@ -0,0 +1,35 @@ +package mate.academy.connection; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; +import mate.academy.exception.DataProcessingException; + +public class ConnectionUtil { + private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; + private static final String DB_PATH = "jdbc:mysql://localhost:3306/jdbc"; + private static final String USER = "root"; + private static final String PASSWORD = "MySQL1234"; + private static final Properties PROPERTIES; + + static { + PROPERTIES = new Properties(); + PROPERTIES.put("user", USER); + PROPERTIES.put("password", PASSWORD); + + try { + Class.forName(DRIVER); + } catch (ClassNotFoundException e) { + throw new DataProcessingException("Something wrong with Driver", e); + } + } + + public static Connection getConnection() { + try { + return DriverManager.getConnection(DB_PATH, PROPERTIES); + } catch (SQLException e) { + throw new DataProcessingException("Failed to establish database connection", e); + } + } +} 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..9fc4cce4 --- /dev/null +++ b/src/main/java/mate/academy/dao/BookDao.java @@ -0,0 +1,18 @@ +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..705f3baf --- /dev/null +++ b/src/main/java/mate/academy/dao/BookDaoImpl.java @@ -0,0 +1,138 @@ +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.connection.ConnectionUtil; +import mate.academy.exception.DataProcessingException; +import mate.academy.lib.Dao; +import mate.academy.model.Book; + +@Dao +public class BookDaoImpl implements BookDao { + private static final int COLUMN_TITLE = 1; + private static final int COLUMN_PRICE = 2; + private static final int COLUMN_ID = 3; + private static final String ID = "id"; + private static final String TITLE = "title"; + private static final String PRICE = "price"; + + @Override + public Book create(Book book) { + String sql = "INSERT INTO books (title, price) VALUES (?, ?)"; + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection + .prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) + ) { + statement.setString(COLUMN_TITLE, book.getTitle()); + statement.setBigDecimal(COLUMN_PRICE, book.getPrice()); + + int affectedRows = statement.executeUpdate(); + isUpdated(affectedRows, book); + ResultSet generatedKeys = statement.getGeneratedKeys(); + if (generatedKeys.next()) { + Long id = generatedKeys.getObject(COLUMN_TITLE, Long.class); + book.setId(id); + } + } catch (SQLException e) { + throw new DataProcessingException("Can't save new book: " + book, e); + } + return book; + } + + @Override + public Optional findById(Long id) { + Book book = new Book(); + String sql = "SELECT * FROM books WHERE id = ?"; + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setLong(COLUMN_TITLE, id); + ResultSet resultSet = statement.executeQuery(); + if (resultSet.next()) { + mapToBook(resultSet); + } + } catch (SQLException e) { + throw new DataProcessingException("Can't get a book by id " + id, e); + } + return Optional.of(book); + } + + @Override + public List findAll() { + List books = new ArrayList<>(); + String sql = "SELECT * FROM books"; + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement(sql)) { + ResultSet resultSet = statement.executeQuery(); + while (resultSet.next()) { + Book book = new Book(); + book = mapToBook(resultSet); + books.add(book); + } + } catch (SQLException e) { + throw new DataProcessingException("Can't fetch 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 statement = connection.prepareStatement(sql)) { + statement.setString(COLUMN_TITLE, book.getTitle()); + statement.setBigDecimal(COLUMN_PRICE, book.getPrice()); + statement.setLong(COLUMN_ID, book.getId()); + + int affectedRows = statement.executeUpdate(); + isUpdated(affectedRows, book); + } catch (SQLException e) { + throw new DataProcessingException("Can't update a book: " + book, e); + } + return book; + } + + @Override + public boolean deleteById(Long id) { + String sql = "DELETE FROM books WHERE id = ?"; + int affectedRows; + try (Connection connection = ConnectionUtil.getConnection(); + PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setLong(COLUMN_TITLE, id); + affectedRows = statement.executeUpdate(); + } catch (SQLException e) { + throw new DataProcessingException("Can't delete a book with id: " + id, e); + } + return affectedRows > 0; + } + + private boolean isUpdated(int affectedRows, Book book) { + if (affectedRows < 1) { + throw new DataProcessingException( + "Expected to update at least one row, but updated nothing: " + + book); + } + return true; + } + + private Book mapToBook(ResultSet resultSet) { + Book book = new Book(); + try { + long columnId = resultSet.getObject(ID, Long.class); + Object columnPrice = resultSet.getObject(PRICE); + String columnTitle = resultSet.getString(TITLE); + book.setPrice((BigDecimal) columnPrice); + book.setTitle(columnTitle); + book.setId(columnId); + } catch (SQLException e) { + throw new DataProcessingException("Can`t get result from DB"); + } + 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..02f156f9 --- /dev/null +++ b/src/main/java/mate/academy/exception/DataProcessingException.java @@ -0,0 +1,11 @@ +package mate.academy.exception; + +public class DataProcessingException extends RuntimeException { + public DataProcessingException(String message, Throwable ex) { + super(message, ex); + } + + public DataProcessingException(String message) { + super(message); + } +} 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..1cab86c3 --- /dev/null +++ b/src/main/java/mate/academy/model/Book.java @@ -0,0 +1,41 @@ +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/resources/init_db.sql b/src/main/resources/init_db.sql new file mode 100644 index 00000000..1b53caa3 --- /dev/null +++ b/src/main/resources/init_db.sql @@ -0,0 +1,6 @@ +USE jdbc; +CREATE TABLE books ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255), + price DECIMAL(10,2) +);