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

Implemented BookDao #416

Open
wants to merge 4 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
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
</maven.checkstyle.plugin.configLocation>
</properties>

<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/mate/academy/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
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 zapovit = new Book("zapovit", BigDecimal.valueOf(100));
Book newZapiovit = bookDao.create(zapovit);

Choose a reason for hiding this comment

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

The variable 'newZapiovit' is never used. If you don't need the result of 'bookDao.create(zapovit)', you can omit assigning it to a variable.


Optional<Book> bookById2 = bookDao.findById(2L);

Choose a reason for hiding this comment

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

The variable 'bookById2' is never used. Make sure to use it or remove it if it's not necessary for the main logic.


List<Book> allBooks = bookDao.findAll();

Choose a reason for hiding this comment

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

The variable 'allBooks' is never used. Make sure to use it or remove it if it's not necessary for the main logic.


Book updateZapovit = bookDao.update(zapovit);

Choose a reason for hiding this comment

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

The variable 'updateZapovit' is never used. If you don't need the result of 'bookDao.update(zapovit)', you can omit assigning it to a variable.


boolean isDeleted = bookDao.deleteById(3L);

Choose a reason for hiding this comment

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

The variable 'isDeleted' is never used. Consider logging the result or handling the deletion result in some way.

}
}
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);
}
121 changes: 121 additions & 0 deletions src/main/java/mate/academy/dao/BookDaoImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
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 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.

For SQL keywords, use uppercase for better readability. For example, use INSERT INTO instead of insert into.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1, book.getTitle());
statement.setBigDecimal(2, book.getPrice());
validateStatementExecution(statement);
ResultSet generatedKeys = statement.getGeneratedKeys();
if (generatedKeys.next()) {
Long id = generatedKeys.getObject(1, Long.class);
book.setId(id);
}
} catch (SQLException e) {
throw new DataProcessingException("Can not add a new book: " + book, e);

Choose a reason for hiding this comment

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

The exception message should be more informative. For example: 'Can't insert book: ' + book

}
return book;
}

@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.

SQL keywords should be in uppercase for consistency and readability.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setLong(1, id);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
Book book = mapToBook(resultSet);
return Optional.of(book);
} else {
throw new RuntimeException("Can not find book in database by id = " + id);
}
} catch (SQLException e) {
throw new DataProcessingException("Can not create a connection to the database", e);

Choose a reason for hiding this comment

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

The exception message should be more informative and specific to the operation being performed. For example: 'Can't get a book by id ' + id

}
}

@Override
public List<Book> findAll() {
List<Book> books = 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.

Again, use uppercase for SQL keywords to improve readability.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
Book book = mapToBook(resultSet);
books.add(book);
}
} catch (SQLException e) {
throw new DataProcessingException("Can not create a connection to the database", e);

Choose a reason for hiding this comment

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

The exception message should be more informative. For example: 'Can't execute query: ' + sql

}
return books;
}

@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.

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());
validateStatementExecution(statement);
return book;
} catch (SQLException e) {
throw new DataProcessingException("Can not create a connection to the database", e);

Choose a reason for hiding this comment

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

The exception message should be more informative and specific to the operation being performed. For example: 'Can't update book: ' + book

}
}

@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.

try (Connection connection = ConnectionUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setLong(1, id);
int affectedRows = statement.executeUpdate();
return affectedRows >= 1;

Choose a reason for hiding this comment

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

The deleteById method should return true only if one or more rows were affected. The current implementation is correct, so no action is needed here.

} catch (SQLException e) {
throw new DataProcessingException("Can not create a connection to the database", e);

Choose a reason for hiding this comment

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

The exception message should be more informative and specific to the operation being performed. For example: 'Can't delete book with id: ' + id

}
}

private Book mapToBook(ResultSet resultSet) throws SQLException {
Long id = resultSet.getObject("id", Long.class);
String title = resultSet.getString("title");
BigDecimal price = resultSet.getObject("price", BigDecimal.class);
Book book = new Book();
book.setId(id);
book.setTitle(title);
book.setPrice(price);
return book;
}

private void validateStatementExecution(PreparedStatement statement)
throws SQLException {
int affectedRows = statement.executeUpdate();
if (affectedRows < 1) {
throw new DataProcessingException(
"Expected to modify at least one row, but modified 0 rows.");
}
}
}
11 changes: 11 additions & 0 deletions src/main/java/mate/academy/exception/DataProcessingException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.exception;

public class DataProcessingException extends RuntimeException {
public DataProcessingException(String message, Throwable cause) {
super(message, cause);
}

public DataProcessingException(String message) {
super(message);
}
}
41 changes: 41 additions & 0 deletions src/main/java/mate/academy/model/Book.java
Original file line number Diff line number Diff line change
@@ -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 Book(String title, BigDecimal price) {
this.title = title;
this.price = price;
}

public Book() {
}

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;
}
}
34 changes: 34 additions & 0 deletions src/main/java/mate/academy/util/ConnectionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package mate.academy.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class ConnectionUtil {
private static final Properties DB_PROPERTIES = new Properties();
private static final String CONFIG_FILE = "config.properties";

static {
try (InputStream input = ConnectionUtil.class.getClassLoader()
.getResourceAsStream(CONFIG_FILE)) {
if (input == null) {
throw new RuntimeException(
"Configuration file '" + CONFIG_FILE + "' not found in classpath");
}
DB_PROPERTIES.load(input);
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("Cannot load configuration or JDBC driver", e);
}
}

public static Connection getConnection() throws SQLException {
String url = DB_PROPERTIES.getProperty("db.url");
String user = DB_PROPERTIES.getProperty("db.user");
String password = DB_PROPERTIES.getProperty("db.password");
return DriverManager.getConnection(url, user, password);
}
}
3 changes: 3 additions & 0 deletions src/main/resources/config.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
db.url=jdbc:mysql://localhost:3306/intro
db.user=root
db.password=091088a+
17 changes: 17 additions & 0 deletions src/main/resources/init_db.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CREATE DATABASE IF NOT EXISTS `intro`;

USE `intro`;

CREATE TABLE `books` (
`id` BIGINT AUTO_INCREMENT,
`title` VARCHAR(255),
`price` DECIMAL,
PRIMARY KEY (`id`)
);

INSERT INTO `books` (`title`, `price`) VALUES
('The Catcher in the Rye', 120),
('To Kill a Mockingbird', 150),
('1984', 200),
('The Great Gatsby', 180),
('Moby Dick', 220);
Loading