Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Home work #405

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml
</maven.checkstyle.plugin.configLocation>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>

<build>
<plugins>
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/mate/academy/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,48 @@
package mate.academy;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
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);

// Initialize data

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comments please

List<Book> books = initializeBooks();
books.forEach(book -> System.out.println("Created record in DB: " + bookDao.create(book)));

// Fetch all books once and reuse the list
List<Book> allBooks = bookDao.findAll();
System.out.println(System.lineSeparator() + "Records in DB:");
allBooks.forEach(System.out::println);

// Update a specific book
allBooks.stream()
.filter(b -> Objects.equals(b.getTitle(), "Effective Java"))
.findFirst()
.ifPresent(updatedBook -> {
updatedBook.setTitle("SQL in 10 Minutes");
updatedBook.setPrice(BigDecimal.valueOf(24.99));
System.out.println(System.lineSeparator()
+ "Updated in db: " + bookDao.update(updatedBook));
});

// Delete all books
allBooks.forEach(book -> System.out.println("Deleted record from DB: "
+ bookDao.deleteById(book.getId())));
}

private static List<Book> initializeBooks() {
return List.of(
new Book("Core Java Volume I – Fundamentals", BigDecimal.valueOf(29.99)),
new Book("Effective Java", BigDecimal.valueOf(19.99)),
new Book("Java - The Complete Reference", BigDecimal.valueOf(35.45))
);
}
}
17 changes: 17 additions & 0 deletions src/main/java/mate/academy/dao/BookDao.java
Original file line number Diff line number Diff line change
@@ -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<Book> findById(Long id);

List<Book> findAll();

Book update(Book book);

boolean deleteById(Long id);
}
107 changes: 107 additions & 0 deletions src/main/java/mate/academy/dao/BookDaoImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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 {
@Override
public Book create(Book book) {
String query = "INSERT INTO books (title, price) "
+ "VALUES (?, ?)";
try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection
.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1, book.getTitle());
statement.setBigDecimal(2, book.getPrice());
statement.executeUpdate();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we check if rows were updated?

ResultSet resultSet = statement.getGeneratedKeys();
if (resultSet.next()) {
book.setId(resultSet.getObject(1, Long.class));
}
return book;
} catch (SQLException e) {
throw new DataProcessingException("Couldn't create book: " + book, e);
}
}

@Override
public Optional<Book> findById(Long id) {
String query = "SELECT id, title, price FROM books WHERE id = ?;";
try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(query)) {
statement.setLong(1, id);
ResultSet resultSet = statement.executeQuery();
Book book = null;
if (resultSet.next()) {
book = getBook(resultSet);
return Optional.of(book);
}
} catch (SQLException e) {
throw new DataProcessingException("Couldn't get book by id: " + id, e);
}
return Optional.empty();
}

@Override
public List<Book> findAll() {
String query = "SELECT id, title, price FROM books";
try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(query)) {
List<Book> books = new ArrayList<>();
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
books.add(getBook(resultSet));
}
return books;
} catch (SQLException e) {
throw new DataProcessingException("Couldn't get a list of books from books table.", e);
}
}

@Override
public Book update(Book book) {
String query = "UPDATE books SET title = ?, price = ? WHERE id= ?;";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to use uppercase for SQL keywords in your queries for consistency and readability. The update query should have UPDATE, SET, and WHERE in uppercase.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, book.getTitle());
statement.setBigDecimal(2, book.getPrice());
statement.setLong(3, book.getId());
statement.executeUpdate();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we check if rows were updated?

return book;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should execute statement.executeUpdate() in the update method to actually perform the update operation on the database. Without this call, the update will not be applied.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are not closing the PreparedStatement in the update method. Use try-with-resources or explicitly close the statement to avoid potential resource leaks.

} catch (SQLException e) {
throw new DataProcessingException("Couldn't update a book: "
+ book, e);
}
}

@Override
public boolean deleteById(Long id) {
String query = "DELETE FROM books WHERE id = ?";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to use uppercase for SQL keywords in your queries for consistency and readability. The delete query should have DELETE and WHERE in uppercase.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(query)) {
statement.setLong(1, id);
return statement.executeUpdate() > 0;
} catch (SQLException e) {
throw new DataProcessingException("Couldn't delete a book by id " + id, e);
}
}

private Book getBook(ResultSet resultSet) throws SQLException {
Long id = resultSet.getObject("id", Long.class);
String title = resultSet.getString("title");
BigDecimal price = resultSet.getBigDecimal("price");
return new Book(id, title, price);
Comment on lines +107 to +111

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid code duplication when converting ResultSet to Book, it's good that you have moved this logic into a separate private method getBook. This adheres to the checklist item about avoiding code duplication.

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mate.academy.exception;

public class DataProcessingException extends RuntimeException {
public DataProcessingException(String message, Throwable cause) {
super(message, cause);
}
}
76 changes: 76 additions & 0 deletions src/main/java/mate/academy/model/Book.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package mate.academy.model;

import java.math.BigDecimal;
import java.util.Objects;

public class Book {
private Long id;
private String title;
private BigDecimal price;

public Book() {
}

public Book(String title, BigDecimal price) {
this.title = title;
this.price = price;
}

public Book(Long id, String title, BigDecimal price) {
this.id = id;
this.title = title;
this.price = 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 boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Book book = (Book) o;
return Objects.equals(id, book.id)
&& Objects.equals(title, book.title)
&& Objects.equals(price, book.price);
}

@Override
public int hashCode() {
return Objects.hash(id, title, price);
}

@Override
public String toString() {
return "Book{"
+ "id=" + id
+ ", title='" + title + '\''
+ ", price=" + price
+ '}';
}
}
28 changes: 28 additions & 0 deletions src/main/java/mate/academy/util/ConnectionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package mate.academy.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class ConnectionUtil {
private static final String URL = "jdbc:mysql://localhost:3306/library_service";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL for the JDBC connection is incomplete. It should specify the database you're connecting to, for example, 'jdbc:mysql://localhost:3306/myDatabase'.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using hard-coded database credentials in your code. Consider using environment variables or configuration files to manage sensitive information securely.

private static final String USERNAME = "root";
private static final String PASSWORD = "root";
private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";

static {
try {
Class.forName(JDBC_DRIVER);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Can't find SQL Driver", e);
}
}

public static Connection getConnection() throws SQLException {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good practice to handle SQLException inside the getConnection method rather than throwing it, so you can provide more informative error messages and handle specific cases if necessary.

Properties dbProperties = new Properties();
dbProperties.setProperty("user", USERNAME);
dbProperties.setProperty("password", PASSWORD);
return DriverManager.getConnection(URL, dbProperties);
}
}
Empty file added src/main/resources/init_db.sql
Empty file.
Loading