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

hw is done, please check #420

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
</maven.checkstyle.plugin.configLocation>
</properties>

<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/mate/academy/ConnectionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package mate.academy;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class ConnectionUtil {
private static String password;
private static final String DB_URL = "jdbc:mysql://localhost:3306/jdbc_intro";

Choose a reason for hiding this comment

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

The DB_URL string is not complete, it should contain the full JDBC URL, including the database server's IP, port, and the specific database name you want to connect to.

Choose a reason for hiding this comment

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

The DB_URL seems to be incomplete. Make sure to provide the full database URL including the database name, parameters, and avoid using schema names directly in the URL as per the checklist.

private static final String USERNAME = "root";
private static final String PASSWORD_PATH = "src/main/resources/password.txt";
private static final Properties DB_PROPERTIES;

static {
try (BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(PASSWORD_PATH))) {
password = bufferedReader.readLine();
} catch (IOException e) {
throw new RuntimeException("Can't read file", e);

Choose a reason for hiding this comment

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

Consider using a more informative message for the exception that includes the path to the file that couldn't be read.

}
DB_PROPERTIES = new Properties();
DB_PROPERTIES.put("user", USERNAME);
DB_PROPERTIES.put("password", password);

try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException("Can not load JDBC driver", e);

Choose a reason for hiding this comment

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

For the exception message, include the name of the JDBC driver that could not be loaded to provide more context.

}
}

public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(DB_URL, DB_PROPERTIES);
}
}
28 changes: 28 additions & 0 deletions src/main/java/mate/academy/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
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.getDaoInstance(BookDao.class);

Book book = new Book();
book.setTitle("NewTitle");
book.setPrice(BigDecimal.valueOf(40));
Book bookUpdated = bookDao.create(book);

Choose a reason for hiding this comment

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

Ensure that the 'create' method in 'BookDao' uses 'Statement.RETURN_GENERATED_KEYS' to retrieve and set the generated id for the book. This is crucial for the subsequent findById operation to work correctly.

System.out.println("Created: " + bookUpdated);

Optional<Book> findById = bookDao.findById(book.getId());

Choose a reason for hiding this comment

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

When you retrieve a book using 'findById', it's good practice to check if the book is present before trying to use it. Use 'findById.isPresent()' to check if the book was found.

System.out.println("Found: " + findById);

List<Book> list = bookDao.findAll();
System.out.println("All lines: " + list);

book.setTitle("Updated title");
bookDao.update(book);
System.out.println("Updated: " + book);

boolean deleted = bookDao.deleteById(book.getId());

Choose a reason for hiding this comment

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

Verify that the 'deleteById' method in 'BookDao' returns a boolean value depending on the result of 'preparedStatement.executeUpdate()'. It should not always return 'true'.

System.out.println("Deleted: " + deleted);
}
}
19 changes: 19 additions & 0 deletions src/main/java/mate/academy/dao/BookDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package mate.academy.dao;

import java.util.List;
import java.util.Optional;
import mate.academy.lib.Dao;
import mate.academy.model.Book;

@Dao
public interface BookDao {
Book create(Book book);

Optional<Book> findById(Long id);

List<Book> findAll();

Book update(Book book);

boolean deleteById(Long id);
}
113 changes: 113 additions & 0 deletions src/main/java/mate/academy/dao/impl/BookDaoImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package mate.academy.dao.impl;

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.ConnectionUtil;
import mate.academy.dao.BookDao;
import mate.academy.lib.Dao;
import mate.academy.model.Book;
import mate.academy.service.DataProcessingException;

@Dao
public class BookDaoImpl implements BookDao {
@Override
public Book create(Book book) {
String sql = "INSERT INTO books (title, price) VALUES (?, ?)";

Choose a reason for hiding this comment

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

According to the checklist, don't use schema's name in your queries. You should configure the schema while establishing a connection with the DB, so there is no need to specify it in the SQL query.

Choose a reason for hiding this comment

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

Use uppercase for SQL keywords to follow SQL style conventions. For example, use 'INSERT INTO books (title, price) VALUES (?, ?)'.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1, book.getTitle());
statement.setBigDecimal(2, book.getPrice());

int affectedRows = statement.executeUpdate();
if (affectedRows == 0) {
throw new DataProcessingException("Expected to insert at least one row,"
+ " but inserted 0 rows");
}

ResultSet generatedKeys = statement.getGeneratedKeys();
if (generatedKeys.next()) {
Long id = generatedKeys.getObject(1, Long.class);
book.setId(id);
}
} catch (SQLException e) {
throw new DataProcessingException("Can' add a new book with id: " + book.getId());

Choose a reason for hiding this comment

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

Use informative messages for exceptions. Include the book object in the message to provide more context. For example, 'Can't add a new book: ' + book.

Choose a reason for hiding this comment

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

The error message in the DataProcessingException should be more informative. Instead of appending the book ID (which is not set when the book hasn't been inserted yet), include the book title and price in the message.

}
return book;
Comment on lines +21 to +43

Choose a reason for hiding this comment

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

According to the checklist, Statement.RETURN_GENERATED_KEYS should only be used with the create statement, which you've correctly done here.

}

@Override
public Optional<Book> findById(Long id) {
String sql = "SELECT * FROM books WHERE id = ?";

Choose a reason for hiding this comment

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

Use uppercase for SQL keywords to follow SQL style conventions. For example, use 'SELECT * FROM books WHERE id = ?'.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setLong(1, id);
ResultSet resultSet = statement.executeQuery();

if (resultSet.next()) {
return Optional.of(getBookFromResultSet(resultSet));
}
} catch (SQLException e) {
throw new DataProcessingException("Can't find a book with id: " + id);
}
return Optional.empty();
Comment on lines +47 to +60

Choose a reason for hiding this comment

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

Use resultSet.getObject() to retrieve the id from the ResultSet to handle null values correctly, as per the checklist.

}

@Override
public List<Book> findAll() {
List<Book> listOfBooks = new ArrayList<>();
String sql = "SELECT * FROM books;";

Choose a reason for hiding this comment

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

SQL keywords should be in uppercase for consistency and readability. Use 'SELECT' instead of 'select'.

Choose a reason for hiding this comment

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

Use uppercase for SQL keywords to follow SQL style conventions. For example, use 'SELECT * FROM books'. Also, remove the semicolon at the end of the query string as it is not necessary in JDBC.

Choose a reason for hiding this comment

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

SQL keywords should be written in uppercase for consistency and readability, as per the checklist.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {

Choose a reason for hiding this comment

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

Use PreparedStatement over Statement, even for a static query with no parameters in findAll() method. It's the best practice, and it's slightly faster.

Choose a reason for hiding this comment

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

Even though the findAll method does not have parameters, it is still recommended to use PreparedStatement over Statement as per the checklist.

ResultSet resultSet = statement.executeQuery();

while (resultSet.next()) {
listOfBooks.add(getBookFromResultSet(resultSet));
}
} catch (SQLException e) {
throw new DataProcessingException("Can't retrieve all books from the database.");
}
return listOfBooks;
}

@Override
public Book update(Book book) {
String sql = "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.

Use uppercase for SQL keywords to follow SQL style conventions. For example, use 'UPDATE books SET title = ?, price = ? WHERE id = ?'.

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

Choose a reason for hiding this comment

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

You're missing the statement.executeUpdate() call to actually perform the update operation on the database. Without it, the update will not be executed.

} catch (SQLException e) {
throw new DataProcessingException("Can't update the book with id: " + book.getId());

Choose a reason for hiding this comment

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

The error message in the DataProcessingException should be more informative. Include the book's details in the message to provide more context about the failure.

}
}

@Override
public boolean deleteById(Long id) {
String sql = "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.

Use uppercase for SQL keywords to follow SQL style conventions. For example, use 'DELETE FROM books WHERE id = ?'.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setLong(1, id);
return statement.executeUpdate() > 0;
} catch (SQLException e) {
throw new DataProcessingException("Can't delete the book with id: " + id);

Choose a reason for hiding this comment

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

The error message in the DataProcessingException should be more informative. Include the id of the book in the message to provide more context about the failure.

}
}

private Book getBookFromResultSet(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);
}
}
8 changes: 4 additions & 4 deletions src/main/java/mate/academy/lib/Injector.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

public class Injector {
private static final Map<String, Injector> injectors = new HashMap<>();
private final List<Class<?>> classes = new ArrayList<>();
private static final List<Class<?>> classes = new ArrayList<>();

private Injector(String mainPackageName) {
try {
Expand All @@ -31,12 +31,12 @@ public static Injector getInstance(String mainPackageName) {
return injector;
}

public Object getInstance(Class<?> certainInterface) {
public static Object getDaoInstance(Class<?> certainInterface) {
Class<?> clazz = findClassExtendingInterface(certainInterface);
return createInstance(clazz);
}

private Class<?> findClassExtendingInterface(Class<?> certainInterface) {
private static Class<?> findClassExtendingInterface(Class<?> certainInterface) {
for (Class<?> clazz : classes) {
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> singleInterface : interfaces) {
Expand All @@ -51,7 +51,7 @@ private Class<?> findClassExtendingInterface(Class<?> certainInterface) {
+ " interface and has valid annotation (Dao or Service)");
}

private Object createInstance(Class<?> clazz) {
private static Object createInstance(Class<?> clazz) {
Object newInstance;
try {
Constructor<?> classConstructor = clazz.getConstructor();
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/mate/academy/model/Book.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package mate.academy.model;

import java.math.BigDecimal;

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

public Book() {
}

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 String toString() {
return "Book{"
+ "id=" + id
+ ", title='" + title + '\''
+ ", price=" + price
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mate.academy.service;

public class DataProcessingException extends RuntimeException {
public DataProcessingException(String message) {
super(message);
}
}
5 changes: 5 additions & 0 deletions src/main/resources/init_db.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS books(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR (255) NOT NULL,
price DECIMAL (10, 2) NOT NULL
);
1 change: 1 addition & 0 deletions src/main/resources/password.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Nk102030!
Loading