From f691fe740f0d5406700f9d2be03ac97b28104eab Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 4 Jan 2025 15:34:58 +0100 Subject: [PATCH 1/5] Add generic DB Dialect --- .../jdbc/dialect/GenericDialect.java | 29 +++++++++++++++++++ .../jdbc/dialect/GenericDialectITest.java | 5 ++++ 2 files changed, 34 insertions(+) create mode 100644 src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java create mode 100644 src/test/java/org/itsallcode/jdbc/dialect/GenericDialectITest.java diff --git a/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java b/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java new file mode 100644 index 0000000..d647ad2 --- /dev/null +++ b/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java @@ -0,0 +1,29 @@ +package org.itsallcode.jdbc.dialect; + +import org.itsallcode.jdbc.resultset.generic.ColumnMetaData; + +/** + * A generic {@link DbDialect} without any special handling. + */ +public class GenericDialect implements DbDialect { + /** + * Create a new instance. + */ + public GenericDialect() { + } + + @Override + public boolean supportsUrl(final String jdbcUrl) { + return true; + } + + @Override + public ColumnValueExtractor createExtractor(final ColumnMetaData column) { + return Extractors.generic(); + } + + @Override + public ColumnValueSetter createSetter(final Class type) { + return Setters.generic(); + } +} diff --git a/src/test/java/org/itsallcode/jdbc/dialect/GenericDialectITest.java b/src/test/java/org/itsallcode/jdbc/dialect/GenericDialectITest.java new file mode 100644 index 0000000..0b4751b --- /dev/null +++ b/src/test/java/org/itsallcode/jdbc/dialect/GenericDialectITest.java @@ -0,0 +1,5 @@ +package org.itsallcode.jdbc.dialect; + +public class GenericDialectITest { + +} From db6e1150fac90b6cca0f5a28375cf18a8dfb7f9c Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 4 Jan 2025 15:36:21 +0100 Subject: [PATCH 2/5] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4274b3..c41e538 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [PR #41](https://github.com/itsallcode/simple-jdbc/pull/41): Allow direct access to `PreparedStatement` for batch insert - [PR #42](https://github.com/itsallcode/simple-jdbc/pull/42): Allow direct access to `Connection` - [PR #43](https://github.com/itsallcode/simple-jdbc/pull/43): Allow direct access to `Connection` from `DbOperations` +- [PR #44](https://github.com/itsallcode/simple-jdbc/pull/44): Add `GenericDialect` for unsupported databases ## [0.9.0] - 2024-12-23 From 7ab09f287e481a1ac58f911d47960ffbe01fd532 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 4 Jan 2025 16:40:30 +0100 Subject: [PATCH 3/5] Use generic dialect as fallback --- .../org/itsallcode/jdbc/DbDialectFactory.java | 10 ++++- .../org/itsallcode/jdbc/DbOperations.java | 2 +- .../jdbc/dialect/GenericDialect.java | 10 ++--- .../itsallcode/jdbc/DbDialectFactoryTest.java | 26 +++++++++++ .../jdbc/dialect/GenericDialectITest.java | 43 ++++++++++++++++++- 5 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 src/test/java/org/itsallcode/jdbc/DbDialectFactoryTest.java diff --git a/src/main/java/org/itsallcode/jdbc/DbDialectFactory.java b/src/main/java/org/itsallcode/jdbc/DbDialectFactory.java index 892d0c0..c43daa5 100644 --- a/src/main/java/org/itsallcode/jdbc/DbDialectFactory.java +++ b/src/main/java/org/itsallcode/jdbc/DbDialectFactory.java @@ -2,10 +2,15 @@ import java.util.ServiceLoader; import java.util.ServiceLoader.Provider; +import java.util.logging.Logger; import org.itsallcode.jdbc.dialect.DbDialect; +import org.itsallcode.jdbc.dialect.GenericDialect; class DbDialectFactory { + + private static final Logger LOG = Logger.getLogger(DbDialectFactory.class.getName()); + public DbDialect createDialect(final String url) { final ServiceLoader serviceLoader = ServiceLoader.load(DbDialect.class, Thread.currentThread().getContextClassLoader()); @@ -13,6 +18,9 @@ public DbDialect createDialect(final String url) { .map(Provider::get) .filter(dialect -> dialect.supportsUrl(url)) .findAny() - .orElseThrow(() -> new IllegalStateException("No DB dialect registered for JDBC URL '" + url + "'")); + .orElseGet(() -> { + LOG.warning(() -> "No dialect found for URL '%s', using generic dialect.".formatted(url)); + return GenericDialect.INSTANCE; + }); } } diff --git a/src/main/java/org/itsallcode/jdbc/DbOperations.java b/src/main/java/org/itsallcode/jdbc/DbOperations.java index 32b3676..d7b305a 100644 --- a/src/main/java/org/itsallcode/jdbc/DbOperations.java +++ b/src/main/java/org/itsallcode/jdbc/DbOperations.java @@ -143,7 +143,7 @@ SimpleResultSet query(final String sql, final PreparedStatementSetter pre * * @return original wrapped connection */ - public Connection getOriginalConnection(); + Connection getOriginalConnection(); @Override void close(); diff --git a/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java b/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java index d647ad2..64f6807 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java @@ -5,11 +5,11 @@ /** * A generic {@link DbDialect} without any special handling. */ -public class GenericDialect implements DbDialect { - /** - * Create a new instance. - */ - public GenericDialect() { +public final class GenericDialect implements DbDialect { + public static final DbDialect INSTANCE = new GenericDialect(); + + private GenericDialect() { + // Nothing to do } @Override diff --git a/src/test/java/org/itsallcode/jdbc/DbDialectFactoryTest.java b/src/test/java/org/itsallcode/jdbc/DbDialectFactoryTest.java new file mode 100644 index 0000000..042b014 --- /dev/null +++ b/src/test/java/org/itsallcode/jdbc/DbDialectFactoryTest.java @@ -0,0 +1,26 @@ +package org.itsallcode.jdbc; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.itsallcode.jdbc.dialect.*; +import org.junit.jupiter.api.Test; + +class DbDialectFactoryTest { + @Test + void exasolDialect() { + assertThat(new DbDialectFactory().createDialect("jdbc:exa:localhost:8563;schema=SCHEMA")) + .isInstanceOf(ExasolDialect.class); + } + + @Test + void h2Dialect() { + assertThat(new DbDialectFactory().createDialect("jdbc:h2:mem:")) + .isInstanceOf(H2Dialect.class); + } + + @Test + void genericDialect() { + assertThat(new DbDialectFactory().createDialect("jdbc:unknown:localhost:8563;schema=SCHEMA")) + .isInstanceOf(GenericDialect.class); + } +} diff --git a/src/test/java/org/itsallcode/jdbc/dialect/GenericDialectITest.java b/src/test/java/org/itsallcode/jdbc/dialect/GenericDialectITest.java index 0b4751b..ba5945d 100644 --- a/src/test/java/org/itsallcode/jdbc/dialect/GenericDialectITest.java +++ b/src/test/java/org/itsallcode/jdbc/dialect/GenericDialectITest.java @@ -1,5 +1,46 @@ package org.itsallcode.jdbc.dialect; -public class GenericDialectITest { +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import java.util.List; +import java.util.stream.Stream; + +import org.h2.jdbcx.JdbcDataSource; +import org.itsallcode.jdbc.*; +import org.itsallcode.jdbc.resultset.ContextRowMapper; +import org.itsallcode.jdbc.resultset.generic.Row; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class GenericDialectITest { + + static Stream types() { + return Stream.of( + Arguments.of(1), + Arguments.of("a"), + Arguments.of(1.0), + Arguments.of(true), + Arguments.of((Object) null)); + } + + @ParameterizedTest + @MethodSource("types") + void setGet(final Object value) { + try (SimpleConnection connection = genericDialectConnection()) { + final List result = connection + .query("select ?", asList(value), ContextRowMapper.generic(GenericDialect.INSTANCE)).toList(); + assertThat(result) + .hasSize(1) + .first().extracting(row -> row.get(0).getValue()) + .isEqualTo(value); + } + } + + private SimpleConnection genericDialectConnection() { + final JdbcDataSource dataSource = new JdbcDataSource(); + dataSource.setURL(H2TestFixture.H2_MEM_JDBC_URL); + return DataSourceConnectionFactory.create(GenericDialect.INSTANCE, dataSource).getConnection(); + } } From e166292508a3f31169d79c2c6925540c435b055e Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 4 Jan 2025 16:41:24 +0100 Subject: [PATCH 4/5] Fix javadoc --- src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java b/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java index 64f6807..89716cd 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/GenericDialect.java @@ -6,6 +6,7 @@ * A generic {@link DbDialect} without any special handling. */ public final class GenericDialect implements DbDialect { + /** Singleton instance of the generic DB dialect. */ public static final DbDialect INSTANCE = new GenericDialect(); private GenericDialect() { From 0ea7f6e6faa04464ae4a5ddd1caa72d52904c738 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 4 Jan 2025 16:44:19 +0100 Subject: [PATCH 5/5] Add classname to log format --- src/test/resources/logging.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/resources/logging.properties b/src/test/resources/logging.properties index 1f2fdd1..069cb61 100644 --- a/src/test/resources/logging.properties +++ b/src/test/resources/logging.properties @@ -2,5 +2,5 @@ handlers=java.util.logging.ConsoleHandler .level=INFO java.util.logging.ConsoleHandler.level=ALL java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter -java.util.logging.SimpleFormatter.format=%1$tF %1$tT.%1$tL [%4$-7s] %5$s %n +java.util.logging.SimpleFormatter.format=%1$tF %1$tT.%1$tL [%4$-7s] %2$s %5$s %n org.itsallcode.level=ALL