From df935f1f33bd2248332a4e8ad221034aa6a1d4e4 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Fri, 14 Jun 2024 15:23:38 +0200 Subject: [PATCH] Automatically load driver --- README.md | 1 + .../processor/RepositoryProcessor.java | 2 +- .../processor/type/SqlDatabaseGenerator.java | 4 +- .../databaseutils/CredentialsFileHandler.java | 8 ++-- .../databaseutils/DatabaseContext.java | 10 ++--- .../databaseutils/DatabaseLoader.java | 4 +- .../databaseutils/DatabaseRegistry.java | 19 ++++---- .../geysermc/databaseutils/DatabaseType.java | 6 +++ .../databaseutils/DatabaseTypePresent.java | 29 ------------ .../geysermc/databaseutils/DatabaseUtils.java | 18 ++++---- .../DatabaseWithDialectType.java | 45 +++++++++++++++++++ .../databaseutils/sql/SqlDatabase.java | 12 ++++- .../databaseutils/sql/SqlDatabasePresent.java | 35 --------------- .../databaseutils/sql/SqlDialect.java | 34 +++++--------- .../org/geysermc/databaseutils/SqlTest.java | 3 +- 15 files changed, 107 insertions(+), 123 deletions(-) create mode 100644 core/src/main/java/org/geysermc/databaseutils/DatabaseType.java delete mode 100644 core/src/main/java/org/geysermc/databaseutils/DatabaseTypePresent.java create mode 100644 core/src/main/java/org/geysermc/databaseutils/DatabaseWithDialectType.java delete mode 100644 core/src/main/java/org/geysermc/databaseutils/sql/SqlDatabasePresent.java diff --git a/README.md b/README.md index 8de5b7c..7ca5274 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ currently examples can be found in the tests of the AP module and the tests of t - support adding every variable of the entity as parameter - add `save` which either inserts the entity if it's not present or updates the already existing entity - implementing MongoDB support +- adding migrations - and plenty more # Supported types diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/RepositoryProcessor.java b/ap/src/main/java/org/geysermc/databaseutils/processor/RepositoryProcessor.java index 8aff37f..0fe7e42 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/RepositoryProcessor.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/RepositoryProcessor.java @@ -153,7 +153,7 @@ record GeneratedType(String packageName, TypeSpec database) {} @Override public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latestSupported(); + return SourceVersion.latest(); } private void writeGeneratedTypes(List generatedTypes) { diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/type/SqlDatabaseGenerator.java b/ap/src/main/java/org/geysermc/databaseutils/processor/type/SqlDatabaseGenerator.java index eeefb5c..063d750 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/type/SqlDatabaseGenerator.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/type/SqlDatabaseGenerator.java @@ -46,7 +46,7 @@ public Class databaseClass() { @Override protected void addEntities(Collection entities, MethodSpec.Builder method) { method.addException(SQLException.class); - method.addStatement("$T dialect = database.dialect()", SqlDialect.class); + method.addStatement("$T type = database.type()", SqlDialect.class); method.beginControlFlow("try ($T connection = database.dataSource().getConnection())", Connection.class); method.beginControlFlow("try ($T statement = connection.createStatement())", Statement.class); @@ -73,7 +73,7 @@ private CodeBlock.Builder addEntityQuery(EntityInfo entity) { } builder.add( - "\"$L \" + $T.sqlTypeFor($T.class, dialect) ", + "\"$L \" + $T.sqlTypeFor($T.class, type) ", column.name(), SqlTypeMappingRegistry.class, ClassName.bestGuess(column.typeName().toString())); diff --git a/core/src/main/java/org/geysermc/databaseutils/CredentialsFileHandler.java b/core/src/main/java/org/geysermc/databaseutils/CredentialsFileHandler.java index 5f8d2e6..490c41e 100644 --- a/core/src/main/java/org/geysermc/databaseutils/CredentialsFileHandler.java +++ b/core/src/main/java/org/geysermc/databaseutils/CredentialsFileHandler.java @@ -31,10 +31,9 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Properties; -import org.geysermc.databaseutils.sql.SqlDialect; final class CredentialsFileHandler { - public DatabaseConfig handle(SqlDialect dialect, Path credentialsFile) { + public DatabaseConfig handle(DatabaseWithDialectType dialect, Path credentialsFile) { DatabaseConfig config = defaultValuesFor(dialect); if (credentialsFile != null) { if (Files.exists(credentialsFile)) { @@ -75,14 +74,15 @@ private void createConfig(DatabaseConfig defaults, Path toStore) { } } - private DatabaseConfig defaultValuesFor(SqlDialect dialect) { - return switch (dialect) { + private DatabaseConfig defaultValuesFor(DatabaseWithDialectType type) { + return switch (type) { case H2 -> configFor("jdbc:h2:./database", "sa"); case SQL_SERVER -> configFor("jdbc:sqlserver://localhost;encrypt=true;integratedSecurity=true;"); case MYSQL -> configFor("jdbc:mysql://localhost/database"); case ORACLE_DATABASE -> configFor("jdbc:oracle:thin:@//localhost/service"); case POSTGRESQL -> configFor("jdbc:postgresql://localhost/database"); case SQLITE -> configFor("jdbc:sqlite:./database"); + case MONGODB -> configFor("mongodb://localhost:27017"); }; } diff --git a/core/src/main/java/org/geysermc/databaseutils/DatabaseContext.java b/core/src/main/java/org/geysermc/databaseutils/DatabaseContext.java index 68cfd97..7b1d590 100644 --- a/core/src/main/java/org/geysermc/databaseutils/DatabaseContext.java +++ b/core/src/main/java/org/geysermc/databaseutils/DatabaseContext.java @@ -26,7 +26,6 @@ import java.util.concurrent.ExecutorService; import org.geysermc.databaseutils.codec.TypeCodecRegistry; -import org.geysermc.databaseutils.sql.SqlDialect; public record DatabaseContext( String url, @@ -34,19 +33,20 @@ public record DatabaseContext( String password, String poolName, int connectionPoolSize, - SqlDialect dialect, + DatabaseWithDialectType type, ExecutorService service, TypeCodecRegistry registry) { + public DatabaseContext { if (poolName == null || poolName.isEmpty()) throw new IllegalArgumentException("poolName cannot be null or empty"); - if (dialect == null) throw new IllegalArgumentException("dialect cannot be null"); + if (type == null) throw new IllegalArgumentException("A database type has to be provided"); } public DatabaseContext( DatabaseConfig config, String poolName, - SqlDialect dialect, + DatabaseWithDialectType type, ExecutorService service, TypeCodecRegistry registry) { this( @@ -55,7 +55,7 @@ public DatabaseContext( config.password(), poolName, config.connectionPoolSize(), - dialect, + type, service, registry); } diff --git a/core/src/main/java/org/geysermc/databaseutils/DatabaseLoader.java b/core/src/main/java/org/geysermc/databaseutils/DatabaseLoader.java index 452d541..a239040 100644 --- a/core/src/main/java/org/geysermc/databaseutils/DatabaseLoader.java +++ b/core/src/main/java/org/geysermc/databaseutils/DatabaseLoader.java @@ -36,9 +36,9 @@ final class DatabaseLoader { @SuppressWarnings({"unchecked"}) @NonNull StartResult startDatabase(DatabaseContext context) { - var database = DatabaseRegistry.firstPresentDatabase(); + var database = DatabaseRegistry.databaseFor(context.type()); if (database == null) { - throw new IllegalStateException("Couldn't find any present database"); + throw new IllegalStateException("Couldn't find a database manager for " + context.type()); } Class databaseImplClass; diff --git a/core/src/main/java/org/geysermc/databaseutils/DatabaseRegistry.java b/core/src/main/java/org/geysermc/databaseutils/DatabaseRegistry.java index ff883e1..f470d29 100644 --- a/core/src/main/java/org/geysermc/databaseutils/DatabaseRegistry.java +++ b/core/src/main/java/org/geysermc/databaseutils/DatabaseRegistry.java @@ -24,21 +24,20 @@ */ package org.geysermc.databaseutils; -import java.util.List; +import java.util.Map; import java.util.function.Supplier; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.databaseutils.sql.SqlDatabase; -import org.geysermc.databaseutils.sql.SqlDatabasePresent; final class DatabaseRegistry { - private static final List> TYPES = List.of(SqlDatabase::new); - private static final List TYPE_PRESENT = List.of(new SqlDatabasePresent()); + private static final Map> TYPES = Map.of(DatabaseType.SQL, SqlDatabase::new); - public static Database firstPresentDatabase() { - for (int i = 0; i < TYPE_PRESENT.size(); i++) { - if (TYPE_PRESENT.get(i).isPresent()) { - return TYPES.get(i).get(); - } + public static @Nullable Database databaseFor(@NonNull DatabaseWithDialectType type) { + var instanceSupplier = TYPES.get(type.databaseType()); + if (instanceSupplier == null) { + return null; } - return null; + return instanceSupplier.get(); } } diff --git a/core/src/main/java/org/geysermc/databaseutils/DatabaseType.java b/core/src/main/java/org/geysermc/databaseutils/DatabaseType.java new file mode 100644 index 0000000..7ccab7c --- /dev/null +++ b/core/src/main/java/org/geysermc/databaseutils/DatabaseType.java @@ -0,0 +1,6 @@ +package org.geysermc.databaseutils; + +public enum DatabaseType { + SQL, + MONGODB +} diff --git a/core/src/main/java/org/geysermc/databaseutils/DatabaseTypePresent.java b/core/src/main/java/org/geysermc/databaseutils/DatabaseTypePresent.java deleted file mode 100644 index e15b4a4..0000000 --- a/core/src/main/java/org/geysermc/databaseutils/DatabaseTypePresent.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2024 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/DatabaseUtils - */ -package org.geysermc.databaseutils; - -public interface DatabaseTypePresent { - boolean isPresent(); -} diff --git a/core/src/main/java/org/geysermc/databaseutils/DatabaseUtils.java b/core/src/main/java/org/geysermc/databaseutils/DatabaseUtils.java index a4192ce..4f671cd 100644 --- a/core/src/main/java/org/geysermc/databaseutils/DatabaseUtils.java +++ b/core/src/main/java/org/geysermc/databaseutils/DatabaseUtils.java @@ -31,7 +31,6 @@ import java.util.concurrent.Executors; import org.geysermc.databaseutils.codec.TypeCodec; import org.geysermc.databaseutils.codec.TypeCodecRegistry; -import org.geysermc.databaseutils.sql.SqlDialect; public class DatabaseUtils { private final DatabaseContext context; @@ -81,7 +80,8 @@ public static class Builder { private String password; private String poolName = "database-utils"; private int connectionPoolSize = 0; - private SqlDialect dialect; + + private DatabaseWithDialectType type; private Path credentialsFile; private boolean useDefaultCredentials = true; @@ -158,12 +158,12 @@ public Builder connectionPoolSize(int connectionPoolSize) { return this; } - public SqlDialect dialect() { - return dialect; + public DatabaseWithDialectType type() { + return type; } - public Builder dialect(SqlDialect dialect) { - this.dialect = dialect; + public Builder type(DatabaseWithDialectType type) { + this.type = type; return this; } @@ -210,14 +210,14 @@ public DatabaseUtils build() { var actual = config; if (credentialsFile != null) { - actual = new CredentialsFileHandler().handle(dialect, credentialsFile); + actual = new CredentialsFileHandler().handle(type, credentialsFile); } else if (useDefaultCredentials) { - actual = new CredentialsFileHandler().handle(dialect, null); + actual = new CredentialsFileHandler().handle(type, null); } else if (config == null) { actual = new DatabaseConfig(uri, username, password, connectionPoolSize); } - return new DatabaseUtils(new DatabaseContext(actual, poolName, dialect, service, registry)); + return new DatabaseUtils(new DatabaseContext(actual, poolName, type, service, registry)); } } } diff --git a/core/src/main/java/org/geysermc/databaseutils/DatabaseWithDialectType.java b/core/src/main/java/org/geysermc/databaseutils/DatabaseWithDialectType.java new file mode 100644 index 0000000..ab06838 --- /dev/null +++ b/core/src/main/java/org/geysermc/databaseutils/DatabaseWithDialectType.java @@ -0,0 +1,45 @@ +package org.geysermc.databaseutils; + +import java.util.Locale; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.databaseutils.sql.SqlDialect; + +public enum DatabaseWithDialectType { + H2(DatabaseType.SQL, SqlDialect.H2), + SQL_SERVER(DatabaseType.SQL, SqlDialect.SQL_SERVER), + MYSQL(DatabaseType.SQL, SqlDialect.MYSQL), + ORACLE_DATABASE(DatabaseType.SQL, SqlDialect.ORACLE_DATABASE), + POSTGRESQL(DatabaseType.SQL, SqlDialect.POSTGRESQL), + SQLITE(DatabaseType.SQL, SqlDialect.SQLITE), + MONGODB(DatabaseType.MONGODB, null); + + private static final DatabaseWithDialectType[] VALUES = values(); + + private final DatabaseType databaseType; + private final SqlDialect dialect; + + DatabaseWithDialectType(@NonNull DatabaseType databaseType, @Nullable SqlDialect dialect) { + this.databaseType = Objects.requireNonNull(databaseType); + this.dialect = dialect; + } + + public static @Nullable DatabaseWithDialectType byName(@NonNull String name) { + var normalized = name.replace('-', '_').replace(' ', '_').toUpperCase(Locale.ROOT); + for (DatabaseWithDialectType value : VALUES) { + if (value.name().equals(normalized)) { + return value; + } + } + return null; + } + + public @NonNull DatabaseType databaseType() { + return databaseType; + } + + public @Nullable SqlDialect dialect() { + return dialect; + } +} diff --git a/core/src/main/java/org/geysermc/databaseutils/sql/SqlDatabase.java b/core/src/main/java/org/geysermc/databaseutils/sql/SqlDatabase.java index 22abc71..2f545af 100644 --- a/core/src/main/java/org/geysermc/databaseutils/sql/SqlDatabase.java +++ b/core/src/main/java/org/geysermc/databaseutils/sql/SqlDatabase.java @@ -28,6 +28,7 @@ import com.zaxxer.hikari.HikariDataSource; import org.geysermc.databaseutils.Database; import org.geysermc.databaseutils.DatabaseContext; +import org.geysermc.databaseutils.util.ClassUtils; public final class SqlDatabase extends Database { private SqlDialect dialect; @@ -36,7 +37,16 @@ public final class SqlDatabase extends Database { @Override public void start(DatabaseContext context) { super.start(context); - this.dialect = context.dialect(); + this.dialect = context.type().dialect(); + + if (dialect == null) { + throw new IllegalStateException("The selected type '" + context.type() + "' is not a sql type"); + } + + // This also loads the driver + if (!ClassUtils.isClassPresent(dialect.driverName())) { + throw new IllegalStateException("The driver for the selected dialect '" + dialect + "' is not present!"); + } var hikariConfig = new HikariConfig(); hikariConfig.setJdbcUrl(context.url()); diff --git a/core/src/main/java/org/geysermc/databaseutils/sql/SqlDatabasePresent.java b/core/src/main/java/org/geysermc/databaseutils/sql/SqlDatabasePresent.java deleted file mode 100644 index 7a16255..0000000 --- a/core/src/main/java/org/geysermc/databaseutils/sql/SqlDatabasePresent.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2024 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/DatabaseUtils - */ -package org.geysermc.databaseutils.sql; - -import org.geysermc.databaseutils.DatabaseTypePresent; -import org.geysermc.databaseutils.util.ClassUtils; - -public final class SqlDatabasePresent implements DatabaseTypePresent { - @Override - public boolean isPresent() { - return ClassUtils.isClassPresent("com.zaxxer.hikari.HikariConfig"); - } -} diff --git a/core/src/main/java/org/geysermc/databaseutils/sql/SqlDialect.java b/core/src/main/java/org/geysermc/databaseutils/sql/SqlDialect.java index 0b4dcbb..57a9d07 100644 --- a/core/src/main/java/org/geysermc/databaseutils/sql/SqlDialect.java +++ b/core/src/main/java/org/geysermc/databaseutils/sql/SqlDialect.java @@ -24,33 +24,21 @@ */ package org.geysermc.databaseutils.sql; -import java.util.Locale; - public enum SqlDialect { - H2, - SQL_SERVER, - MYSQL, - ORACLE_DATABASE, - POSTGRESQL, - SQLITE; + H2("org.h2.Driver"), + SQL_SERVER("com.microsoft.sqlserver.jdbc.SQLServerDriver"), + MYSQL("org.mariadb.jdbc.Driver"), + ORACLE_DATABASE("oracle.jdbc.driver.OracleDriver"), + POSTGRESQL("org.postgresql.Driver"), + SQLITE("org.sqlite.JDBC"),; - private static final SqlDialect[] VALUES = values(); + private final String driverName; - public static SqlDialect byName(String dialectName) { - var normalized = dialectName.replace('-', '_').replace(' ', '_').toUpperCase(Locale.ROOT); - for (SqlDialect value : VALUES) { - if (value.name().equals(normalized)) { - return value; - } - } - return null; + SqlDialect(String driverName) { + this.driverName = driverName; } - public static SqlDialect requireByName(String dialectName) { - var result = byName(dialectName); - if (result == null) { - throw new IllegalStateException("Could not find dialect for " + dialectName); - } - return result; + public String driverName() { + return driverName; } } diff --git a/core/src/test/java/org/geysermc/databaseutils/SqlTest.java b/core/src/test/java/org/geysermc/databaseutils/SqlTest.java index 479adc5..e9c2fed 100644 --- a/core/src/test/java/org/geysermc/databaseutils/SqlTest.java +++ b/core/src/test/java/org/geysermc/databaseutils/SqlTest.java @@ -29,7 +29,6 @@ import java.util.concurrent.Executors; import org.geysermc.databaseutils.data.BasicRepository; import org.geysermc.databaseutils.data.TestEntity; -import org.geysermc.databaseutils.sql.SqlDialect; import org.junit.jupiter.api.Test; final class SqlTest { @@ -37,7 +36,7 @@ final class SqlTest { void hello() throws ExecutionException, InterruptedException { var utils = DatabaseUtils.builder() .useDefaultCredentials(true) - .dialect(SqlDialect.H2) + .type(DatabaseWithDialectType.H2) .executorService(Executors.newCachedThreadPool()) .build(); utils.start();