From fe60b39d9abc11b3f63a47d79a32d963abb68fbe Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 06:04:21 +0000 Subject: [PATCH] Refactor raw SQL execution to use PreparedStatement via SqlHelper Refactored `DatabaseHandler.execAction` to a new method `execUpdate` that uses `PreparedStatement` to prevent SQL injection. Introduced `SqlHelper` class to encapsulate the `PreparedStatement` logic and allow unit testing without JavaFX dependencies. Updated `MainController` to use `execUpdate` with parameterized queries. Added unit tests for `SqlHelper` using Derby in-memory database. Deprecated `execAction` in `DatabaseHandler`. Co-authored-by: G30RG3-GJ <203693057+G30RG3-GJ@users.noreply.github.com> --- .../assistant/database/DatabaseHandler.java | 5 ++ src/library/assistant/database/SqlHelper.java | 26 ++++++ .../assistant/ui/main/MainController.java | 18 ++-- .../assistant/database/SqlHelperTest.java | 90 +++++++++++++++++++ 4 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 src/library/assistant/database/SqlHelper.java create mode 100644 test/library/assistant/database/SqlHelperTest.java diff --git a/src/library/assistant/database/DatabaseHandler.java b/src/library/assistant/database/DatabaseHandler.java index e9c4f53..b39f7b4 100644 --- a/src/library/assistant/database/DatabaseHandler.java +++ b/src/library/assistant/database/DatabaseHandler.java @@ -123,6 +123,11 @@ public ResultSet execQuery(String query) { return result; } + public boolean execUpdate(String query, Object... params) { + return SqlHelper.execUpdate(getConnection(), query, params); + } + + @Deprecated public boolean execAction(String qu) { try { stmt = conn.createStatement(); diff --git a/src/library/assistant/database/SqlHelper.java b/src/library/assistant/database/SqlHelper.java new file mode 100644 index 0000000..3a0e566 --- /dev/null +++ b/src/library/assistant/database/SqlHelper.java @@ -0,0 +1,26 @@ +package library.assistant.database; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class SqlHelper { + + private final static Logger LOGGER = LogManager.getLogger(SqlHelper.class.getName()); + + public static boolean execUpdate(Connection conn, String query, Object... params) { + try (PreparedStatement pstmt = conn.prepareStatement(query)) { + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); + } + pstmt.executeUpdate(); + return true; + } catch (SQLException ex) { + LOGGER.log(Level.ERROR, "Exception at execUpdate", ex); + return false; + } + } +} diff --git a/src/library/assistant/ui/main/MainController.java b/src/library/assistant/ui/main/MainController.java index 50b0fff..db5c501 100644 --- a/src/library/assistant/ui/main/MainController.java +++ b/src/library/assistant/ui/main/MainController.java @@ -236,13 +236,11 @@ private void loadIssueOperation(ActionEvent event) { JFXButton yesButton = new JFXButton("YES"); yesButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event1) -> { - String str = "INSERT INTO ISSUE(memberID,bookID) VALUES (" - + "'" + memberID + "'," - + "'" + bookID + "')"; - String str2 = "UPDATE BOOK SET isAvail = false WHERE id = '" + bookID + "'"; + String str = "INSERT INTO ISSUE(memberID,bookID) VALUES (?, ?)"; + String str2 = "UPDATE BOOK SET isAvail = false WHERE id = ?"; System.out.println(str + " and " + str2); - if (databaseHandler.execAction(str) && databaseHandler.execAction(str2)) { + if (databaseHandler.execUpdate(str, memberID, bookID) && databaseHandler.execUpdate(str2, bookID)) { JFXButton button = new JFXButton("Done!"); button.setOnAction((actionEvent) -> { bookIDInput.requestFocus(); @@ -330,10 +328,10 @@ private void loadSubmissionOp(ActionEvent event) { JFXButton yesButton = new JFXButton("YES, Please"); yesButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent ev) -> { String id = bookID.getText(); - String ac1 = "DELETE FROM ISSUE WHERE BOOKID = '" + id + "'"; - String ac2 = "UPDATE BOOK SET ISAVAIL = TRUE WHERE ID = '" + id + "'"; + String ac1 = "DELETE FROM ISSUE WHERE BOOKID = ?"; + String ac2 = "UPDATE BOOK SET ISAVAIL = TRUE WHERE ID = ?"; - if (databaseHandler.execAction(ac1) && databaseHandler.execAction(ac2)) { + if (databaseHandler.execUpdate(ac1, id) && databaseHandler.execUpdate(ac2, id)) { JFXButton btn = new JFXButton("Done!"); btn.setOnAction((actionEvent) -> { bookID.requestFocus(); @@ -364,9 +362,9 @@ private void loadRenewOp(ActionEvent event) { } JFXButton yesButton = new JFXButton("YES, Please"); yesButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event1) -> { - String ac = "UPDATE ISSUE SET issueTime = CURRENT_TIMESTAMP, renew_count = renew_count+1 WHERE BOOKID = '" + bookID.getText() + "'"; + String ac = "UPDATE ISSUE SET issueTime = CURRENT_TIMESTAMP, renew_count = renew_count+1 WHERE BOOKID = ?"; System.out.println(ac); - if (databaseHandler.execAction(ac)) { + if (databaseHandler.execUpdate(ac, bookID.getText())) { JFXButton btn = new JFXButton("Alright!"); AlertMaker.showMaterialDialog(rootPane, rootAnchorPane, Arrays.asList(btn), "Book Has Been Renewed", null); disableEnableControls(false); diff --git a/test/library/assistant/database/SqlHelperTest.java b/test/library/assistant/database/SqlHelperTest.java new file mode 100644 index 0000000..2ba386e --- /dev/null +++ b/test/library/assistant/database/SqlHelperTest.java @@ -0,0 +1,90 @@ +package library.assistant.database; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +public class SqlHelperTest { + + private Connection conn; + + @Before + public void setUp() throws Exception { + // Use in-memory database for testing + Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance(); + conn = DriverManager.getConnection("jdbc:derby:memory:testdb;create=true"); + try (Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE TEST_TABLE (ID INT PRIMARY KEY, NAME VARCHAR(100))"); + } + } + + @After + public void tearDown() throws Exception { + try { + DriverManager.getConnection("jdbc:derby:memory:testdb;drop=true"); + } catch (Exception e) { + // Derby throws exception on successfull drop + } + } + + @Test + public void testExecUpdate_Insert() throws Exception { + String insert = "INSERT INTO TEST_TABLE (ID, NAME) VALUES (?, ?)"; + boolean result = SqlHelper.execUpdate(conn, insert, 1, "Test Name"); + assertTrue("Insert should return true", result); + + try (Statement stmt = conn.createStatement()) { + ResultSet rs = stmt.executeQuery("SELECT NAME FROM TEST_TABLE WHERE ID = 1"); + assertTrue("Row should exist", rs.next()); + assertEquals("Name should match", "Test Name", rs.getString("NAME")); + } + } + + @Test + public void testExecUpdate_Update() throws Exception { + // Insert first + try (Statement stmt = conn.createStatement()) { + stmt.execute("INSERT INTO TEST_TABLE (ID, NAME) VALUES (2, 'Initial Name')"); + } + + String update = "UPDATE TEST_TABLE SET NAME = ? WHERE ID = ?"; + boolean result = SqlHelper.execUpdate(conn, update, "Updated Name", 2); + assertTrue("Update should return true", result); + + try (Statement stmt = conn.createStatement()) { + ResultSet rs = stmt.executeQuery("SELECT NAME FROM TEST_TABLE WHERE ID = 2"); + assertTrue("Row should exist", rs.next()); + assertEquals("Name should be updated", "Updated Name", rs.getString("NAME")); + } + } + + @Test + public void testExecUpdate_Delete() throws Exception { + // Insert first + try (Statement stmt = conn.createStatement()) { + stmt.execute("INSERT INTO TEST_TABLE (ID, NAME) VALUES (3, 'To Delete')"); + } + + String delete = "DELETE FROM TEST_TABLE WHERE ID = ?"; + boolean result = SqlHelper.execUpdate(conn, delete, 3); + assertTrue("Delete should return true", result); + + try (Statement stmt = conn.createStatement()) { + ResultSet rs = stmt.executeQuery("SELECT count(*) FROM TEST_TABLE WHERE ID = 3"); + rs.next(); + assertEquals("Count should be 0", 0, rs.getInt(1)); + } + } + + @Test + public void testExecUpdate_InvalidSql() { + String invalid = "INSERT INTO NON_EXISTENT_TABLE VALUES (?)"; + boolean result = SqlHelper.execUpdate(conn, invalid, 1); + assertFalse("Invalid SQL should return false", result); + } +}