From d07af7fcf981ece7462ad127aa502892a17eaf7f Mon Sep 17 00:00:00 2001 From: Oskarowski <31699795+Oskarowski@users.noreply.github.com> Date: Thu, 6 Jun 2024 18:48:04 +0200 Subject: [PATCH 01/12] fix: try with resources for DAO component --- .../sudoku/dao/FileSudokuBoardDaoTest.java | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/Dao/src/test/java/sudoku/dao/FileSudokuBoardDaoTest.java b/Dao/src/test/java/sudoku/dao/FileSudokuBoardDaoTest.java index 258c759..ceca3fb 100644 --- a/Dao/src/test/java/sudoku/dao/FileSudokuBoardDaoTest.java +++ b/Dao/src/test/java/sudoku/dao/FileSudokuBoardDaoTest.java @@ -6,8 +6,7 @@ import java.io.File; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; - +import org.junit.jupiter.api.Assertions; import sudoku.dao.factories.SudokuBoardDaoFactory; import sudoku.dao.interfaces.Dao; import sudoku.model.exceptions.InvalidSudokuException; @@ -18,14 +17,6 @@ public class FileSudokuBoardDaoTest { // Directory path for testing private static final String TEST_DIRECTORY = "test_dir"; - // Dao instance for testing - private Dao dao; - - @BeforeEach - void setUp() { - dao = SudokuBoardDaoFactory.createSudokuBoardDao(TEST_DIRECTORY); - } - private void cleanUpDirectory(File directory) { File[] files = directory.listFiles(); if (files != null) { @@ -59,20 +50,24 @@ public void testWriteAndRead() { assertNotNull(sampleBoard); - assertDoesNotThrow(() -> dao.write(boardName, sampleBoard)); + try (Dao dao = SudokuBoardDaoFactory.createSudokuBoardDao(TEST_DIRECTORY)) { + assertDoesNotThrow(() -> dao.write(boardName, sampleBoard)); + + assertTrue(new File(TEST_DIRECTORY, boardName).exists()); - assertTrue(new File(TEST_DIRECTORY, boardName).exists()); + SudokuBoard readBoard = assertDoesNotThrow(() -> dao.read(boardName)); + assertNotNull(readBoard); - SudokuBoard readBoard = assertDoesNotThrow(() -> dao.read(boardName)); + assertEquals(sampleBoard, readBoard); - assertNotNull(readBoard); + assertEquals(sampleBoard.getField(0, 0), readBoard.getField(0, 0)); + assertEquals(sampleBoard.getField(0, 5), readBoard.getField(0, 5)); + assertEquals(sampleBoard.getField(4, 7), readBoard.getField(4, 7)); - assertEquals(sampleBoard, readBoard); + } catch (Exception e) { + Assertions.fail(); + } - assertEquals(sampleBoard.getField(0, 0), readBoard.getField(0, 0)); - assertEquals(sampleBoard.getField(0, 5), readBoard.getField(0, 5)); - assertEquals(sampleBoard.getField(4, 7), readBoard.getField(4, 7)); } - } From 8fa98c2bd3de587018300dc723c3047119b32c4e Mon Sep 17 00:00:00 2001 From: Oskarowski <31699795+Oskarowski@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:41:35 +0200 Subject: [PATCH 02/12] init new mvn module --- JdbcDao/pom.xml | 77 +++++++++++++++++++ .../src/main/java/sudoku/jdbcdao/Main.java | 7 ++ pom.xml | 6 ++ 3 files changed, 90 insertions(+) create mode 100644 JdbcDao/pom.xml create mode 100644 JdbcDao/src/main/java/sudoku/jdbcdao/Main.java diff --git a/JdbcDao/pom.xml b/JdbcDao/pom.xml new file mode 100644 index 0000000..c29a88b --- /dev/null +++ b/JdbcDao/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + + sudoku.root + SudokuGameProject + 2.0-SNAPSHOT + + + sudoku.jdbcdao + JdbcDaoProject + jar + + + + sudoku.model + ModelProject + ${project.version} + + + org.junit.jupiter + junit-jupiter-params + + + org.junit.jupiter + junit-jupiter-engine + + + com.puppycrawl.tools + checkstyle + + + org.apache.commons + commons-lang3 + + + org.slf4j + slf4j-api + 1.7.16 + + + ch.qos.logback + logback-classic + 1.2.6 + + + org.xerial + sqlite-jdbc + + + + + + + org.jacoco + jacoco-maven-plugin + + + + + + + 247026 + Oskar Kacprzak + 247026@edu.p.lodz.pl + + + 247027 + Wojciech Kapica + 247027@edu.p.lodz.pl + + + + \ No newline at end of file diff --git a/JdbcDao/src/main/java/sudoku/jdbcdao/Main.java b/JdbcDao/src/main/java/sudoku/jdbcdao/Main.java new file mode 100644 index 0000000..b04d38f --- /dev/null +++ b/JdbcDao/src/main/java/sudoku/jdbcdao/Main.java @@ -0,0 +1,7 @@ +package sudoku.jdbcdao; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 197bded..6c616d1 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ Model Dao View + JdbcDao @@ -55,6 +56,11 @@ + + org.xerial + sqlite-jdbc + 3.36.0.3 + org.junit.jupiter junit-jupiter-params From b3b26a775a654c0db1df4e7f491e0a53cc815a91 Mon Sep 17 00:00:00 2001 From: Oskarowski <31699795+Oskarowski@users.noreply.github.com> Date: Thu, 6 Jun 2024 23:14:03 +0200 Subject: [PATCH 03/12] come up with some implementation of JDBCDao with SQLite --- JdbcDao/pom.xml | 5 + .../sudoku/jdbcdao/JdbcSudokuBoardDao.java | 149 ++++++++++++++++++ .../jdbcdao/JdbcSudokuBoardDaoTest.java | 94 +++++++++++ 3 files changed, 248 insertions(+) create mode 100644 JdbcDao/src/main/java/sudoku/jdbcdao/JdbcSudokuBoardDao.java create mode 100644 JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java diff --git a/JdbcDao/pom.xml b/JdbcDao/pom.xml index c29a88b..c01c8ea 100644 --- a/JdbcDao/pom.xml +++ b/JdbcDao/pom.xml @@ -20,6 +20,11 @@ ModelProject ${project.version} + + sudoku.dao + DaoProject + ${project.version} + org.junit.jupiter junit-jupiter-params diff --git a/JdbcDao/src/main/java/sudoku/jdbcdao/JdbcSudokuBoardDao.java b/JdbcDao/src/main/java/sudoku/jdbcdao/JdbcSudokuBoardDao.java new file mode 100644 index 0000000..2c0885d --- /dev/null +++ b/JdbcDao/src/main/java/sudoku/jdbcdao/JdbcSudokuBoardDao.java @@ -0,0 +1,149 @@ +package sudoku.jdbcdao; + +import java.sql.Connection; +import java.sql.DriverManager; +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 sudoku.dao.interfaces.Dao; +import sudoku.model.models.SudokuBoard; +import sudoku.model.solver.BacktrackingSudokuSolver; + +public class JdbcSudokuBoardDao implements Dao { + private static final String DB_URL = "jdbc:sqlite:db/sudoku.db"; + private Connection connection; + + public JdbcSudokuBoardDao() throws SQLException { + connection = DriverManager.getConnection(DB_URL); + createTablesIfNotExist(); + } + + public JdbcSudokuBoardDao(String connectionString) throws SQLException { + connection = DriverManager.getConnection(connectionString); + createTablesIfNotExist(); + } + + private void createTablesIfNotExist() throws SQLException { + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS SudokuBoard (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "name TEXT UNIQUE)"); + + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS SudokuField (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "board_id INTEGER, " + + "row INTEGER, " + + "column INTEGER, " + + "value INTEGER, " + + "FOREIGN KEY(board_id) REFERENCES SudokuBoard(id))"); + } + } + + @Override + public void write(String name, SudokuBoard obj) { + try { + connection.setAutoCommit(false); // Start transaction + + // Insert the board + try (PreparedStatement insertBoardStmt = connection.prepareStatement( + "INSERT INTO SudokuBoard (name) VALUES (?)", Statement.RETURN_GENERATED_KEYS)) { + + insertBoardStmt.setString(1, name); + insertBoardStmt.executeUpdate(); + + int boardId; + try (ResultSet generatedKeys = insertBoardStmt.getGeneratedKeys()) { + if (generatedKeys.next()) { + boardId = generatedKeys.getInt(1); + } else { + connection.rollback(); + throw new SQLException("Creating board failed, no ID obtained."); + } + } + + // Insert fields + try (PreparedStatement insertFieldStmt = connection.prepareStatement( + "INSERT INTO SudokuField (board_id, row, column, value) VALUES (?, ?, ?, ?)")) { + + for (int row = 0; row < 9; row++) { + for (int col = 0; col < 9; col++) { + insertFieldStmt.setInt(1, boardId); + insertFieldStmt.setInt(2, row); + insertFieldStmt.setInt(3, col); + insertFieldStmt.setInt(4, obj.getField(row, col).getValue()); + insertFieldStmt.addBatch(); + } + } + insertFieldStmt.executeBatch(); + } + + connection.commit(); // Commit transaction + } catch (SQLException e) { + connection.rollback(); // Rollback transaction on error + throw e; + } finally { + connection.setAutoCommit(true); // Restore auto-commit mode + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @Override + public SudokuBoard read(String name) { + SudokuBoard board = null; + try (PreparedStatement selectBoardStmt = connection.prepareStatement( + "SELECT id FROM SudokuBoard WHERE name = ?"); + PreparedStatement selectFieldsStmt = connection.prepareStatement( + "SELECT row, column, value FROM SudokuField WHERE board_id = ?")) { + + selectBoardStmt.setString(1, name); + try (ResultSet boardRs = selectBoardStmt.executeQuery()) { + if (boardRs.next()) { + int boardId = boardRs.getInt("id"); + + board = new SudokuBoard(new BacktrackingSudokuSolver()); + + selectFieldsStmt.setInt(1, boardId); + try (ResultSet fieldsRs = selectFieldsStmt.executeQuery()) { + while (fieldsRs.next()) { + int row = fieldsRs.getInt("row"); + int col = fieldsRs.getInt("column"); + int value = fieldsRs.getInt("value"); + board.setField(row, col, value); + } + } + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return board; + } + + @Override + public List names() { + List names = new ArrayList<>(); + try (Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT name FROM SudokuBoard")) { + + while (rs.next()) { + names.add(rs.getString("name")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return names; + } + + @Override + public void close() throws SQLException { + if (connection != null) { + connection.close(); + } + } +} diff --git a/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java b/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java new file mode 100644 index 0000000..17fe999 --- /dev/null +++ b/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java @@ -0,0 +1,94 @@ +package sudoku.jdbcdao; + +import org.junit.jupiter.api.*; +import sudoku.dao.interfaces.Dao; +import sudoku.model.models.SudokuBoard; +import sudoku.model.solver.BacktrackingSudokuSolver; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import static org.junit.jupiter.api.Assertions.*; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class JdbcSudokuBoardDaoTest { + + private static final String TEST_DB_URL = "jdbc:sqlite:test_sudoku.db"; + private Connection connection; + + @BeforeAll + public void setUp() throws SQLException { + connection = DriverManager.getConnection(TEST_DB_URL); + try (var statement = connection.createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS SudokuBoard (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "name TEXT UNIQUE)"); + statement.execute("CREATE TABLE IF NOT EXISTS SudokuField (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "board_id INTEGER, " + + "row INTEGER, " + + "column INTEGER, " + + "value INTEGER, " + + "FOREIGN KEY(board_id) REFERENCES SudokuBoard(id))"); + } + } + + @AfterEach + public void tearDown() throws SQLException { + try (var statement = connection.createStatement()) { + statement.execute("DELETE FROM SudokuField"); + statement.execute("DELETE FROM SudokuBoard"); + } + } + + @AfterAll + public void cleanUp() throws SQLException { + try (var statement = connection.createStatement()) { + statement.execute("DROP TABLE SudokuField"); + statement.execute("DROP TABLE SudokuBoard"); + } + connection.close(); + } + + @Test + public void testWriteAndRead() { + try (Dao dao = new JdbcSudokuBoardDao(TEST_DB_URL)) { + SudokuBoard board = new SudokuBoard(new BacktrackingSudokuSolver()); + board.setField(0, 0, 5); + board.setField(1, 1, 3); + dao.write("testBoard", board); + + SudokuBoard readBoard = dao.read("testBoard"); + + assertEquals(5, readBoard.getField(0, 0).getValue()); + assertEquals(3, readBoard.getField(1, 1).getValue()); + } catch (Exception e) { + fail(e); + } + } + + @Test + public void testNames() { + try (Dao dao = new JdbcSudokuBoardDao(TEST_DB_URL)) { + dao.write("testBoard1", new SudokuBoard(new BacktrackingSudokuSolver())); + dao.write("testBoard2", new SudokuBoard(new BacktrackingSudokuSolver())); + + var names = dao.names(); + + assertTrue(names.contains("testBoard1")); + assertTrue(names.contains("testBoard2")); + } catch (Exception e) { + fail(e); + } + } + + @Test + public void testReadNonExistentBoard() { + try (Dao dao = new JdbcSudokuBoardDao(TEST_DB_URL)) { + assertNull(dao.read("nonExistentBoard")); + } catch (Exception e) { + fail(e); + } + } +} From a3b8b223b4c63b273cab6c2bb4d45ec4e1e23af6 Mon Sep 17 00:00:00 2001 From: Oskarowski <31699795+Oskarowski@users.noreply.github.com> Date: Sat, 8 Jun 2024 17:55:17 +0200 Subject: [PATCH 04/12] add JOOQ --- JdbcDao/pom.xml | 120 ++++++++++++ .../sudoku/jdbcdao/JdbcSudokuBoardDao.java | 182 ++++++++---------- .../src/main/java/sudoku/jdbcdao/Main.java | 7 - .../sudoku/jdbcdao/SchemaInitializer.java | 24 +++ .../java/sudoku/jdbcdao/database/schema.sql | 13 ++ .../jdbcdao/JdbcSudokuBoardDaoTest.java | 53 ++--- pom.xml | 32 +++ 7 files changed, 300 insertions(+), 131 deletions(-) delete mode 100644 JdbcDao/src/main/java/sudoku/jdbcdao/Main.java create mode 100644 JdbcDao/src/main/java/sudoku/jdbcdao/SchemaInitializer.java create mode 100644 JdbcDao/src/main/java/sudoku/jdbcdao/database/schema.sql diff --git a/JdbcDao/pom.xml b/JdbcDao/pom.xml index c01c8ea..97a1e0a 100644 --- a/JdbcDao/pom.xml +++ b/JdbcDao/pom.xml @@ -55,14 +55,134 @@ org.xerial sqlite-jdbc + + org.jooq + jooq + + + org.jooq + jooq-meta + + + org.jooq + jooq-codegen + + src/main/java + src/test/java org.jacoco jacoco-maven-plugin + + + org.jooq + jooq-codegen-maven + 3.19.9 + + + generate-sources + generate-sources + + generate + + + + org.sqlite.JDBC + jdbc:sqlite:sudoku.db + + + + org.jooq.meta.sqlite.SQLiteDatabase + + + sudokujdbc.jooq.generated + target/generated-sources/jooq + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + default-compile + compile + + compile + + + + default-testCompile + test-compile + + testCompile + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + + add-source + generate-sources + + add-source + + + + target/generated-sources/jooq + + + + + add-test-source + generate-test-sources + + add-test-source + + + + target/generated-sources/jooq + + + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + + src/main/java + + **/*.java + + + target/generated-sources/jooq/** + + + + + + + + diff --git a/JdbcDao/src/main/java/sudoku/jdbcdao/JdbcSudokuBoardDao.java b/JdbcDao/src/main/java/sudoku/jdbcdao/JdbcSudokuBoardDao.java index 2c0885d..2adcc03 100644 --- a/JdbcDao/src/main/java/sudoku/jdbcdao/JdbcSudokuBoardDao.java +++ b/JdbcDao/src/main/java/sudoku/jdbcdao/JdbcSudokuBoardDao.java @@ -2,142 +2,124 @@ import java.sql.Connection; import java.sql.DriverManager; -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 org.jooq.DSLContext; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; + import sudoku.dao.interfaces.Dao; import sudoku.model.models.SudokuBoard; import sudoku.model.solver.BacktrackingSudokuSolver; +import static sudokujdbc.jooq.generated.Tables.SUDOKU_BOARD; +import static sudokujdbc.jooq.generated.Tables.SUDOKU_FIELD; + public class JdbcSudokuBoardDao implements Dao { - private static final String DB_URL = "jdbc:sqlite:db/sudoku.db"; + private final String url; private Connection connection; + private DSLContext dsl; - public JdbcSudokuBoardDao() throws SQLException { - connection = DriverManager.getConnection(DB_URL); - createTablesIfNotExist(); + public JdbcSudokuBoardDao(String url) { + this.url = url; } - public JdbcSudokuBoardDao(String connectionString) throws SQLException { - connection = DriverManager.getConnection(connectionString); - createTablesIfNotExist(); - } - - private void createTablesIfNotExist() throws SQLException { - try (Statement stmt = connection.createStatement()) { - stmt.executeUpdate("CREATE TABLE IF NOT EXISTS SudokuBoard (" + - "id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "name TEXT UNIQUE)"); - - stmt.executeUpdate("CREATE TABLE IF NOT EXISTS SudokuField (" + - "id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "board_id INTEGER, " + - "row INTEGER, " + - "column INTEGER, " + - "value INTEGER, " + - "FOREIGN KEY(board_id) REFERENCES SudokuBoard(id))"); - } + private void connect() throws Exception { + connection = DriverManager.getConnection(url); + dsl = DSL.using(connection, SQLDialect.SQLITE); } @Override - public void write(String name, SudokuBoard obj) { + public void write(String name, SudokuBoard board) { try { + connect(); connection.setAutoCommit(false); // Start transaction - // Insert the board - try (PreparedStatement insertBoardStmt = connection.prepareStatement( - "INSERT INTO SudokuBoard (name) VALUES (?)", Statement.RETURN_GENERATED_KEYS)) { - - insertBoardStmt.setString(1, name); - insertBoardStmt.executeUpdate(); - - int boardId; - try (ResultSet generatedKeys = insertBoardStmt.getGeneratedKeys()) { - if (generatedKeys.next()) { - boardId = generatedKeys.getInt(1); - } else { - connection.rollback(); - throw new SQLException("Creating board failed, no ID obtained."); - } - } - - // Insert fields - try (PreparedStatement insertFieldStmt = connection.prepareStatement( - "INSERT INTO SudokuField (board_id, row, column, value) VALUES (?, ?, ?, ?)")) { - - for (int row = 0; row < 9; row++) { - for (int col = 0; col < 9; col++) { - insertFieldStmt.setInt(1, boardId); - insertFieldStmt.setInt(2, row); - insertFieldStmt.setInt(3, col); - insertFieldStmt.setInt(4, obj.getField(row, col).getValue()); - insertFieldStmt.addBatch(); - } - } - insertFieldStmt.executeBatch(); + var boardId = dsl.insertInto(SUDOKU_BOARD) + .columns(SUDOKU_BOARD.NAME) + .values(name) + .returning(SUDOKU_BOARD.ID) + .fetchOne() + .getId(); + + for (int row = 0; row < 9; row++) { + for (int col = 0; col < 9; col++) { + int value = board.getField(row, col).getValue(); + dsl.insertInto(SUDOKU_FIELD) + .columns(SUDOKU_FIELD.BOARD_ID, SUDOKU_FIELD.ROW, SUDOKU_FIELD.COLUMN, SUDOKU_FIELD.VALUE) + .values(boardId, row, col, value) + .execute(); } + } - connection.commit(); // Commit transaction - } catch (SQLException e) { + connection.commit(); // Commit transaction + } catch (Exception e) { + try { connection.rollback(); // Rollback transaction on error - throw e; - } finally { - connection.setAutoCommit(true); // Restore auto-commit mode + } catch (SQLException ex) { + throw new RuntimeException(ex); } - } catch (SQLException e) { - e.printStackTrace(); + throw new RuntimeException(e); + } finally { + closeConnection(); } } @Override public SudokuBoard read(String name) { - SudokuBoard board = null; - try (PreparedStatement selectBoardStmt = connection.prepareStatement( - "SELECT id FROM SudokuBoard WHERE name = ?"); - PreparedStatement selectFieldsStmt = connection.prepareStatement( - "SELECT row, column, value FROM SudokuField WHERE board_id = ?")) { - - selectBoardStmt.setString(1, name); - try (ResultSet boardRs = selectBoardStmt.executeQuery()) { - if (boardRs.next()) { - int boardId = boardRs.getInt("id"); - - board = new SudokuBoard(new BacktrackingSudokuSolver()); - - selectFieldsStmt.setInt(1, boardId); - try (ResultSet fieldsRs = selectFieldsStmt.executeQuery()) { - while (fieldsRs.next()) { - int row = fieldsRs.getInt("row"); - int col = fieldsRs.getInt("column"); - int value = fieldsRs.getInt("value"); - board.setField(row, col, value); - } - } - } + try { + connect(); + + var boardRecord = dsl.selectFrom(SUDOKU_BOARD) + .where(SUDOKU_BOARD.NAME.eq(name)) + .fetchOne(); + + if (boardRecord == null) { + return null; } - } catch (SQLException e) { - e.printStackTrace(); + + SudokuBoard board = new SudokuBoard(new BacktrackingSudokuSolver()); + + dsl.selectFrom(SUDOKU_FIELD) + .where(SUDOKU_FIELD.BOARD_ID.eq(boardRecord.getId())) + .forEach(record -> { + int row = record.getRow(); + int col = record.getColumn(); + int value = record.getValue(); + board.setField(row, col, value); + }); + + return board; + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + closeConnection(); } - return board; } @Override public List names() { - List names = new ArrayList<>(); - try (Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT name FROM SudokuBoard")) { + try { + connect(); + return dsl.select(SUDOKU_BOARD.NAME) + .from(SUDOKU_BOARD) + .fetch(SUDOKU_BOARD.NAME); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + closeConnection(); + } + } - while (rs.next()) { - names.add(rs.getString("name")); + private void closeConnection() { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); } - } catch (SQLException e) { - e.printStackTrace(); } - return names; } @Override diff --git a/JdbcDao/src/main/java/sudoku/jdbcdao/Main.java b/JdbcDao/src/main/java/sudoku/jdbcdao/Main.java deleted file mode 100644 index b04d38f..0000000 --- a/JdbcDao/src/main/java/sudoku/jdbcdao/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package sudoku.jdbcdao; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello world!"); - } -} \ No newline at end of file diff --git a/JdbcDao/src/main/java/sudoku/jdbcdao/SchemaInitializer.java b/JdbcDao/src/main/java/sudoku/jdbcdao/SchemaInitializer.java new file mode 100644 index 0000000..43e54cc --- /dev/null +++ b/JdbcDao/src/main/java/sudoku/jdbcdao/SchemaInitializer.java @@ -0,0 +1,24 @@ +package sudoku.jdbcdao; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; + + +public class SchemaInitializer { + private static final String DB_URL = "jdbc:sqlite:sudoku.db"; + + public static void main(String[] args) { + try (Connection conn = DriverManager.getConnection(DB_URL); + Statement stmt = conn.createStatement()) { + + String sql = new String(Files.readAllBytes(Paths.get("src/main/database/schema.sql"))); + stmt.executeUpdate(sql); + System.out.println("Database schema initialized."); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/JdbcDao/src/main/java/sudoku/jdbcdao/database/schema.sql b/JdbcDao/src/main/java/sudoku/jdbcdao/database/schema.sql new file mode 100644 index 0000000..2181d66 --- /dev/null +++ b/JdbcDao/src/main/java/sudoku/jdbcdao/database/schema.sql @@ -0,0 +1,13 @@ +CREATE TABLE IF NOT EXISTS SUDOKU_BOARD ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE +); + +CREATE TABLE IF NOT EXISTS SUDOKU_FIELD ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + board_id INTEGER, + row INTEGER, + column INTEGER, + value INTEGER, + FOREIGN KEY (board_id) REFERENCES SUDOKU_BOARD (id) +); diff --git a/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java b/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java index 17fe999..081f860 100644 --- a/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java +++ b/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java @@ -1,53 +1,58 @@ package sudoku.jdbcdao; +import org.jooq.DSLContext; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; import org.junit.jupiter.api.*; import sudoku.dao.interfaces.Dao; import sudoku.model.models.SudokuBoard; import sudoku.model.solver.BacktrackingSudokuSolver; +import java.io.File; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import static org.junit.jupiter.api.Assertions.*; +import static sudokujdbc.jooq.generated.Tables.SUDOKU_BOARD; +import static sudokujdbc.jooq.generated.Tables.SUDOKU_FIELD; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class JdbcSudokuBoardDaoTest { - private static final String TEST_DB_URL = "jdbc:sqlite:test_sudoku.db"; + private static final String TEST_DB_URL = "jdbc:sqlite:test/java/sudoku/jdbcdao/test_sudoku.db"; private Connection connection; + private DSLContext dsl; - @BeforeAll - public void setUp() throws SQLException { - connection = DriverManager.getConnection(TEST_DB_URL); - try (var statement = connection.createStatement()) { - statement.execute("CREATE TABLE IF NOT EXISTS SudokuBoard (" + - "id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "name TEXT UNIQUE)"); - statement.execute("CREATE TABLE IF NOT EXISTS SudokuField (" + - "id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "board_id INTEGER, " + - "row INTEGER, " + - "column INTEGER, " + - "value INTEGER, " + - "FOREIGN KEY(board_id) REFERENCES SudokuBoard(id))"); + + @BeforeAll + public void setUp() throws SQLException { + File dbFile = new File("test/java/sudoku/jdbcdao/test_sudoku.db"); + dbFile.getParentFile().mkdirs(); // Ensure directories exist + + connection = DriverManager.getConnection(TEST_DB_URL); + dsl = DSL.using(connection, SQLDialect.SQLITE); + + dsl.createTableIfNotExists(SUDOKU_BOARD) + .columns(SUDOKU_BOARD.fields()) + .execute(); + + dsl.createTableIfNotExists(SUDOKU_FIELD) + .columns(SUDOKU_FIELD.fields()) + .execute(); } - } + @AfterEach public void tearDown() throws SQLException { - try (var statement = connection.createStatement()) { - statement.execute("DELETE FROM SudokuField"); - statement.execute("DELETE FROM SudokuBoard"); - } + dsl.deleteFrom(SUDOKU_FIELD).execute(); + dsl.deleteFrom(SUDOKU_BOARD).execute(); } @AfterAll public void cleanUp() throws SQLException { - try (var statement = connection.createStatement()) { - statement.execute("DROP TABLE SudokuField"); - statement.execute("DROP TABLE SudokuBoard"); - } + dsl.dropTableIfExists(SUDOKU_FIELD).execute(); + dsl.dropTableIfExists(SUDOKU_BOARD).execute(); connection.close(); } diff --git a/pom.xml b/pom.xml index 6c616d1..70431e9 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,21 @@ + + org.jooq + jooq + 3.19.9 + + + org.jooq + jooq-meta + 3.19.9 + + + org.jooq + jooq-codegen + 3.19.9 + org.xerial sqlite-jdbc @@ -290,6 +305,23 @@ + + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + \ No newline at end of file From 0cd753baab1e08cb342767876bcd6818452b98b2 Mon Sep 17 00:00:00 2001 From: Oskarowski <31699795+Oskarowski@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:41:51 +0200 Subject: [PATCH 05/12] fixes --- JdbcDao/pom.xml | 11 ++++++++--- .../sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java | 5 +---- .../sudoku/jdbcdao/resources/test_sudoku.db | Bin 0 -> 20480 bytes pom.xml | 6 ++++++ 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 JdbcDao/src/test/java/sudoku/jdbcdao/resources/test_sudoku.db diff --git a/JdbcDao/pom.xml b/JdbcDao/pom.xml index 97a1e0a..eae8112 100644 --- a/JdbcDao/pom.xml +++ b/JdbcDao/pom.xml @@ -71,7 +71,13 @@ src/main/java + + + src/test/resources + + src/test/java + org.jacoco @@ -81,7 +87,6 @@ org.jooq jooq-codegen-maven - 3.19.9 generate-sources @@ -129,7 +134,7 @@ - + diff --git a/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java b/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java index 081f860..7153c67 100644 --- a/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java +++ b/JdbcDao/src/test/java/sudoku/jdbcdao/JdbcSudokuBoardDaoTest.java @@ -20,16 +20,13 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class JdbcSudokuBoardDaoTest { - private static final String TEST_DB_URL = "jdbc:sqlite:test/java/sudoku/jdbcdao/test_sudoku.db"; + private static final String TEST_DB_URL = "jdbc:sqlite:target/test_sudoku.db"; private Connection connection; private DSLContext dsl; @BeforeAll public void setUp() throws SQLException { - File dbFile = new File("test/java/sudoku/jdbcdao/test_sudoku.db"); - dbFile.getParentFile().mkdirs(); // Ensure directories exist - connection = DriverManager.getConnection(TEST_DB_URL); dsl = DSL.using(connection, SQLDialect.SQLITE); diff --git a/JdbcDao/src/test/java/sudoku/jdbcdao/resources/test_sudoku.db b/JdbcDao/src/test/java/sudoku/jdbcdao/resources/test_sudoku.db new file mode 100644 index 0000000000000000000000000000000000000000..9fc05154c614f557fedb2b2fb5129bd9d5e847e0 GIT binary patch literal 20480 zcmeI%&uZH+9KdloO}i2B(p#>&1QJ?S#u&TKZIoi-+O1>5au~*KJP0K5k|yoiTj+!J zNp{#<>^RwrsT=5J^c21V+4^Hi{(Y>AaR0&?EfV=XoloM0{3!N?VTdnM3Lz>w@9JEn ztX?fD`filUFBL1|{rwNK{zp{J-=cn0e|+~#7f1*ofB*srAbxEI*R!*l}-aYY^6(|{4$BvTa9@#Q$Qm4^S+HRmul`qeI`^@qO zvZn^p3Ioq}_2@I@28aJ1y-efzFnSuWZl0&N#T4t7t8~1aOg9>D;_))sXzO~uvQOQ7 z#a1zWTl%W2eC3{~e(}o3p5=FBYcy<|wf6bWUgO}v803*(&&K+s8(k-}Wiq`=HmcQU z@o#jtrtu^>)Fay~)5tYf-^!Z#qHD9fTzNKIx#vGLv&Chgz6R3ubPgRyhOT`PDjnhH z%3h<_G{k5+On$E4I$kc)ydISojy{xYJ9?wN%{H5vUG}pJ2>}EUKmY**5I_I{1Q0*~ z0R*;Hpq2&3`F~qKFL5D&00IagfB*srAb3.8.1 + + org.jooq + jooq-codegen-maven + 3.19.9 + + \ No newline at end of file From 66edd621fda477db2a431dd51b93a156ec9e9be1 Mon Sep 17 00:00:00 2001 From: Oskarowski <31699795+Oskarowski@users.noreply.github.com> Date: Sat, 8 Jun 2024 22:38:07 +0200 Subject: [PATCH 06/12] working saving to DB, remember to do migrations on DB --- Dao/pom.xml | 5 +++ .../dao/factories/SudokuBoardDaoFactory.java | 6 +++ JdbcDao/pom.xml | 6 +++ View/pom.xml | 5 +++ .../main/java/sudoku/view/GameController.java | 43 +++++++++++++++++-- .../resources/sudoku/view/SudokuGame.fxml | 8 +++- .../sudoku/view/bundles/en_EN.properties | 3 +- .../sudoku/view/bundles/pl_PL.properties | 3 +- 8 files changed, 73 insertions(+), 6 deletions(-) diff --git a/Dao/pom.xml b/Dao/pom.xml index 7e1b5fe..45819af 100644 --- a/Dao/pom.xml +++ b/Dao/pom.xml @@ -20,6 +20,11 @@ ModelProject ${project.version} + + sudoku.jdbcdao + JdbcDaoProject + ${project.version} + org.junit.jupiter junit-jupiter-params diff --git a/Dao/src/main/java/sudoku/dao/factories/SudokuBoardDaoFactory.java b/Dao/src/main/java/sudoku/dao/factories/SudokuBoardDaoFactory.java index af63968..2e98783 100644 --- a/Dao/src/main/java/sudoku/dao/factories/SudokuBoardDaoFactory.java +++ b/Dao/src/main/java/sudoku/dao/factories/SudokuBoardDaoFactory.java @@ -2,6 +2,7 @@ import sudoku.dao.interfaces.Dao; import sudoku.dao.models.FileSudokuBoardDao; +import sudoku.jdbcdao.JdbcSudokuBoardDao; import sudoku.model.models.SudokuBoard; public class SudokuBoardDaoFactory { @@ -23,4 +24,9 @@ private SudokuBoardDaoFactory() { public static Dao createSudokuBoardDao(String dirPath) { return new FileSudokuBoardDao(dirPath); } + + public static Dao createJdbcSudokuBoardDao(){ + // load from .env file + return new JdbcSudokuBoardDao("jdbc:sqlite:sudoku.db"); + } } diff --git a/JdbcDao/pom.xml b/JdbcDao/pom.xml index eae8112..ed664e4 100644 --- a/JdbcDao/pom.xml +++ b/JdbcDao/pom.xml @@ -25,6 +25,12 @@ DaoProject ${project.version} + + sudoku.jdbcdao + JdbcDaoProject + ${project.version} + + org.junit.jupiter junit-jupiter-params diff --git a/View/pom.xml b/View/pom.xml index 221b1b3..c8248ef 100644 --- a/View/pom.xml +++ b/View/pom.xml @@ -33,6 +33,11 @@ DaoProject ${project.version} + + sudoku.jdbcdao + JdbcDaoProject + ${project.version} + org.junit.jupiter diff --git a/View/src/main/java/sudoku/view/GameController.java b/View/src/main/java/sudoku/view/GameController.java index 53c6499..0249928 100644 --- a/View/src/main/java/sudoku/view/GameController.java +++ b/View/src/main/java/sudoku/view/GameController.java @@ -20,6 +20,7 @@ import sudoku.dao.exceptions.SudokuReadException; import sudoku.dao.factories.SudokuBoardDaoFactory; import sudoku.dao.interfaces.Dao; +import sudoku.jdbcdao.JdbcSudokuBoardDao; import sudoku.model.exceptions.InvalidSudokuException; import sudoku.model.models.SudokuBoard; import sudoku.model.models.SudokuField; @@ -32,7 +33,9 @@ public class GameController implements Initializable { @FXML - private Button saveGameButton; + private Button saveGameToFileButton; + @FXML + private Button saveGameToDatabaseButton; private DifficultyEnum gameDifficulty; private int gameBoardSize; @@ -60,7 +63,8 @@ public GameController(DifficultyEnum initialGameDifficulty, SudokuBoard sudokuBo public void initialize(URL location, ResourceBundle resources) { logger.info("Sudoku Game Controller Initialized"); - saveGameButton.setOnAction(event -> saveSudokuGameToFile()); + saveGameToFileButton.setOnAction(event -> saveSudokuGameToFile()); + saveGameToDatabaseButton.setOnAction(event -> saveSudokuGameToDatabase()); if (sudokuBoard == null) { sudokuBoard = new SudokuBoard(new BacktrackingSudokuSolver()); @@ -164,7 +168,7 @@ private void saveSudokuGameToFile() { DirectoryChooser directoryChooser = new DirectoryChooser(); directoryChooser.setTitle("Choose Where To Save Sudoku Board"); - File selectedDirectory = directoryChooser.showDialog(saveGameButton.getScene().getWindow()); + File selectedDirectory = directoryChooser.showDialog(saveGameToFileButton.getScene().getWindow()); if (selectedDirectory != null) { logger.info("Directory Path: " + selectedDirectory); @@ -195,4 +199,37 @@ private void saveSudokuGameToFile() { e.printStackTrace(); } } + + private void saveSudokuGameToDatabase() { + logger.info("Try To Save Sudoku Game In Database"); + + try { + // Prompt the user to enter the name under which to save the Sudoku board + TextInputDialog dialog = new TextInputDialog(); + dialog.setTitle("Enter Game Name"); + dialog.setHeaderText("Please enter the name for the Sudoku board game:"); + dialog.setContentText("Game Name:"); + + Optional result = dialog.showAndWait(); + + if (result.isEmpty()) { + return; + } + + String gameName = result.get(); + logger.info("Game Name: " + gameName); + + // Create the JdbcSudokuBoardDao instance + try (Dao sudokuBoardDao = new JdbcSudokuBoardDao("jdbc:sqlite:sudoku.db")) { + sudokuBoardDao.write(gameName, this.sudokuBoard); + logger.info("Sudoku board saved to database with name: " + gameName); + } catch (Exception e) { + logger.error("Error occurred while saving sudoku board to database", e); + throw e; + } + } catch (Exception e) { + e.printStackTrace(); + logger.error("Unexpected error occurred", e); + } + } } diff --git a/View/src/main/resources/sudoku/view/SudokuGame.fxml b/View/src/main/resources/sudoku/view/SudokuGame.fxml index 9d9856a..381639c 100644 --- a/View/src/main/resources/sudoku/view/SudokuGame.fxml +++ b/View/src/main/resources/sudoku/view/SudokuGame.fxml @@ -5,6 +5,7 @@ + @@ -23,6 +24,11 @@ -