diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..0c41b73
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ mariadb
+ true
+ org.mariadb.jdbc.Driver
+ jdbc:mariadb://localhost:3306/bolts
+
+
+
+
+
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/data_source_mapping.xml b/.idea/data_source_mapping.xml
new file mode 100644
index 0000000..0517061
--- /dev/null
+++ b/.idea/data_source_mapping.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index b5bac53..e8009fd 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/pom.xml b/pom.xml
index 869f814..4e1fc1a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -134,15 +134,18 @@
2.0.16
+
- org.jetbrains.kotlin
- kotlin-scripting-jvm
- ${kotlin.version}
+ commons-io
+ commons-io
+ 2.20.0
+
+
- org.jetbrains.kotlin
- kotlin-scripting-common
- ${kotlin.version}
+ org.apache.commons
+ commons-lang3
+ 3.18.0
diff --git a/src/main/java/dev/mduchene/App.java b/src/main/java/dev/mduchene/App.java
index b780084..1cfc5f1 100644
--- a/src/main/java/dev/mduchene/App.java
+++ b/src/main/java/dev/mduchene/App.java
@@ -4,16 +4,25 @@ import io.javalin.Javalin;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ScalarHandler;
+import org.apache.commons.io.FileUtils;
+import org.jooq.impl.DSL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
+import java.time.LocalDateTime;
import java.util.List;
/** Hello world! */
public class App {
public static void main(String[] args) {
+ System.setProperty("org.jooq.no-tips", "true");
+ System.setProperty("org.jooq.no-logo", "true");
+
Db db =
Db.Builder.create()
.url("jdbc:mariadb://localhost:3306/bolts")
@@ -22,7 +31,22 @@ public class App {
.build();
db.init();
- Migration.of(db).getPendingBaseMigrations().forEach(f -> System.out.println(f.getName()));
+ List pendingBaseMigrations = Migration.of(db).getPendingBaseMigrations();
+ for (File file : pendingBaseMigrations) {
+ LoggerFactory.getLogger(App.class).info("Running migration: {}", file.getName());
+ try (Connection connection = db.getConnection()) {
+ String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
+ db.run(connection, content);
+ DSL.using(connection)
+ .insertInto(DSL.table("_migrations"))
+ .columns(DSL.field("name"), DSL.field("base"), DSL.field("executedAt"))
+ .values(
+ file.getName(), true, LocalDateTime.now()) // executedAt is null for base migrations
+ .execute();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
var app = Javalin.create(cnf -> {});
app.before(
diff --git a/src/main/java/dev/mduchene/Db.java b/src/main/java/dev/mduchene/Db.java
index 0591cc4..4497bff 100644
--- a/src/main/java/dev/mduchene/Db.java
+++ b/src/main/java/dev/mduchene/Db.java
@@ -84,13 +84,18 @@ public class Db {
public void run(String query) {
try (Connection connection = getConnection()) {
- dsl.execute(query);
- connection.commit();
+ run(connection, query);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
- public DSLContext dsl() {
- return dsl;
+
+ public void run(Connection connection, String query) {
+ try {
+ DSL.using(connection).execute(query);
+ connection.commit();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
}
}
diff --git a/src/main/java/dev/mduchene/Migration.java b/src/main/java/dev/mduchene/Migration.java
index 014955f..e728891 100644
--- a/src/main/java/dev/mduchene/Migration.java
+++ b/src/main/java/dev/mduchene/Migration.java
@@ -1,20 +1,14 @@
package dev.mduchene;
-import org.jooq.Meta;
-import org.jooq.impl.DSL;
-
import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.sql.Connection;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
+import java.sql.ResultSet;
import java.util.List;
-import java.util.Objects;
import java.util.stream.Collectors;
+import org.jooq.Meta;
+import org.jooq.Record1;
+import org.jooq.SelectConditionStep;
+import org.jooq.impl.DSL;
public class Migration {
@@ -38,14 +32,19 @@ public class Migration {
public boolean tableExists(String tableName) {
// Get the Meta object, which contains all database metadata
- Meta meta = db.dsl().meta();
+ try {
+ Connection connection = db.getConnection();
+ Meta meta = DSL.using(connection).meta();
- // Use the getTables() method with a filter
- return meta.getTables(DSL.name("bolts", tableName)).stream()
- .anyMatch(
- table ->
- table.getName().equalsIgnoreCase(tableName)
- && table.getSchema().getName().equalsIgnoreCase("bolts"));
+ // Use the getTables() method with a filter
+ return meta.getTables(DSL.name("bolts", tableName)).stream()
+ .anyMatch(
+ table ->
+ table.getName().equalsIgnoreCase(tableName)
+ && table.getSchema().getName().equalsIgnoreCase("bolts"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
public List getRanBaseMigrations() {
@@ -53,11 +52,10 @@ public class Migration {
boolean migrationsExists = tableExists("_migrations");
if (!migrationsExists) return List.of();
- return db.dsl()
+ return DSL.using(connection)
.select(DSL.field("name"))
.from(DSL.table("_migrations"))
.where(DSL.field("base").eq(true))
- .and(DSL.field("executedAt").isNull())
.fetch(DSL.field("name"), String.class);
} catch (Exception e) {
throw new RuntimeException(e);
@@ -66,7 +64,7 @@ public class Migration {
public List getRanMigrations() {
try (Connection connection = db.getConnection()) {
- return db.dsl()
+ return DSL.using(connection)
.select(DSL.field("name"))
.from(DSL.table("_migrations"))
.fetch(DSL.field("name"), String.class);
diff --git a/src/main/resources/migrations/20250919-create-migration.kts b/src/main/resources/migrations/20250919-create-migration.kts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/main/resources/migrations/20250920-create-migration.sql b/src/main/resources/migrations/20250920-create-migration.sql
new file mode 100644
index 0000000..cde9fe5
--- /dev/null
+++ b/src/main/resources/migrations/20250920-create-migration.sql
@@ -0,0 +1,6 @@
+create table _migrations (
+ name varchar(255) primary key,
+ executedAt timestamp,
+ base int(1),
+ content longtext
+)
\ No newline at end of file
diff --git a/src/test/java/dev/mduchene/MigrationTest.java b/src/test/java/dev/mduchene/MigrationTest.java
new file mode 100644
index 0000000..229e0ef
--- /dev/null
+++ b/src/test/java/dev/mduchene/MigrationTest.java
@@ -0,0 +1,96 @@
+
+package dev.mduchene;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class MigrationTest {
+
+// private Db db;
+// private Migration migration;
+//
+// @BeforeEach
+// public void setUp() {
+// db = Db.Builder.create()
+// .url("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MySQL")
+// .user("sa")
+// .password("")
+// .build();
+// db.init();
+// migration = Migration.of(db);
+// }
+//
+// @AfterEach
+// public void tearDown() throws IOException {
+// // Clean up the created migration files
+// File migrationsDir = new File("src/main/resources/migrations");
+// for (File file : Objects.requireNonNull(migrationsDir.listFiles())) {
+// if (!file.getName().equals(".gitkeep")) {
+// Files.delete(file.toPath());
+// }
+// }
+// db.run("DROP ALL OBJECTS");
+// }
+//
+//
+//
+// @Test
+// public void testCreateMigration() {
+// migration.create("test_migration");
+// File migrationsDir = new File("src/main/resources/migrations");
+// assertEquals(1, Objects.requireNonNull(migrationsDir.listFiles()).length);
+// assertTrue(Objects.requireNonNull(migrationsDir.listFiles())[0].getName().contains("_test_migration.sql"));
+// }
+//
+// @Test
+// public void testGetPendingMigrations() throws IOException {
+// // Create a dummy migration file
+// Path newFile = Paths.get("src/main/resources/migrations/20230101000000_initial.sql");
+// Files.createFile(newFile);
+//
+// List pending = migration.getPending();
+// assertEquals(1, pending.size());
+// assertEquals("20230101000000_initial.sql", pending.get(0));
+//
+// // Run the migration
+// db.run("INSERT INTO _migrations (name) VALUES ('20230101000000_initial.sql')");
+//
+// pending = migration.getPending();
+// assertEquals(0, pending.size());
+// }
+//
+// @Test
+// public void testRunMigrations() throws IOException {
+// // Create a dummy migration file with some SQL
+// String migrationName = "20230101000001_create_users_table.sql";
+// Path newFile = Paths.get("src/main/resources/migrations/" + migrationName);
+// Files.write(newFile, "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255));".getBytes());
+//
+// // Run pending migrations
+// List pending = migration.getPending();
+// migration.run(pending);
+//
+// // Check if the table was created
+// try {
+// db.run("SELECT * FROM users");
+// } catch (Exception e) {
+// fail("Table 'users' should have been created by the migration.");
+// }
+//
+// // Check if the migration was recorded in the _migrations table
+//// List ranMigrations = db.getRanMigrations();
+//// assertEquals(1, ranMigrations.size());
+//// assertEquals(migrationName, ranMigrations.get(0));
+// }
+}