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

jdbc-intro #399

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
36 changes: 36 additions & 0 deletions src/main/java/mate/academy/ConnectionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package mate.academy;

Choose a reason for hiding this comment

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

Suggested change
package mate.academy;
package mate.academy.util;

replace to util package


import java.io.FileInputStream;
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 String PROPERTIES_FILE_PATH = "src/main/resources/db.properties";
private static final String DATABASE_URL = "db.url";
private static final String DATABASE_USER_NAME = "db.username";
private static final String DATABASE_USER_PASSWORD = "db.password";
private static final Properties properties = new Properties();

static {
readDatabaseFileProperties();
}

private static final void readDatabaseFileProperties() {
try (InputStream inputStreamProperties = new FileInputStream(PROPERTIES_FILE_PATH)) {
properties.load(inputStreamProperties);
} catch (IOException e) {
throw new RuntimeException("Cannot read properties file", e);
}
}

public static Connection connectToDatabase() throws SQLException {
String url = properties.getProperty(DATABASE_URL);
String username = properties.getProperty(DATABASE_USER_NAME);
String password = properties.getProperty(DATABASE_USER_PASSWORD);
return DriverManager.getConnection(url, username, password);
}
}
16 changes: 15 additions & 1 deletion src/main/java/mate/academy/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
package mate.academy;

import java.math.BigDecimal;
import mate.academy.dao.BookDao;
import mate.academy.domain.Book;
import mate.academy.lib.Injector;

public class Main {
public static void main(String[] args) {
private static final Injector injector = Injector.getInstance("mate.academy.dao");

public static void main(String[] args) {
BookDao bookDao = (BookDao) injector.getInstance(BookDao.class);
Book book = new Book("The witcher", new BigDecimal("195.00"));
bookDao.create(book);
bookDao.create(book);
bookDao.update(book);
bookDao.deleteById(1);
bookDao.findAll();
bookDao.findById(2);
}
}
18 changes: 18 additions & 0 deletions src/main/java/mate/academy/dao/BookDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package mate.academy.dao;

import java.util.List;
import java.util.Optional;
import mate.academy.domain.Book;

public interface BookDao {

Choose a reason for hiding this comment

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

Suggested change

Book create(Book book);

Optional<Book> findById(int id);

List<Book> findAll();

Book update(Book book);

boolean deleteById(int id);
}
123 changes: 123 additions & 0 deletions src/main/java/mate/academy/dao/BookDaoImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
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.ConnectionUtil;
import mate.academy.domain.Book;
import mate.academy.lib.Dao;

@Dao
public class BookDaoImpl implements BookDao {
private static final String CREATE_BOOK_QUERY = "INSERT INTO book (title, price) VALUES (?, ?)";
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved
private static final String FIND_BOOK_BY_ID_QUERY = "SELECT * FROM book WHERE id = ?";
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved
private static final String FIND_ALL_BOOK_QUERY = "SELECT * FROM book";
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved
private static final String UPDATE_BOOK_QUERY = "UPDATE book SET title = ?, "
+ "price = ? WHERE id = ?";
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved
private static final String DELETE_BOOK_QUERY = "DELETE FROM book WHERE id = ?";
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved

@Override
public Book create(Book book) {
Book createdBook;

Choose a reason for hiding this comment

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

Suggested change
Book createdBook;

try (Connection connection = ConnectionUtil.connectToDatabase();
PreparedStatement preparedStatement = connection.prepareStatement(CREATE_BOOK_QUERY,
Statement.RETURN_GENERATED_KEYS)) {

Choose a reason for hiding this comment

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

Use PreparedStatement.RETURN_GENERATED_KEYS only in create statement. Here, it is correctly used.

preparedStatement.setString(2, book.getTitle());
preparedStatement.setBigDecimal(3, book.getPrice());

Choose a reason for hiding this comment

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

The parameters for preparedStatement.setString and preparedStatement.setBigDecimal should start from index 1, not 2 and 3 respectively. This is a bug that will cause an SQLException due to missing parameter bindings.

preparedStatement.execute();

Choose a reason for hiding this comment

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

Suggested change
preparedStatement.execute();
preparedStatement.execute();
int affectedRows = preparedStatement.executeUpdate();
if (affectedRows < 1) {
throw new DataProcessingException(
"Expected to insert at least 1 row, but 0 was inserted");
}

try (ResultSet resultSet = preparedStatement.getGeneratedKeys()) {
resultSet.next();
createdBook = new Book(
resultSet.getString("title"),
resultSet.getBigDecimal("price"));

Choose a reason for hiding this comment

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

Instead of using resultSet.getString and resultSet.getBigDecimal, use resultSet.getObject with the appropriate type to handle possible null values correctly. Also, the constructor for Book should be called with the id parameter.

Choose a reason for hiding this comment

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

Suggested change
resultSet.next();
createdBook = new Book(
resultSet.getString("title"),
resultSet.getBigDecimal("price"));
if (generatedKeys.next()) {
Long id = generatedKeys.getObject(1, Long.class);
book.setId(id);

}
} catch (SQLException throwables) {

Choose a reason for hiding this comment

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

Suggested change
} catch (SQLException throwables) {
} catch (SQLException e) {
throw new DataProcessingException("Can't add a new book, " + book, e);
}
return book;

throw new RuntimeException(throwables);

Choose a reason for hiding this comment

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

Use a more informative message for the RuntimeException, such as including the action that was attempted when the exception occurred.

}
return createdBook;
}

@Override
public Optional<Book> findById(int id) {
Book findBookById = null;

Choose a reason for hiding this comment

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

Suggested change
Book findBookById = null;
Optional<Book> result = Optional.empty();

try (Connection connection = ConnectionUtil.connectToDatabase();
PreparedStatement preparedStatement =
connection.prepareStatement(FIND_BOOK_BY_ID_QUERY,
Statement.RETURN_GENERATED_KEYS)) {

Choose a reason for hiding this comment

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

Do not use Statement.RETURN_GENERATED_KEYS in the findById method as it is not needed here.

Choose a reason for hiding this comment

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

fix comment

preparedStatement.setLong(1, id);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
Long bookId = resultSet.getLong("id");
String title = resultSet.getString("title");
BigDecimal price = resultSet.getBigDecimal("price");
findBookById = new Book(bookId, title, price);

Choose a reason for hiding this comment

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

Use if(resultSet.next()) instead of while loop for findById method since you're expecting at most one record.

Choose a reason for hiding this comment

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

Suggested change
while (resultSet.next()) {
Long bookId = resultSet.getLong("id");
String title = resultSet.getString("title");
BigDecimal price = resultSet.getBigDecimal("price");
findBookById = new Book(bookId, title, price);
if (resultSet.next()) {
result = Optional.of(mapResultSetToBook(resultSet, id));
}

}
} catch (SQLException throwables) {
throw new RuntimeException(throwables);

Choose a reason for hiding this comment

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

Suggested change
throw new RuntimeException(throwables);
throw new DataProcessingException(....

check all places

}
return Optional.ofNullable(findBookById);

Choose a reason for hiding this comment

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

Suggested change
return Optional.ofNullable(findBookById);
return result;

}

@Override
public List<Book> findAll() {
List<Book> foundBooks = new ArrayList<>();

Choose a reason for hiding this comment

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

Suggested change
List<Book> foundBooks = new ArrayList<>();
List<Book> books = new ArrayList<>();

try (Connection connection = ConnectionUtil.connectToDatabase();
PreparedStatement preparedStatement =
connection.prepareStatement(FIND_ALL_BOOK_QUERY,
Statement.RETURN_GENERATED_KEYS)) {

Choose a reason for hiding this comment

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

Do not use Statement.RETURN_GENERATED_KEYS in the findAll method as it is not needed here.

Choose a reason for hiding this comment

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

fix comment


ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
Long bookId = resultSet.getLong("id");
String title = resultSet.getString("title");
BigDecimal price = resultSet.getBigDecimal("price");
foundBooks.add(new Book(bookId, title, price));

Choose a reason for hiding this comment

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

Suggested change
String title = resultSet.getString("title");
BigDecimal price = resultSet.getBigDecimal("price");
foundBooks.add(new Book(bookId, title, price));
books.add(mapResultSetToBook(resultSet, id));

}
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}
return foundBooks;
}

@Override
public Book update(Book book) {
Book updatedBook = null;
try (Connection connection = ConnectionUtil.connectToDatabase();
PreparedStatement preparedStatement = connection.prepareStatement(UPDATE_BOOK_QUERY,
Statement.RETURN_GENERATED_KEYS)) {

Choose a reason for hiding this comment

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

Do not use Statement.RETURN_GENERATED_KEYS in the update method as it is not needed here.

Choose a reason for hiding this comment

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

fix comment

preparedStatement.setString(2, book.getTitle());
preparedStatement.setBigDecimal(3, book.getPrice());

Choose a reason for hiding this comment

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

The parameters for preparedStatement.setString and preparedStatement.setBigDecimal should start from index 1, not 2 and 3 respectively. This is a bug that will cause an SQLException due to missing parameter bindings.

ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
Long bookId = resultSet.getLong("id");
String title = resultSet.getString("title");
BigDecimal price = resultSet.getBigDecimal("price");
updatedBook = new Book(bookId, title, price);
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved
}
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved
}
return updatedBook;
}

@Override
public boolean deleteById(int id) {
boolean isDeleted;

Choose a reason for hiding this comment

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

Suggested change
boolean isDeleted;

try (Connection connection = ConnectionUtil.connectToDatabase();
PreparedStatement preparedStatement = connection.prepareStatement(DELETE_BOOK_QUERY,
Statement.RETURN_GENERATED_KEYS)) {
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved
preparedStatement.setLong(1, id);
isDeleted = preparedStatement.execute();
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved

Choose a reason for hiding this comment

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

Suggested change
preparedStatement.setLong(1, id);
isDeleted = preparedStatement.execute();
preparedStatement.setLong(1, id);
return preparedStatement.executeUpdate() > 0;

} catch (SQLException throwables) {
throw new RuntimeException(throwables);
alwayswannajava marked this conversation as resolved.
Show resolved Hide resolved
}
return isDeleted;

Choose a reason for hiding this comment

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

Suggested change
return isDeleted;

}
}

47 changes: 47 additions & 0 deletions src/main/java/mate/academy/domain/Book.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package mate.academy.domain;

import java.math.BigDecimal;

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;
}
}
3 changes: 3 additions & 0 deletions src/main/resources/db.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
db.url=jdbc:mysql://localhost:3307
db.username=postgres
db.password=root
7 changes: 7 additions & 0 deletions src/main/resources/init_db.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
create schema if not exists test;

create table if not exists book (
book_id serial primary key,

Choose a reason for hiding this comment

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

Suggested change
book_id serial primary key,
id bigint primary key,

title varchar(20),
price decimal
);
Loading