work on db connection basic

This commit is contained in:
Maxime Duchene-Savard 2025-09-19 08:17:22 -04:00
parent 1142def290
commit 2fa76d84df
11 changed files with 327 additions and 45 deletions

6
.idea/copilot.data.migration.agent.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

6
.idea/copilot.data.migration.ask.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AskMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Ask2AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

6
.idea/copilot.data.migration.edit.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EditMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

16
.idea/kotlinc.xml generated Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.1" />
<option name="languageVersion" value="2.1" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.2.0" />
</component>
</project>

95
pom.xml
View File

@ -12,6 +12,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.version>2.2.0</kotlin.version>
</properties> </properties>
<build> <build>
@ -24,6 +25,70 @@
<target>21</target> <target>21</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<source>src/main/java</source>
<source>target/generated-sources/annotations</source>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<source>src/test/java</source>
<source>target/generated-test-sources/test-annotations</source>
</sourceDirs>
</configuration>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<execution>
<id>default-testCompile</id>
<phase>none</phase>
</execution>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
@ -69,6 +134,17 @@
<version>2.0.16</version> <version>2.0.16</version>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-scripting-jvm</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-scripting-common</artifactId>
<version>${kotlin.version}</version>
</dependency>
<!-- TEST --> <!-- TEST -->
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --> <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
@ -78,5 +154,24 @@
<version>5.13.4</version> <version>5.13.4</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.3.230</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -22,6 +22,7 @@ public class App {
.build(); .build();
db.init(); db.init();
Migration.of(db).getPendingBaseMigrations().forEach(f -> System.out.println(f.getName()));
var app = Javalin.create(cnf -> {}); var app = Javalin.create(cnf -> {});
app.before( app.before(

View File

@ -2,63 +2,95 @@ package dev.mduchene;
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import org.jooq.DSLContext;
import org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import org.jooq.SQLDialect;
import org.jooq.conf.RenderQuotedNames;
import org.jooq.conf.Settings;
import org.jooq.SQLDialect;
public class Db { public class Db {
private HikariDataSource dataSource; private HikariDataSource dataSource;
private Db() {} private org.jooq.DSLContext dsl;
public void init() { private Db() {}
public void init() {
try (Connection connection = dataSource.getConnection()) {
connection.setAutoCommit(true);
} catch (Exception e) {
// Ignore if the table already exists
}
}
public static class Builder {
private String url;
private String user;
private String password;
private Builder() {}
public static Builder create() {
return new Builder();
} }
public static class Builder { public Builder url(String url) {
private String url; this.url = url;
private String user; return this;
private String password;
private Builder() {}
public static Builder create() {
return new Builder();
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder user(String user) {
this.user = user;
return this;
}
public Builder password(String password) {
this.password = password;
return this;
}
public Db build() {
Db db = new Db();
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(user);
config.setPassword(password);
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.setMaximumPoolSize(64);
db.dataSource = new HikariDataSource(config);
return db;
}
} }
public Connection getConnection() throws Exception { public Builder user(String user) {
Connection connection = dataSource.getConnection(); this.user = user;
connection.setAutoCommit(false); return this;
return connection;
} }
public Builder password(String password) {
this.password = password;
return this;
}
public Db build() {
Db db = new Db();
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(user);
config.setPassword(password);
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.setMaximumPoolSize(64);
db.dataSource = new HikariDataSource(config);
db.dsl =
DSL.using(
db.dataSource,
SQLDialect.MARIADB,
new Settings().withRenderQuotedNames(RenderQuotedNames.NEVER));
return db;
}
}
public Connection getConnection() throws Exception {
Connection connection = dataSource.getConnection();
connection.setAutoCommit(false);
return connection;
}
public void run(String query) {
try (Connection connection = getConnection()) {
dsl.execute(query);
connection.commit();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public DSLContext dsl() {
return dsl;
}
} }

View File

@ -0,0 +1,77 @@
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.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class Migration {
private final Db db;
private Migration(Db db) {
this.db = db;
}
public static Migration of(Db db) {
return new Migration(db);
}
public List<File> getPendingBaseMigrations() {
List<String> ranMigrations = getRanBaseMigrations();
List<File> migrations = ResourceFileLister.listFilesInResourceFolder("migrations");
return migrations.stream()
.filter(file -> !ranMigrations.contains(file.getName()))
.collect(Collectors.toList());
}
public boolean tableExists(String tableName) {
// Get the Meta object, which contains all database metadata
Meta meta = db.dsl().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"));
}
public List<String> getRanBaseMigrations() {
try (Connection connection = db.getConnection()) {
boolean migrationsExists = tableExists("_migrations");
if (!migrationsExists) return List.of();
return db.dsl()
.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);
}
}
public List<String> getRanMigrations() {
try (Connection connection = db.getConnection()) {
return db.dsl()
.select(DSL.field("name"))
.from(DSL.table("_migrations"))
.fetch(DSL.field("name"), String.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,37 @@
package dev.mduchene;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ResourceFileLister {
public static void main(String[] args) {
listFilesInResourceFolder("data");
}
public static List<File> listFilesInResourceFolder(String folderName) {
ClassLoader classLoader = ResourceFileLister.class.getClassLoader();
URL folderUrl = classLoader.getResource(folderName);
if (folderUrl == null) {
throw new IllegalArgumentException("Folder not found: " + folderName);
}
try {
Path folderPath = Paths.get(folderUrl.toURI());
try (Stream<Path> stream = Files.list(folderPath)) {
return stream.filter(Files::isRegularFile).map(Path::toFile).toList();
}
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
}
}
}