Requirements:
- Add/Delete member.
- Add/Delete book.
- Issue/Return book.
Review Request:
General coding comments, bad practices, style et cetera.
Code:
Main.java:
package library;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.io.IOException;
public class Main extends Application {
private static Stage stg;
private static final String URL = "jdbc:sqlite:src/library/library.db";
static Connection conn = null;
private boolean checkDatabaseTables() throws SQLException {
final var query = "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' LIMIT 1;";
var stmt = conn.createStatement();
var rs = stmt.executeQuery(query);
return rs.next();
}
private void populateDatabase() throws SQLException {
final var query1 = """
CREATE TABLE books (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
author TEXT NOT NULL,
genre TEXT NOT NULL,
total_count INTEGER NOT NULL,
total_alloc INTEGER NOT NULL DEFAULT 0
);
""";
final var query2 = """
CREATE TABLE members (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
address TEXT NOT NULL,
phone_no TEXT NOT NULL,
alloc_book_id INTEGER REFERENCES books(id)
);
""";
final var query3 = """
CREATE TABLE admin (
id INTEGER PRIMARY KEY CHECK (id = 1), -- Ensure only one entry
password_hash TEXT NOT NULL
);
""";
var stmt = conn.createStatement();
stmt.execute(query1);
stmt.execute(query2);
stmt.execute(query3);
}
@Override
public void start(Stage primaryStage) throws Exception {
try {
conn = DriverManager.getConnection(URL);
} catch (SQLException e) {
System.err.println(e.getMessage());
throw new SQLException("Failed to establish connection with database!");
}
final var databaseExists = checkDatabaseTables();
if (!databaseExists) {
populateDatabase();
}
var startingStage = !databaseExists ? "Signup.fxml" : "Login.fxml";
stg = primaryStage;
primaryStage.setResizable(false);
Parent root = FXMLLoader.load(getClass().getResource(startingStage));
primaryStage.setTitle("Library Management System");
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
public void changeScene(String fxml) throws IOException {
Parent pane = FXMLLoader.load(getClass().getResource(fxml));
stg.getScene().setRoot(pane);
}
public static void main(String[] args) {
launch(args);
}
}
Login.java:
package library;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.event.ActionEvent;
import java.sql.SQLException;
public class Login {
public Login() {
}
@FXML
private Button button;
@FXML
private Label wrongLogin;
@FXML
private PasswordField password;
public void userLogin(ActionEvent event) throws Exception {
checkLogin();
}
private String getPassword() throws Exception {
final var query = "SELECT password_hash FROM admin";
try (var rs = Main.conn.createStatement().executeQuery(query)) {
return rs.getString("password_hash");
} catch (Exception e) {
wrongLogin.setText("Internal error: Failed to login!");
System.err.println(e.getMessage());
throw new SQLException("Failed to fetch password!");
}
}
private void checkLogin() throws Exception {
var m = new Main();
final var pwd = getPassword();
if (password.getText().equals(pwd)) {
m.changeScene("AfterLogin.fxml");
} else if (password.getText().isEmpty()) {
wrongLogin.setText("Please enter your password!");
} else {
wrongLogin.setText("Incorrect password!");
}
}
}
Signup.java:
package library;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import java.sql.SQLException;
public class Signup {
public Signup() {
}
@FXML
private Button button;
@FXML
private PasswordField password;
public void userSignup(ActionEvent event) throws Exception {
// TODO: Hash password.
final var pwd = password.getText();
final var query = "INSERT INTO admin (password_hash) VALUES (?)";
try (var pstmt = Main.conn.prepareStatement(query)) {
pstmt.setString(1, pwd);
pstmt.executeUpdate();
} catch (SQLException e) {
throw new Exception("Failed to sign up!");
}
var m = new Main();
m.changeScene("Login.fxml");
}
}
AfterLogin.java:
package library;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import java.io.IOException;
public class AfterLogin {
@FXML
private Button memberManagement;
@FXML
private Button bookManagement;
@FXML
private Button borrowingAndReturning;
public void manageMember(ActionEvent event) throws IOException {
var m = new Main();
m.changeScene("ManageMember.fxml");
}
public void manageBook(ActionEvent event) throws IOException {
var m = new Main();
m.changeScene("ManageBook.fxml");
}
public void borrowAndReturn(ActionEvent event) throws IOException {
var m = new Main();
m.changeScene("BorrowAndReturn.fxml");
}
public void userLogout(ActionEvent event) throws IOException {
var m = new Main();
m.changeScene("Login.fxml");
}
}
ManageMember.java:
package library;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.event.ActionEvent;
import java.sql.Connection;
import java.sql.SQLException;
import java.io.IOException;
public class ManageMember {
@FXML
private TextField MemberName;
@FXML
private TextField HouseAddress;
@FXML
private TextField ContactAdd;
@FXML
private TextField ContactRemove;
@FXML
private Button Home;
@FXML
private Button AddMember;
@FXML
private Button RemoveMember;
@FXML
private Label AddLabel;
@FXML
private Label RemoveLabel;
final Connection conn = Main.conn;
public void returnHome(ActionEvent event) throws IOException {
var m = new Main();
m.changeScene("AfterLogin.fxml");
}
private boolean checkMember(String phone_no) throws Exception {
final var query = "SELECT name FROM members WHERE phone_no = ?";
var pstmt = conn.prepareStatement(query);
pstmt.setString(3, phone_no);
final var rs = pstmt.executeQuery();
return rs.next();
}
public void addMember(ActionEvent event) throws Exception {
final var name = MemberName.getText();
final var address = HouseAddress.getText();
final var phone_no = ContactAdd.getText();
try {
if (name.isEmpty() || address.isEmpty() || phone_no.isEmpty()) {
AddLabel.setText("Missing details!");
} else {
if (!checkMember(phone_no)) {
final var query = "INSERT INTO members (name, address, phone_no) VALUES (?, ?, ?)";
var pstmt = conn.prepareStatement(query);
pstmt.setString(1, name);
pstmt.setString(2, address);
pstmt.setString(3, phone_no);
pstmt.executeUpdate();
AddLabel.setText("");
MemberName.clear();
HouseAddress.clear();
ContactAdd.clear();
} else {
AddLabel.setText("Member already exists!");
}
}
} catch (SQLException e) {
AddLabel.setText("Internal error: Failed to add member!");
System.err.println(e.getMessage());
throw new SQLException("Failed to add member!");
}
}
public void removeMember(ActionEvent event) throws Exception {
final var phone_no = ContactRemove.getText();
try {
if (phone_no.isEmpty()) {
RemoveLabel.setText("Missing field!");
} else {
var query = "SELECT alloc_book_id FROM members WHERE phone_no = ?";
var pstmt = conn.prepareStatement(query);
pstmt.setString(1, phone_no);
var rs = pstmt.executeQuery();
if (rs.next()) { // Member exists.
if (rs.getObject("alloc_book_id") != null) { // Has issued a book.
// Add 1 to total_alloc
query = "SELECT total_alloc FROM books WHERE id = ?";
var pstmt1 = conn.prepareStatement(query);
final var id = rs.getInt("alloc_book_id");
pstmt1.setInt(1, id);
rs = pstmt1.executeQuery();
var total_alloc = rs.getInt("total_alloc") - 1;
query = "UPDATE books SET total_alloc = ? WHERE id = ?";
pstmt1 = conn.prepareStatement(query);
pstmt1.setInt(1, total_alloc);
pstmt1.setInt(2, id);
pstmt1.executeUpdate();
}
query = "DELETE FROM members WHERE phone_no = ?";
var pstmt2 = conn.prepareStatement(query);
pstmt2.setString(1, phone_no);
pstmt2.executeUpdate();
RemoveLabel.setText("");
ContactRemove.clear();
} else {
RemoveLabel.setText("The member does not exist!");
}
}
} catch (SQLException e) {
RemoveLabel.setText("Internal error: Failed to remove book!");
System.err.println(e.getMessage());
throw new SQLException("Failed to remove member!");
}
}
}
ManageBook.java:
package library;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.event.ActionEvent;
import java.sql.Connection;
import java.sql.SQLException;
import java.io.IOException;
public class ManageBook {
@FXML
private TextField BookName;
@FXML
private TextField AuthorName;
@FXML
private TextField Genre;
@FXML
private Button Home;
@FXML
private Button AddBook;
@FXML
private Button RemoveBook;
@FXML
private Label Error;
final Connection conn = Main.conn;
public void returnHome(ActionEvent event) throws IOException {
var m = new Main();
m.changeScene("AfterLogin.fxml");
}
private void insertBook(String name, String author, String genre, int total_count) throws Exception {
final var query = "INSERT INTO books (name, author, genre, total_count) VALUES (?, ?, ?, ?)";
var pstmt = Main.conn.prepareStatement(query);
pstmt.setString(1, name);
pstmt.setString(2, author);
pstmt.setString(3, genre);
pstmt.setInt(4, total_count);
pstmt.executeUpdate();
}
private void updateBookCount(String name, String author, String genre, int count) throws Exception {
final var query = "UPDATE books SET total_count = ? WHERE name = ? AND author = ? AND genre = ?";
var pstmt = conn.prepareStatement(query);
pstmt.setInt(1, count);
pstmt.setString(2, name);
pstmt.setString(3, author);
pstmt.setString(4, genre);
pstmt.executeUpdate();
}
public void addBook(ActionEvent event) throws Exception {
final var name = BookName.getText();
final var author = AuthorName.getText();
final var genre = Genre.getText();
int total_count = 1;
final var query = "SELECT name, author, genre, total_count FROM books WHERE name = ? AND author = ? AND genre = ?";
if (name.isEmpty() || author.isEmpty() || genre.isEmpty()) {
Error.setText("Missing details!");
} else {
try (var pstmt = conn.prepareStatement(query)) {
pstmt.setString(1, name);
pstmt.setString(2, author);
pstmt.setString(3, genre);
final var rs = pstmt.executeQuery();
if (rs.next()) {
total_count += rs.getInt("total_count");
updateBookCount(name, author, genre, total_count);
} else {
insertBook(name, author, genre, total_count);
}
Error.setText("");
BookName.clear();
AuthorName.clear();
Genre.clear();
} catch (Exception e) {
Error.setText("Internal error: Failed to add book!");
System.err.println(e.getMessage());
throw new SQLException("Failed to add book!");
}
}
}
private void deleteBook(String name, String author, String genre) throws Exception {
final var query = "DELETE FROM books WHERE name = ? AND author = ? AND genre = ?";
var pstmt = conn.prepareStatement(query);
pstmt.setString(1, name);
pstmt.setString(2, author);
pstmt.setString(3, genre);
pstmt.executeUpdate();
}
public void removeBook(ActionEvent event) throws Exception {
final var name = BookName.getText();
final var author = AuthorName.getText();
final var genre = Genre.getText();
final var query = "SELECT name, author, genre, total_count, total_alloc FROM books WHERE name = ? AND author = ? AND genre = ?";
if (name.isEmpty() || author.isEmpty() || genre.isEmpty()) {
Error.setText("Missing details!");
} else {
try (var pstmt = conn.prepareStatement(query)) {
pstmt.setString(1, name);
pstmt.setString(2, author);
pstmt.setString(3, genre);
final var rs = pstmt.executeQuery();
if (!rs.next()) {
Error.setText("Book does not exist!");
} else if (rs.getInt("total_count") > rs.getInt("total_alloc")) {
final var total_count = rs.getInt("total_count") - 1;
if (total_count == 0) {
deleteBook(name, author, genre);
} else {
updateBookCount(name, author, genre, total_count);
}
Error.setText("");
BookName.clear();
AuthorName.clear();
Genre.clear();
} else {
Error.setText("The book is currently issued!");
}
} catch (Exception e) {
Error.setText("Internal error: Failed to remove book!");
System.err.println(e.getMessage());
throw new SQLException("Failed to remove book!");
}
}
}
}
BorrowAndReturn.java:
package library;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.event.ActionEvent;
import java.sql.Connection;
import java.sql.SQLException;
import java.io.IOException;
public class BorrowAndReturn {
@FXML
private TextField BookName;
@FXML
private TextField AuthorName;
@FXML
private TextField Genre;
@FXML
private TextField ContactNo;
@FXML
private Button Home;
@FXML
private Button IssueBook;
@FXML
private Button ReturnBook;
@FXML
private Label Error;
final Connection conn = Main.conn;
public void returnHome(ActionEvent event) throws IOException {
var m = new Main();
m.changeScene("AfterLogin.fxml");
}
public void issueBook(ActionEvent event) throws Exception {
final var book = BookName.getText();
final var author = AuthorName.getText();
final var genre = Genre.getText();
final var phone_no = ContactNo.getText();
var query = "SELECT id, total_count, total_alloc FROM books WHERE name = ? AND author = ? AND genre = ?";
if (book.isEmpty() || author.isEmpty() || genre.isEmpty() || phone_no.isEmpty()) {
Error.setText("Missing details!");
} else {
try (var pstmt = conn.prepareStatement(query)) {
pstmt.setString(1, book);
pstmt.setString(2, author);
pstmt.setString(3, genre);
var rs = pstmt.executeQuery();
if (!rs.next()) {
Error.setText("The book doesn't exist in the database!");
} else if (rs.getInt("total_alloc") == rs.getInt("total_count")) {
Error.setText("All the books have already been issued!");
} else {
query = "SELECT * FROM members WHERE phone_no = ?";
var pstmt2 = conn.prepareStatement(query);
pstmt2.setString(1, phone_no);
var res = pstmt2.executeQuery();
if (res.next() && res.getObject("alloc_book_id") == null) {
query = "UPDATE members SET alloc_book_id = ? WHERE phone_no = ?";
var pstmt3 = conn.prepareStatement(query);
pstmt3.setInt(1, rs.getInt("id"));
pstmt3.setString(2, phone_no);
pstmt3.executeUpdate();
query = "UPDATE books SET total_alloc = ? WHERE name = ? AND author = ? AND genre = ?";
var pstmt4 = conn.prepareStatement(query);
pstmt4.setInt(1, rs.getInt("total_alloc") + 1);
pstmt4.setString(2, book);
pstmt4.setString(3, author);
pstmt4.setString(4, genre);
pstmt4.executeUpdate();
Error.setText("");
BookName.clear();
AuthorName.clear();
Genre.clear();
ContactNo.clear();
} else {
Error.setText("The member does not exist or has already issued a book!");
}
}
} catch (Exception e) {
Error.setText("Internal error: Failed to issue book!");
System.err.println(e.getMessage());
throw new SQLException("Failed to issue book!");
}
}
}
public void returnBook(ActionEvent event) throws Exception {
final var book = BookName.getText();
final var author = AuthorName.getText();
final var genre = Genre.getText();
final var phone_no = ContactNo.getText();
var query = "SELECT * FROM books WHERE name = ? AND author = ? AND genre = ?";
if (book.isEmpty() || author.isEmpty() || genre.isEmpty() || phone_no.isEmpty()) {
Error.setText("Missing details!");
} else {
try (var pstmt = conn.prepareStatement(query)) {
pstmt.setString(1, book);
pstmt.setString(2, author);
pstmt.setString(3, genre);
var rs = pstmt.executeQuery();
if (!rs.next()) {
Error.setText("The book doesn't exist in the database!");
} else {
query = "SELECT * FROM members WHERE phone_no = ?";
var pstmt2 = conn.prepareStatement(query);
pstmt2.setString(1, phone_no);
var res = pstmt2.executeQuery();
if (res.next() && res.getInt("alloc_book_id") == rs.getInt("id")) {
query = "UPDATE members SET alloc_book_id = NULL WHERE phone_no = ?";
var pstmt3 = conn.prepareStatement(query);
pstmt3.setString(1, phone_no);
pstmt3.executeUpdate();
query = "UPDATE books SET total_alloc = ? WHERE name = ? AND author = ? AND genre = ?";
var pstmt4 = conn.prepareStatement(query);
pstmt4.setInt(1, rs.getInt("total_alloc") - 1);
pstmt4.setString(2, book);
pstmt4.setString(3, author);
pstmt4.setString(4, genre);
pstmt4.executeUpdate();
} else {
Error.setText("The member does not exist or has not issued this book!");
}
Error.setText("");
BookName.clear();
AuthorName.clear();
Genre.clear();
ContactNo.clear();
}
} catch (Exception e) {
Error.setText("Internal error: Failed to return book!");
System.err.println(e.getMessage());
throw new SQLException("Failed to return book!");
}
}
}
}