From 625c8999a341fdde918276ee1e998fbe3d1ef6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Thu, 27 Oct 2022 23:38:13 +0200 Subject: [PATCH] move java.util.Date to java.time --- .../java/org/xbib/jdbc/query/Database.java | 6 - .../org/xbib/jdbc/query/DatabaseImpl.java | 11 +- .../org/xbib/jdbc/query/DatabaseProvider.java | 2 +- .../java/org/xbib/jdbc/query/DdlImpl.java | 2 - .../main/java/org/xbib/jdbc/query/Flavor.java | 16 +- .../java/org/xbib/jdbc/query/Options.java | 26 +- .../org/xbib/jdbc/query/OptionsDefault.java | 19 +- .../org/xbib/jdbc/query/OptionsOverride.java | 17 +- .../main/java/org/xbib/jdbc/query/Row.java | 44 +-- .../java/org/xbib/jdbc/query/RowsAdaptor.java | 49 ++-- .../java/org/xbib/jdbc/query/RowsHandler.java | 1 + .../main/java/org/xbib/jdbc/query/Schema.java | 20 +- .../main/java/org/xbib/jdbc/query/Sql.java | 18 +- .../java/org/xbib/jdbc/query/SqlArgs.java | 184 +++++-------- .../java/org/xbib/jdbc/query/SqlInsert.java | 18 +- .../org/xbib/jdbc/query/SqlInsertImpl.java | 71 ++--- .../java/org/xbib/jdbc/query/SqlSelect.java | 122 +-------- .../org/xbib/jdbc/query/SqlSelectImpl.java | 103 +++---- .../java/org/xbib/jdbc/query/SqlUpdate.java | 12 +- .../org/xbib/jdbc/query/SqlUpdateImpl.java | 63 +---- .../org/xbib/jdbc/query/StatementAdaptor.java | 107 ++------ .../org/xbib/jdbc/query/flavor/BigQuery.java | 21 +- .../org/xbib/jdbc/query/flavor/Derby.java | 19 +- .../java/org/xbib/jdbc/query/flavor/H2.java | 175 ++++++++++++ .../java/org/xbib/jdbc/query/flavor/Hsql.java | 21 +- .../org/xbib/jdbc/query/flavor/MySQL.java | 177 ++++++++++++ .../org/xbib/jdbc/query/flavor/Oracle.java | 19 +- .../xbib/jdbc/query/flavor/Postgresql.java | 19 +- .../org/xbib/jdbc/query/flavor/SqlServer.java | 19 +- .../org/xbib/jdbc/query/util/DebugSql.java | 13 +- .../services/org.xbib.jdbc.query.Flavor | 2 + .../org/xbib/jdbc/query/test/CommonTest.java | 252 +++++++++--------- .../org/xbib/jdbc/query/test/HsqldbTest.java | 18 +- .../org/xbib/jdbc/query/test/RowStub.java | 30 ++- .../query/test/example/InsertReturning.java | 4 +- .../jdbc/query/test/example/SampleDao.java | 18 +- 36 files changed, 796 insertions(+), 922 deletions(-) create mode 100644 jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/H2.java create mode 100644 jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/MySQL.java diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Database.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Database.java index c683d91..7dbbbbe 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Database.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Database.java @@ -2,7 +2,6 @@ package org.xbib.jdbc.query; import java.sql.Connection; import java.sql.SQLException; -import java.time.LocalDateTime; import java.util.Map; import java.util.function.Consumer; import java.util.function.Supplier; @@ -90,11 +89,6 @@ public interface Database extends Supplier { */ Long nextSequenceValue( String sequenceName); - /** - * Get the value that would be used if you specify an argNowPerApp() parameter. - */ - LocalDateTime nowPerApp(); - /** * Cause the underlying connection to commit its transaction immediately. This * must be explicitly enabled (see {@link Options}, diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseImpl.java index 77d786e..d532271 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseImpl.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseImpl.java @@ -10,7 +10,6 @@ import java.sql.Statement; import java.sql.Timestamp; import java.time.Duration; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; @@ -95,11 +94,6 @@ public class DatabaseImpl implements Database { return toSelect(flavor().sequenceSelectNextVal(sequenceName)).queryLongOrNull(); } - @Override - public LocalDateTime nowPerApp() { - return options.currentDate(); - } - @Override public void commitNow() { if (options.ignoreTransactionControl()) { @@ -250,8 +244,9 @@ public class DatabaseImpl implements Database { @Override public void assertTimeSynchronized(long millisToWarn, long millisToError) { toSelect("select ?" + flavor().fromAny()) - .argDateNowPerDb().queryFirstOrNull(r -> { - LocalDateTime appDate = nowPerApp(); + .argLocalDateTimeNowPerDb() + .queryFirstOrNull(r -> { + LocalDateTime appDate = LocalDateTime.now(); LocalDateTime dbDate = r.getLocalDateTimeOrNull(); if (dbDate == null) { throw new DatabaseException("Expecting a date in the result"); diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseProvider.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseProvider.java index 41e6510..906e7d9 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseProvider.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseProvider.java @@ -1106,7 +1106,7 @@ public final class DatabaseProvider implements Supplier { public Builder withDatePerAppOnly() { return new BuilderImpl(ds, connectionProvider, new OptionsOverride() { @Override - public boolean useDatePerAppOnly() { + public boolean useLocalDateTimeOnly() { return true; } }.withParent(this.options)); diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/DdlImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/DdlImpl.java index abd6640..3139452 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/DdlImpl.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/DdlImpl.java @@ -30,13 +30,11 @@ public class DdlImpl implements Ddl { private void updateInternal(boolean quiet) { CallableStatement ps = null; Metric metric = new Metric(log.isLoggable(Level.FINE)); - boolean isSuccess = false; String errorCode = null; Exception logEx = null; try { ps = connection.prepareCall(sql); - metric.checkpoint("prep"); ps.execute(); metric.checkpoint("exec"); diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Flavor.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Flavor.java index 9b13fc2..7c9c67b 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Flavor.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Flavor.java @@ -1,8 +1,5 @@ package org.xbib.jdbc.query; -import java.sql.Date; -import java.sql.Timestamp; -import java.util.Calendar; import java.util.ServiceLoader; public interface Flavor { @@ -39,7 +36,7 @@ public interface Flavor { String typeBlob(); - String typeDate(); + String typeLocalDateTime(); String typeLocalDate(); @@ -72,17 +69,6 @@ public interface Flavor { */ String fromAny(); - /** - * Return a SQL function representing the specified date. For example, in PostgreSQL this - * looks like "'1970-01-02 02:17:36.789000 GMT'::timestamp". - */ - String dateAsSqlFunction(Timestamp timestamp, Calendar calendar); - - /** - * Return a SQL function representing the specified date without time. - */ - String localDateAsSqlFunction(Date date); - String sequenceOptions(); /** diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Options.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Options.java index c6f08ff..6560394 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Options.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Options.java @@ -1,8 +1,5 @@ package org.xbib.jdbc.query; -import java.time.LocalDateTime; -import java.util.Calendar; - /** * Control various optional behavior for the database interactions. */ @@ -95,27 +92,10 @@ public interface Options { Flavor flavor(); /** - * The value returned by this method will be used for argDateNowPerApp() calls. It - * may also be used for argDateNowPerDb() calls if you have enabled that. + * This is useful for testing purposes as you can use OptionsOverride to provide your + * own clock that will be used. */ - LocalDateTime currentDate(); - - /** - * Wherever argDateNowPerDb() is specified, use argDateNowPerApp() instead. This is - * useful for testing purposes as you can use OptionsOverride to provide your - * own system clock that will be used for time travel. - */ - boolean useDatePerAppOnly(); - - /** - * This calendar will be used for conversions when storing and retrieving timestamps - * from the database. By default this is the JVM default with TimeZone explicitly set - * to GMT (so timestamps will be stored in the database as GMT). - * - *

It is strongly recommended to always run your database in GMT timezone, and - * leave this set to the default.

- */ - Calendar calendarForTimestamps(); + boolean useLocalDateTimeOnly(); /** * The maximum number of characters to print in debug SQL for a given String type diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsDefault.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsDefault.java index 1015de0..905a153 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsDefault.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsDefault.java @@ -1,10 +1,6 @@ package org.xbib.jdbc.query; -import java.text.SimpleDateFormat; import java.time.LocalDateTime; -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; /** * Control various optional behavior for the database interactions. @@ -44,8 +40,7 @@ public class OptionsDefault implements Options { @Override public String generateErrorCode() { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd:H:m:s"); - return sdf.format(new Date()) + "-" + Math.round(Math.random() * 1000000); + return LocalDateTime.now() + "-" + Math.round(Math.random() * 1000000); } @Override @@ -64,20 +59,10 @@ public class OptionsDefault implements Options { } @Override - public LocalDateTime currentDate() { - return LocalDateTime.now(); - } - - @Override - public boolean useDatePerAppOnly() { + public boolean useLocalDateTimeOnly() { return false; } - @Override - public Calendar calendarForTimestamps() { - return Calendar.getInstance(TimeZone.getDefault()); - } - @Override public int maxStringLengthParam() { return 4000; diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsOverride.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsOverride.java index be2c2f9..03f623c 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsOverride.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsOverride.java @@ -2,9 +2,6 @@ package org.xbib.jdbc.query; import org.xbib.jdbc.query.flavor.Postgresql; -import java.time.LocalDateTime; -import java.util.Calendar; -import java.util.Date; /** * Base class for selectively overriding another Options object. @@ -90,18 +87,8 @@ public class OptionsOverride implements Options { } @Override - public LocalDateTime currentDate() { - return parent.currentDate(); - } - - @Override - public boolean useDatePerAppOnly() { - return parent.useDatePerAppOnly(); - } - - @Override - public Calendar calendarForTimestamps() { - return parent.calendarForTimestamps(); + public boolean useLocalDateTimeOnly() { + return parent.useLocalDateTimeOnly(); } @Override diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Row.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Row.java index 3f3f322..002e84a 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Row.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Row.java @@ -6,6 +6,7 @@ import java.math.BigDecimal; import java.sql.ResultSetMetaData; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; /** * Interface for reading results from a database query. @@ -228,173 +229,142 @@ public interface Row { * representation. Some databases will pad the number out to "full precision". This * method tries to reduce scale if there is zero padding to the right of the decimal. */ - BigDecimal getBigDecimalOrNull(); - BigDecimal getBigDecimalOrNull(int columnOneBased); - BigDecimal getBigDecimalOrNull(String columnName); - BigDecimal getBigDecimalOrZero(); - BigDecimal getBigDecimalOrZero(int columnOneBased); - BigDecimal getBigDecimalOrZero(String columnName); /** * @return the value, or null if it is SQL null; never returns the empty string */ - String getStringOrNull(); /** * @return the value, or null if it is SQL null; never returns the empty string */ - String getStringOrNull(int columnOneBased); /** * @return the value, or null if it is SQL null; never returns the empty string */ - String getStringOrNull(String columnName); /** * @return the value, or the empty string if it is SQL null; never returns null */ - String getStringOrEmpty(); /** * @return the value, or the empty string if it is SQL null; never returns null */ - String getStringOrEmpty(int columnOneBased); /** * @return the value, or the empty string if it is SQL null; never returns null */ - String getStringOrEmpty(String columnName); /** * @return the value, or null if it is SQL null; never returns the empty string */ - String getClobStringOrNull(); /** * @return the value, or null if it is SQL null; never returns the empty string */ - String getClobStringOrNull(int columnOneBased); /** * @return the value, or null if it is SQL null; never returns the empty string */ - String getClobStringOrNull(String columnName); /** * @return the value, or the empty string if it is SQL null; never returns null */ - String getClobStringOrEmpty(); /** * @return the value, or the empty string if it is SQL null; never returns null */ - String getClobStringOrEmpty(int columnOneBased); /** * @return the value, or the empty string if it is SQL null; never returns null */ - String getClobStringOrEmpty(String columnName); /** * @return the value, or null if it is SQL null */ - Reader getClobReaderOrNull(); /** * @return the value, or null if it is SQL null */ - Reader getClobReaderOrNull(int columnOneBased); /** * @return the value, or null if it is SQL null */ - Reader getClobReaderOrNull(String columnName); /** * @return the value, or a StringReader containing the empty string if it is SQL null */ - Reader getClobReaderOrEmpty(); /** * @return the value, or a StringReader containing the empty string if it is SQL null */ - Reader getClobReaderOrEmpty(int columnOneBased); /** * @return the value, or a StringReader containing the empty string if it is SQL null */ - Reader getClobReaderOrEmpty(String columnName); - byte[] getBlobBytesOrNull(); - byte[] getBlobBytesOrNull(int columnOneBased); - byte[] getBlobBytesOrNull(String columnName); - byte[] getBlobBytesOrZeroLen(); - byte[] getBlobBytesOrZeroLen(int columnOneBased); - byte[] getBlobBytesOrZeroLen(String columnName); - InputStream getBlobInputStreamOrNull(); - InputStream getBlobInputStreamOrNull(int columnOneBased); - InputStream getBlobInputStreamOrNull(String columnName); - InputStream getBlobInputStreamOrEmpty(); - InputStream getBlobInputStreamOrEmpty(int columnOneBased); InputStream getBlobInputStreamOrEmpty(String columnName); LocalDateTime getLocalDateTimeOrNull(); - + LocalDateTime getLocalDateTimeOrNull(int columnOneBased); - + + LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId); + LocalDateTime getLocalDateTimeOrNull(String columnName); + LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId); + /** * Retrieve column as LocalDate, .i.e, date with no time. * diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsAdaptor.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsAdaptor.java index 0782c86..b3088e7 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsAdaptor.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsAdaptor.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; /** * Safely wrap a ResultSet and provide access to the data it contains. @@ -38,7 +39,6 @@ class RowsAdaptor implements Rows { } } - @Override public String[] getColumnLabels() { try { @@ -661,22 +661,49 @@ class RowsAdaptor implements Rows { public LocalDateTime getLocalDateTimeOrNull() { return getLocalDateTimeOrNull(column++); } - + @Override public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) { try { column = columnOneBased + 1; - return toLocalDateTime(rs, columnOneBased); + return rs.getObject(columnOneBased, LocalDateTime.class); } catch (SQLException e) { throw new DatabaseException(e); } } - + + @Override + public LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId) { + try { + column = columnOneBased + 1; + LocalDateTime localDateTime = rs.getObject(columnOneBased, LocalDateTime.class); + Timestamp timestamp = rs.getTimestamp(columnOneBased); + if (zoneId != null) { + return localDateTime != null ? localDateTime.atZone(zoneId).toLocalDateTime() : null; + } else { + return localDateTime; + } + } catch (SQLException e) { + throw new DatabaseException(e); + } + } + @Override public LocalDateTime getLocalDateTimeOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; - return toLocalDateTime(rs, columnName); + return rs.getObject(columnName, LocalDateTime.class); + } catch (SQLException e) { + throw new DatabaseException(e); + } + } + + @Override + public LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId) { + try { + column = rs.findColumn(columnName) + 1; + LocalDateTime localDateTime = rs.getObject(columnName, LocalDateTime.class); + return localDateTime != null ? localDateTime.atZone(zoneId).toLocalDateTime() : null; } catch (SQLException e) { throw new DatabaseException(e); } @@ -686,7 +713,6 @@ class RowsAdaptor implements Rows { public LocalDate getLocalDateOrNull() { return getLocalDateOrNull(column++); } - @Override public LocalDate getLocalDateOrNull(int columnOneBased) { @@ -697,7 +723,6 @@ class RowsAdaptor implements Rows { throw new DatabaseException(e); } } - @Override public LocalDate getLocalDateOrNull(String columnName) { @@ -719,16 +744,6 @@ class RowsAdaptor implements Rows { } } - private LocalDateTime toLocalDateTime(ResultSet rs, int col) throws SQLException { - Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps()); - return val == null ? null : val.toLocalDateTime(); - } - - private LocalDateTime toLocalDateTime(ResultSet rs, String col) throws SQLException { - Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps()); - return val == null ? null : val.toLocalDateTime(); - } - private LocalDate toLocalDate(ResultSet rs, int col) throws SQLException { java.sql.Date val = rs.getDate(col); return val == null ? null : val.toLocalDate(); diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsHandler.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsHandler.java index 968229b..4df1b94 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsHandler.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsHandler.java @@ -5,5 +5,6 @@ package org.xbib.jdbc.query; */ @FunctionalInterface public interface RowsHandler { + T process(Rows rs) throws Exception; } diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Schema.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Schema.java index 4ca0c54..da39399 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Schema.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Schema.java @@ -76,17 +76,14 @@ public class Schema { Table table = addTable(tableName); try { ResultSetMetaData metadata = r.getMetadata(); - int columnCount = metadata.getColumnCount(); String[] names = new String[columnCount]; for (int i = 0; i < columnCount; i++) { names[i] = metadata.getColumnName(i + 1); } names = SqlArgs.tidyColumnNames(names); - for (int i = 0; i < columnCount; i++) { int type = metadata.getColumnType(i + 1); - switch (type) { case Types.SMALLINT: case Types.INTEGER: @@ -142,13 +139,14 @@ public class Schema { // This is the type dates and times with time and time zone associated. // Note that Oracle dates are always really Timestamps. case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: // Check if we really have a LocalDate implemented by the DB as a timestamp if (metadata.getScale(i + 1) == 0) { // If the scale is 0, this is a LocalDate (no time/timezone). // Anything with a time/timezone will have a non-zero scale table.addColumn(names[i]).asLocalDate(); } else { - table.addColumn(names[i]).asDate(); + table.addColumn(names[i]).asLocalDateTime(); } break; @@ -229,8 +227,8 @@ public class Schema { case StringFixed: sql.append(flavor.typeStringFixed(column.scale)); break; - case Date: - sql.append(flavor.typeDate()); // Append a date with time + case LocalDateTime: + sql.append(flavor.typeLocalDateTime()); // Append a date with time break; case LocalDate: sql.append(flavor.typeLocalDate()); // Append a true date - no time @@ -400,7 +398,7 @@ public class Schema { } public enum ColumnType { - Integer, Long, Float, Double, BigDecimal, StringVar, StringFixed, Clob, Blob, Date, LocalDate, Boolean + Integer, Long, Float, Double, BigDecimal, StringVar, StringFixed, Clob, Blob, LocalDateTime, LocalDate, Boolean } public class Sequence { @@ -525,13 +523,13 @@ public class Schema { public Schema schema() { if (createTracking) { - addColumn("create_time").asDate().table(); + addColumn("create_time").asLocalDateTime().table(); } if (createTrackingFkName != null) { addColumn("create_user").foreignKey(createTrackingFkName).references(createTrackingFkTable).table(); } if (updateTracking || updateSequence) { - addColumn("update_time").asDate().table(); + addColumn("update_time").asLocalDateTime().table(); } if (updateTrackingFkName != null) { addColumn("update_user").foreignKey(updateTrackingFkName).references(updateTrackingFkTable).table(); @@ -868,8 +866,8 @@ public class Schema { } // This type is for dates that have time associated - public Column asDate() { - return asType(ColumnType.Date); + public Column asLocalDateTime() { + return asType(ColumnType.LocalDateTime); } // This type is for true dates with no time associated diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Sql.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Sql.java index 1727770..1ec0465 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Sql.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Sql.java @@ -295,32 +295,22 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply { } public Sql argDate(LocalDateTime arg) { - sqlArgs.argDate(arg); + sqlArgs.argLocalDateTime(arg); return this; } public Sql argDate(String argName, LocalDateTime arg) { - sqlArgs.argDate(argName, arg); - return this; - } - - public Sql argDateNowPerApp() { - sqlArgs.argDateNowPerApp(); - return this; - } - - public Sql argDateNowPerApp(String argName) { - sqlArgs.argDateNowPerApp(argName); + sqlArgs.argLocalDateTime(argName, arg); return this; } public Sql argDateNowPerDb() { - sqlArgs.argDateNowPerDb(); + sqlArgs.argLocalDateTimeNowPerDb(); return this; } public Sql argDateNowPerDb(String argName) { - sqlArgs.argDateNowPerDb(argName); + sqlArgs.argLocalDateTimeNowPerDb(argName); return this; } diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlArgs.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlArgs.java index 6b8f512..2f49453 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlArgs.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlArgs.java @@ -63,62 +63,53 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl } return uniqueNames.toArray(new String[uniqueNames.size()]); } - public SqlArgs argBoolean(Boolean arg) { invocations.add(new Invocation(ColumnType.Boolean, null, arg)); return this; } - - public SqlArgs argBoolean( String argName, Boolean arg) { + public SqlArgs argBoolean(String argName, Boolean arg) { invocations.add(new Invocation(ColumnType.Boolean, argName, arg)); return this; } - public SqlArgs argInteger(Integer arg) { invocations.add(new Invocation(ColumnType.Integer, null, arg)); return this; } - - public SqlArgs argInteger( String argName, Integer arg) { + public SqlArgs argInteger(String argName, Integer arg) { invocations.add(new Invocation(ColumnType.Integer, argName, arg)); return this; } - public SqlArgs argLong(Long arg) { invocations.add(new Invocation(ColumnType.Long, null, arg)); return this; } - - public SqlArgs argLong( String argName, Long arg) { + public SqlArgs argLong(String argName, Long arg) { invocations.add(new Invocation(ColumnType.Long, argName, arg)); return this; } - public SqlArgs argFloat(Float arg) { invocations.add(new Invocation(ColumnType.Float, null, arg)); return this; } - - public SqlArgs argFloat( String argName, Float arg) { + public SqlArgs argFloat(String argName, Float arg) { invocations.add(new Invocation(ColumnType.Float, argName, arg)); return this; } - public SqlArgs argDouble(Double arg) { invocations.add(new Invocation(ColumnType.Double, null, arg)); return this; } - public SqlArgs argDouble( String argName, Double arg) { + public SqlArgs argDouble(String argName, Double arg) { invocations.add(new Invocation(ColumnType.Double, argName, arg)); return this; } @@ -128,7 +119,7 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl return this; } - public SqlArgs argBigDecimal( String argName, BigDecimal arg) { + public SqlArgs argBigDecimal(String argName, BigDecimal arg) { invocations.add(new Invocation(ColumnType.BigDecimal, argName, arg)); return this; } @@ -138,109 +129,80 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl return this; } - public SqlArgs argString( String argName, String arg) { + public SqlArgs argString(String argName, String arg) { invocations.add(new Invocation(ColumnType.String, argName, arg)); return this; } - public SqlArgs argDate(LocalDateTime arg) { - // date argument with a time on it - invocations.add(new Invocation(ColumnType.Date, null, arg)); + public SqlArgs argLocalDateTime(LocalDateTime arg) { + invocations.add(new Invocation(ColumnType.LocalDateTime, null, arg)); return this; } - public SqlArgs argDate( String argName, LocalDateTime arg) { - // date argument with a time on it - invocations.add(new Invocation(ColumnType.Date, argName, arg)); + public SqlArgs argLocalDateTime(String argName, LocalDateTime arg) { + invocations.add(new Invocation(ColumnType.LocalDateTime, argName, arg)); return this; } - public SqlArgs argLocalDate(LocalDate arg) { - // date argument with no time on it invocations.add(new Invocation(ColumnType.LocalDate, null, arg)); return this; } - - public SqlArgs argLocalDate( String argName, LocalDate arg) { - // date argument with no time on it + public SqlArgs argLocalDate(String argName, LocalDate arg) { invocations.add(new Invocation(ColumnType.LocalDate, argName, arg)); return this; } - - public SqlArgs argDateNowPerApp() { - invocations.add(new Invocation(ColumnType.DateNowPerApp, null, null)); + public SqlArgs argLocalDateTimeNowPerDb() { + invocations.add(new Invocation(ColumnType.LocalDateTimeNowPerDb, null, null)); return this; } - - public SqlArgs argDateNowPerApp( String argName) { - invocations.add(new Invocation(ColumnType.DateNowPerApp, argName, null)); + public SqlArgs argLocalDateTimeNowPerDb(String argName) { + invocations.add(new Invocation(ColumnType.LocalDateTimeNowPerDb, argName, null)); return this; } - - - public SqlArgs argDateNowPerDb() { - invocations.add(new Invocation(ColumnType.DateNowPerDb, null, null)); - return this; - } - - - public SqlArgs argDateNowPerDb( String argName) { - invocations.add(new Invocation(ColumnType.DateNowPerDb, argName, null)); - return this; - } - public SqlArgs argBlobBytes(byte[] arg) { invocations.add(new Invocation(ColumnType.BlobBytes, null, arg)); return this; } - - public SqlArgs argBlobBytes( String argName, byte[] arg) { + public SqlArgs argBlobBytes(String argName, byte[] arg) { invocations.add(new Invocation(ColumnType.BlobBytes, argName, arg)); return this; } - public SqlArgs argBlobInputStream(InputStream arg) { invocations.add(new Invocation(ColumnType.BlobStream, null, arg)); return this; } - - public SqlArgs argBlobInputStream( String argName, InputStream arg) { + public SqlArgs argBlobInputStream(String argName, InputStream arg) { invocations.add(new Invocation(ColumnType.BlobStream, argName, arg)); return this; } - public SqlArgs argClobString(String arg) { invocations.add(new Invocation(ColumnType.ClobString, null, arg)); return this; } - - public SqlArgs argClobString( String argName, String arg) { + public SqlArgs argClobString(String argName, String arg) { invocations.add(new Invocation(ColumnType.ClobString, argName, arg)); return this; } - public SqlArgs argClobReader(Reader arg) { invocations.add(new Invocation(ColumnType.ClobStream, null, arg)); return this; } - - public SqlArgs argClobReader( String argName, Reader arg) { + public SqlArgs argClobReader(String argName, Reader arg) { invocations.add(new Invocation(ColumnType.ClobStream, argName, arg)); return this; } - public SqlArgs makePositional() { for (Invocation invocation : invocations) { @@ -248,7 +210,6 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl } return this; } - public List names() { List names = new ArrayList<>(); @@ -348,25 +309,18 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl select.argLocalDate(i.argName, (LocalDate) i.arg); } break; - case Date: + case LocalDateTime: if (i.argName == null) { - select.argDate((LocalDateTime) i.arg); + select.argLocalDateTime((LocalDateTime) i.arg); } else { - select.argDate(i.argName, (LocalDateTime) i.arg); + select.argLocalDateTime(i.argName, (LocalDateTime) i.arg); } break; - case DateNowPerApp: + case LocalDateTimeNowPerDb: if (i.argName == null) { - select.argDateNowPerApp(); + select.argLocalDateTimeNowPerDb(); } else { - select.argDateNowPerApp(i.argName); - } - break; - case DateNowPerDb: - if (i.argName == null) { - select.argDateNowPerDb(); - } else { - select.argDateNowPerDb(i.argName); + select.argLocalDateTimeNowPerDb(i.argName); } break; } @@ -456,33 +410,24 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl } break; case LocalDate: - // date argument with no time on it if (i.argName == null) { insert.argLocalDate((LocalDate) i.arg); } else { insert.argLocalDate(i.argName, (LocalDate) i.arg); } break; - case Date: - // date argument with a time on it + case LocalDateTime: if (i.argName == null) { - insert.argDate((LocalDateTime) i.arg); + insert.argLocalDateTime((LocalDateTime) i.arg); } else { - insert.argDate(i.argName, (LocalDateTime) i.arg); + insert.argLocalDateTime(i.argName, (LocalDateTime) i.arg); } break; - case DateNowPerApp: + case LocalDateTimeNowPerDb: if (i.argName == null) { - insert.argDateNowPerApp(); + insert.argLocalDateTimeNowPerDb(); } else { - insert.argDateNowPerApp(i.argName); - } - break; - case DateNowPerDb: - if (i.argName == null) { - insert.argDateNowPerDb(); - } else { - insert.argDateNowPerDb(i.argName); + insert.argLocalDateTimeNowPerDb(i.argName); } break; } @@ -578,25 +523,18 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl update.argLocalDate(i.argName, (LocalDate) i.arg); } break; - case Date: + case LocalDateTime: if (i.argName == null) { - update.argDate((LocalDateTime) i.arg); + update.argLocalDateTime((LocalDateTime) i.arg); } else { - update.argDate(i.argName, (LocalDateTime) i.arg); + update.argLocalDateTime(i.argName, (LocalDateTime) i.arg); } break; - case DateNowPerApp: + case LocalDateTimeNowPerDb: if (i.argName == null) { - update.argDateNowPerApp(); + update.argLocalDateTimeNowPerDb(); } else { - update.argDateNowPerApp(i.argName); - } - break; - case DateNowPerDb: - if (i.argName == null) { - update.argDateNowPerDb(); - } else { - update.argDateNowPerDb(i.argName); + update.argLocalDateTimeNowPerDb(i.argName); } break; } @@ -622,14 +560,29 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl } public enum ColumnType { - Integer, Long, Float, Double, BigDecimal, String, ClobString, ClobStream, - BlobBytes, BlobStream, Date, LocalDate, DateNowPerApp, DateNowPerDb, Boolean + String, + Boolean, + Integer, + Long, + Float, + Double, + BigDecimal, + ClobString, + ClobStream, + BlobBytes, + BlobStream, + LocalDate, + LocalDateTime, + LocalDateTimeNowPerDb } private static class Invocation { - ColumnType columnType; - String argName; - Object arg; + + private final ColumnType columnType; + + private String argName; + + private final Object arg; Invocation(ColumnType columnType, String argName, Object arg) { this.columnType = columnType; @@ -639,8 +592,12 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Invocation that = (Invocation) o; return columnType == that.columnType && Objects.equals(argName, that.argName) && @@ -659,9 +616,13 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl } public static class Builder { + private final int[] types; + private final int[] precision; + private final int[] scale; + private String[] names; public Builder(Row r) { @@ -672,14 +633,12 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl types = new int[columnCount]; precision = new int[columnCount]; scale = new int[columnCount]; - for (int i = 0; i < columnCount; i++) { names[i] = metadata.getColumnLabel(i + 1); types[i] = metadata.getColumnType(i + 1); precision[i] = metadata.getPrecision(i + 1); scale[i] = metadata.getScale(i + 1); } - names = tidyColumnNames(names); } catch (SQLException e) { throw new DatabaseException("Unable to retrieve metadata from ResultSet", e); @@ -727,22 +686,19 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl case Types.NCLOB: args.argClobString(names[i], r.getClobStringOrNull()); break; - - // Check Date before TimeStamp because SQL dates are also timestamps case Types.DATE: args.argLocalDate(names[i], r.getLocalDateOrNull()); break; - case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: if (this.scale[i] == 0) { // If the scale is 0, this is a LocalDate (no time/timezone). // Anything with a time will have a non-zero scale args.argLocalDate(names[i], r.getLocalDateOrNull()); } else { - args.argDate(names[i], r.getLocalDateTimeOrNull()); + args.argLocalDateTime(names[i], r.getLocalDateTimeOrNull()); } break; - case Types.NVARCHAR: case Types.VARCHAR: case Types.CHAR: diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsert.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsert.java index f712da6..22acf51 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsert.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsert.java @@ -5,6 +5,7 @@ import java.io.Reader; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; /** * Interface for configuring (setting parameters) and executing a chunk of SQL. @@ -39,21 +40,19 @@ public interface SqlInsert { SqlInsert argString( String argName, String arg); - SqlInsert argDate(LocalDateTime arg); // date with time + SqlInsert argLocalDateTime(LocalDateTime arg); - SqlInsert argDate(String argName, LocalDateTime arg); // date with time + SqlInsert argLocalDateTime(LocalDateTime arg, ZoneId zoneId); - SqlInsert argLocalDate(LocalDate arg); // date only - no timestamp + SqlInsert argLocalDateTime(String argName, LocalDateTime arg); - SqlInsert argLocalDate( String argName, LocalDate arg); // date only - no timestamp + SqlInsert argLocalDate(LocalDate arg); - SqlInsert argDateNowPerApp(); + SqlInsert argLocalDate( String argName, LocalDate arg); - SqlInsert argDateNowPerApp(String argName); + SqlInsert argLocalDateTimeNowPerDb(); - SqlInsert argDateNowPerDb(); - - SqlInsert argDateNowPerDb(String argName); + SqlInsert argLocalDateTimeNowPerDb(String argName); SqlInsert argBlobBytes(byte[] arg); @@ -126,7 +125,6 @@ public interface SqlInsert { *

This version of insert expects exactly one row to be inserted, and will throw * a DatabaseException if that isn't the case.

*/ - Long insertReturningPkSeq(String primaryKeyColumnName); T insertReturning(String tableName, String primaryKeyColumnName, RowsHandler rowsHandler, diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java index 143544a..6f26d40 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java @@ -15,6 +15,7 @@ import java.sql.ResultSet; import java.sql.Statement; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -40,9 +41,9 @@ public class SqlInsertImpl implements SqlInsert { private List batched; - private List parameterList; // !null ==> traditional ? args + private List parameterList; - private Map parameterMap; // !null ==> named :abc args + private Map parameterMap; private String pkArgName; @@ -141,13 +142,18 @@ public class SqlInsertImpl implements SqlInsert { } @Override - public SqlInsert argDate(LocalDateTime arg) { - return positionalArg(adaptor.nullDate(arg)); + public SqlInsert argLocalDateTime(LocalDateTime arg) { + return positionalArg(adaptor.nullLocalDateTime(arg)); } @Override - public SqlInsert argDate( String argName, LocalDateTime arg) { - return namedArg(argName, adaptor.nullDate(arg)); + public SqlInsert argLocalDateTime(LocalDateTime arg, ZoneId zoneId) { + return positionalArg(adaptor.nullLocalDateTime(arg, zoneId)); + } + + @Override + public SqlInsert argLocalDateTime(String argName, LocalDateTime arg) { + return namedArg(argName, adaptor.nullLocalDateTime(arg)); } @Override @@ -161,27 +167,17 @@ public class SqlInsertImpl implements SqlInsert { } @Override - public SqlInsert argDateNowPerApp() { - return positionalArg(adaptor.nullDate(options.currentDate())); - } - - @Override - public SqlInsert argDateNowPerApp( String argName) { - return namedArg(argName, adaptor.nullDate(options.currentDate())); - } - - @Override - public SqlInsert argDateNowPerDb() { - if (options.useDatePerAppOnly()) { - return positionalArg(adaptor.nullDate(options.currentDate())); + public SqlInsert argLocalDateTimeNowPerDb() { + if (options.useLocalDateTimeOnly()) { + return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now())); } return positionalArg(new RewriteArg(options.flavor().dbTimeMillis())); } @Override - public SqlInsert argDateNowPerDb( String argName) { - if (options.useDatePerAppOnly()) { - return namedArg(argName, adaptor.nullDate(options.currentDate())); + public SqlInsert argLocalDateTimeNowPerDb(String argName) { + if (options.useLocalDateTimeOnly()) { + return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now())); } return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis())); } @@ -235,7 +231,6 @@ public class SqlInsertImpl implements SqlInsert { public SqlInsert withArgs(SqlArgs args) { return apply(args); } - @Override public SqlInsert apply(Apply apply) { @@ -312,7 +307,6 @@ public class SqlInsertImpl implements SqlInsert { if (!hasPk()) { throw new DatabaseException("Identify a primary key with argPk*() before insertReturning()"); } - if (options.flavor().supportsInsertReturning()) { return updateInternal(1, primaryKeyColumnName, handler, otherColumnNames); } else if (pkSeqName != null) { @@ -367,7 +361,6 @@ public class SqlInsertImpl implements SqlInsert { } @Override - public SqlInsert argPkSeq( String argName, String sequenceName) { if (hasPk() && batched == null) { throw new DatabaseException("Only call one argPk*() method"); @@ -381,7 +374,6 @@ public class SqlInsertImpl implements SqlInsert { } @Override - public SqlInsert argPkLong(String argName, Long arg) { if (hasPk() && batched == null) { throw new DatabaseException("Only call one argPk*() method"); @@ -395,7 +387,6 @@ public class SqlInsertImpl implements SqlInsert { } @Override - public SqlInsert argPkLong(Long arg) { if (hasPk() && batched == null) { throw new DatabaseException("Only call one argPk*() method"); @@ -415,18 +406,14 @@ public class SqlInsertImpl implements SqlInsert { private int[] insertBatchInternal() { batch(); - if (batched == null || batched.size() == 0) { throw new DatabaseException("Batch insert requires parameters"); } - PreparedStatement ps = null; Metric metric = new Metric(log.isLoggable(Level.FINE)); - String executeSql = sql; Object[] firstRowParameters = null; List parameters = new ArrayList<>(); - boolean isSuccess = false; String errorCode = null; Exception logEx = null; @@ -444,7 +431,6 @@ public class SqlInsertImpl implements SqlInsert { } parameters.add(mpSql.getArgs()); } - if (connection != null) { ps = connection.prepareStatement(executeSql); @@ -452,7 +438,6 @@ public class SqlInsertImpl implements SqlInsert { adaptor.addParameters(ps, params); ps.addBatch(); } - metric.checkpoint("prep"); int[] numAffectedRows = ps.executeBatch(); metric.checkpoint("execBatch", parameters.size()); @@ -482,13 +467,10 @@ public class SqlInsertImpl implements SqlInsert { if (batched != null) { throw new DatabaseException("Call insertBatch() if you are using the batch() feature"); } - PreparedStatement ps = null; Metric metric = new Metric(log.isLoggable(Level.FINE)); - String executeSql = sql; Object[] parameters = null; - boolean isSuccess = false; String errorCode = null; Exception logEx = null; @@ -496,10 +478,8 @@ public class SqlInsertImpl implements SqlInsert { MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap); executeSql = mpSql.getSqlToExecute(); parameters = mpSql.getArgs(); - if (connection != null) { ps = connection.prepareStatement(executeSql); - adaptor.addParameters(ps, parameters); metric.checkpoint("prep"); int numAffectedRows = ps.executeUpdate(); @@ -536,14 +516,11 @@ public class SqlInsertImpl implements SqlInsert { if (batched != null) { throw new DatabaseException("Call insertBatch() if you are using the batch() feature"); } - PreparedStatement ps = null; ResultSet rs = null; Metric metric = new Metric(log.isLoggable(Level.FINE)); - String executeSql = sql; Object[] parameters = null; - boolean isSuccess = false; String errorCode = null; Exception logEx = null; @@ -551,10 +528,8 @@ public class SqlInsertImpl implements SqlInsert { MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap); executeSql = mpSql.getSqlToExecute(); parameters = mpSql.getArgs(); - if (connection != null) { ps = connection.prepareStatement(executeSql, new String[]{pkToReturn}); - adaptor.addParameters(ps, parameters); metric.checkpoint("prep"); int numAffectedRows = ps.executeUpdate(); @@ -598,14 +573,11 @@ public class SqlInsertImpl implements SqlInsert { if (batched != null) { throw new DatabaseException("Call insertBatch() if you are using the batch() feature"); } - PreparedStatement ps = null; ResultSet rs = null; Metric metric = new Metric(log.isLoggable(Level.FINE)); - String executeSql = sql; Object[] parameters = null; - boolean isSuccess = false; String errorCode = null; Exception logEx = null; @@ -613,14 +585,11 @@ public class SqlInsertImpl implements SqlInsert { MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap); executeSql = mpSql.getSqlToExecute(); parameters = mpSql.getArgs(); - String[] returnCols = new String[otherCols.length + 1]; returnCols[0] = pkToReturn; System.arraycopy(otherCols, 0, returnCols, 1, otherCols.length); - if (connection != null) { ps = connection.prepareStatement(executeSql, returnCols); - adaptor.addParameters(ps, parameters); metric.checkpoint("prep"); int numAffectedRows = ps.executeUpdate(); @@ -682,8 +651,8 @@ public class SqlInsertImpl implements SqlInsert { } private static class Batch { - private final List parameterList; // !null ==> traditional ? args - private final Map parameterMap; // !null ==> named :abc args + private final List parameterList; + private final Map parameterMap; public Batch(List parameterList, Map parameterMap) { this.parameterList = parameterList; diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelect.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelect.java index 12ed371..e97c8a3 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelect.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelect.java @@ -3,6 +3,7 @@ package org.xbib.jdbc.query; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.List; /** @@ -10,126 +11,64 @@ import java.util.List; */ public interface SqlSelect { - SqlSelect argBoolean(Boolean arg); - - SqlSelect argBoolean( String argName, Boolean arg); - - SqlSelect argInteger(Integer arg); - - SqlSelect argInteger( String argName, Integer arg); - - SqlSelect argLong(Long arg); - - SqlSelect argLong( String argName, Long arg); - - SqlSelect argFloat(Float arg); - - SqlSelect argFloat( String argName, Float arg); - - SqlSelect argDouble(Double arg); - - SqlSelect argDouble( String argName, Double arg); - - SqlSelect argBigDecimal(BigDecimal arg); - - SqlSelect argBigDecimal( String argName, BigDecimal arg); - - SqlSelect argString(String arg); - - SqlSelect argString( String argName, String arg); - - - SqlSelect argDate(LocalDateTime arg); // Date with time + SqlSelect argLocalDateTime(LocalDateTime arg); - SqlSelect argDate(String argName, LocalDateTime arg); // Date with time + SqlSelect argLocalDateTime(String argName, LocalDateTime arg); - - - SqlSelect argLocalDate(LocalDate arg); // Date without time + SqlSelect argLocalDate(LocalDate arg); - - - SqlSelect argLocalDate( String argName, LocalDate arg); // Date without time + SqlSelect argLocalDate( String argName, LocalDate arg); - - - SqlSelect argDateNowPerApp(); + SqlSelect argLocalDateTimeNowPerDb(); - - - SqlSelect argDateNowPerApp( String argName); + SqlSelect argLocalDateTimeNowPerDb(String argName); - - - SqlSelect argDateNowPerDb(); - - - - SqlSelect argDateNowPerDb( String argName); - - - SqlSelect withTimeoutSeconds(int seconds); - - SqlSelect withMaxRows(int rows); - - SqlSelect withArgs(SqlArgs args); - - SqlSelect apply(Apply apply); - - SqlSelect fetchSize(int fetchSize); - - Boolean queryBooleanOrNull(); - boolean queryBooleanOrFalse(); - boolean queryBooleanOrTrue(); - - Long queryLongOrNull(); - long queryLongOrZero(); /** @@ -137,61 +76,34 @@ public interface SqlSelect { * * @return the first column values, omitting any that were null */ - - List queryLongs(); - - Integer queryIntegerOrNull(); - int queryIntegerOrZero(); - - List queryIntegers(); - - Float queryFloatOrNull(); - float queryFloatOrZero(); - - List queryFloats(); - - Double queryDoubleOrNull(); - double queryDoubleOrZero(); - - List queryDoubles(); - - BigDecimal queryBigDecimalOrNull(); - - BigDecimal queryBigDecimalOrZero(); - - List queryBigDecimals(); - - String queryStringOrNull(); - - String queryStringOrEmpty(); /** @@ -199,25 +111,19 @@ public interface SqlSelect { * * @return the first column values, omitting any that were null */ - - List queryStrings(); - - - LocalDateTime queryDateOrNull(); // Date with time + LocalDateTime queryLocalDateTimeOrNull(); - - - List queryDates(); // Date with time + LocalDateTime queryLocalDateTimeOrNull(ZoneId zoneId); - - - LocalDate queryLocalDateOrNull(); // Date without time + List queryLocalDateTimes(); + List queryLocalDateTimes(ZoneId zoneId); + + LocalDate queryLocalDateOrNull(); - - List queryLocalDates(); // Date without time + List queryLocalDates(); /** * This is the most generic and low-level way to iterate the query results. diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelectImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelectImpl.java index 7c92936..d72f594 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelectImpl.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelectImpl.java @@ -11,6 +11,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -23,7 +24,7 @@ import java.util.logging.Logger; */ public class SqlSelectImpl implements SqlSelect { - private static final Logger log = Logger.getLogger(Database.class.getName()); + private static final Logger logger = Logger.getLogger(Database.class.getName()); private final Connection connection; @@ -114,115 +115,84 @@ public class SqlSelectImpl implements SqlSelect { return namedArg(argName, adaptor.nullNumeric(arg)); } - @Override public SqlSelect argBigDecimal(BigDecimal arg) { return positionalArg(adaptor.nullNumeric(arg)); } - @Override public SqlSelect argBigDecimal( String argName, BigDecimal arg) { return namedArg(argName, adaptor.nullNumeric(arg)); } - @Override public SqlSelect argString(String arg) { return positionalArg(adaptor.nullString(arg)); } - @Override - public SqlSelect argString( String argName, String arg) { + public SqlSelect argString(String argName, String arg) { return namedArg(argName, adaptor.nullString(arg)); } - @Override - public SqlSelect argDate(LocalDateTime arg) { - // Date with time - return positionalArg(adaptor.nullDate(arg)); + public SqlSelect argLocalDateTime(LocalDateTime arg) { + return positionalArg(adaptor.nullLocalDateTime(arg)); } - @Override - public SqlSelect argDate( String argName, LocalDateTime arg) { - // Date with time - return namedArg(argName, adaptor.nullDate(arg)); + public SqlSelect argLocalDateTime(String argName, LocalDateTime arg) { + return namedArg(argName, adaptor.nullLocalDateTime(arg)); } - @Override public SqlSelect argLocalDate(LocalDate arg) { - // Date with no time return positionalArg(adaptor.nullLocalDate(arg)); } - @Override public SqlSelect argLocalDate( String argName, LocalDate arg) { - // Date with no time return namedArg(argName, adaptor.nullLocalDate(arg)); } - @Override - public SqlSelect argDateNowPerApp() { - return positionalArg(adaptor.nullDate(options.currentDate())); - } - - - @Override - public SqlSelect argDateNowPerApp( String argName) { - return namedArg(argName, adaptor.nullDate(options.currentDate())); - } - - - @Override - public SqlSelect argDateNowPerDb() { - if (options.useDatePerAppOnly()) { - return positionalArg(adaptor.nullDate(options.currentDate())); + public SqlSelect argLocalDateTimeNowPerDb() { + if (options.useLocalDateTimeOnly()) { + return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now())); } return positionalArg(new RewriteArg(options.flavor().dbTimeMillis())); } - @Override - public SqlSelect argDateNowPerDb( String argName) { - if (options.useDatePerAppOnly()) { - return namedArg(argName, adaptor.nullDate(options.currentDate())); + public SqlSelect argLocalDateTimeNowPerDb(String argName) { + if (options.useLocalDateTimeOnly()) { + return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now())); } return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis())); } - @Override public SqlSelect withTimeoutSeconds(int seconds) { timeoutSeconds = seconds; return this; } - @Override public SqlSelect withMaxRows(int rows) { maxRows = rows; return this; } - @Override public SqlSelect withArgs(SqlArgs args) { return apply(args); } - @Override public SqlSelect apply(Apply apply) { apply.apply(this); return this; } - @Override public SqlSelect fetchSize(int rows) { @@ -253,7 +223,6 @@ public class SqlSelectImpl implements SqlSelect { } @Override - public Long queryLongOrNull() { return queryWithTimeout(rs -> { if (rs.next()) { @@ -287,7 +256,6 @@ public class SqlSelectImpl implements SqlSelect { return result; }); } - @Override public Integer queryIntegerOrNull() { @@ -323,7 +291,6 @@ public class SqlSelectImpl implements SqlSelect { return result; }); } - @Override public Float queryFloatOrNull() { @@ -404,7 +371,6 @@ public class SqlSelectImpl implements SqlSelect { return null; }); } - @Override public BigDecimal queryBigDecimalOrZero() { @@ -415,7 +381,6 @@ public class SqlSelectImpl implements SqlSelect { return new BigDecimal(0); }); } - @Override public List queryBigDecimals() { @@ -440,7 +405,6 @@ public class SqlSelectImpl implements SqlSelect { return null; }); } - @Override public String queryStringOrEmpty() { @@ -467,21 +431,31 @@ public class SqlSelectImpl implements SqlSelect { } @Override - public LocalDateTime queryDateOrNull() { + public LocalDateTime queryLocalDateTimeOrNull() { + return queryLocalDateTimeOrNull(null); + } + + @Override + public LocalDateTime queryLocalDateTimeOrNull(ZoneId zoneId) { return queryWithTimeout(rs -> { if (rs.next()) { - return rs.getLocalDateTimeOrNull(1); + return rs.getLocalDateTimeOrNull(1, zoneId); } return null; }); } - + @Override - public List queryDates() { + public List queryLocalDateTimes() { + return queryLocalDateTimes(null); + } + + @Override + public List queryLocalDateTimes(ZoneId zoneId) { return queryWithTimeout(rs -> { List result = new ArrayList<>(); while (rs.next()) { - LocalDateTime value = rs.getLocalDateTimeOrNull(1); + LocalDateTime value = rs.getLocalDateTimeOrNull(1, zoneId); if (value != null) { result.add(value); } @@ -489,7 +463,7 @@ public class SqlSelectImpl implements SqlSelect { return result; }); } - + @Override public LocalDate queryLocalDateOrNull() { // Date without time @@ -603,11 +577,9 @@ public class SqlSelectImpl implements SqlSelect { private T queryWithTimeout(RowsHandler handler) { assert ps == null; ResultSet rs = null; - Metric metric = new Metric(log.isLoggable(Level.FINE)); - + Metric metric = new Metric(logger.isLoggable(Level.FINE)); String executeSql = sql; Object[] parameters = null; - boolean isWarn = false; boolean isSuccess = false; String errorCode = null; @@ -616,26 +588,21 @@ public class SqlSelectImpl implements SqlSelect { MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap); executeSql = mpSql.getSqlToExecute(); parameters = mpSql.getArgs(); - if (connection != null) { synchronized (cancelLock) { ps = connection.prepareStatement(executeSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } - if (timeoutSeconds >= 0) { ps.setQueryTimeout(timeoutSeconds); } - if (maxRows > 0) { ps.setMaxRows(maxRows); } - if (fetchSize >= 0) { ps.setFetchSize(fetchSize); } - adaptor.addParameters(ps, parameters); metric.checkpoint("prep"); rs = ps.executeQuery(); @@ -662,18 +629,18 @@ public class SqlSelectImpl implements SqlSelect { logEx = e; throw DatabaseException.wrap(DebugSql.exceptionMessage(executeSql, parameters, errorCode, options), e); } finally { - adaptor.closeQuietly(rs, log); - adaptor.closeQuietly(ps, log); + adaptor.closeQuietly(rs, logger); + adaptor.closeQuietly(ps, logger); synchronized (cancelLock) { ps = null; } metric.done("close"); if (isSuccess) { - DebugSql.logSuccess("Query", log, metric, executeSql, parameters, options); + DebugSql.logSuccess("Query", logger, metric, executeSql, parameters, options); } else if (isWarn) { - DebugSql.logWarning("Query", log, metric, "QueryTimedOutException", executeSql, parameters, options, null); + DebugSql.logWarning("Query", logger, metric, "QueryTimedOutException", executeSql, parameters, options, null); } else { - DebugSql.logError("Query", log, metric, errorCode, executeSql, parameters, options, logEx); + DebugSql.logError("Query", logger, metric, errorCode, executeSql, parameters, options, logEx); } } } diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdate.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdate.java index 1d2cb78..b4776f6 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdate.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdate.java @@ -39,21 +39,17 @@ public interface SqlUpdate { SqlUpdate argString(String argName, String arg); - SqlUpdate argDate(LocalDateTime arg); + SqlUpdate argLocalDateTime(LocalDateTime arg); - SqlUpdate argDate(String argName, LocalDateTime arg); + SqlUpdate argLocalDateTime(String argName, LocalDateTime arg); SqlUpdate argLocalDate(LocalDate arg); SqlUpdate argLocalDate(String argName, LocalDate arg); - SqlUpdate argDateNowPerApp(); - - SqlUpdate argDateNowPerApp(String argName); + SqlUpdate argLocalDateTimeNowPerDb(); - SqlUpdate argDateNowPerDb(); - - SqlUpdate argDateNowPerDb(String argName); + SqlUpdate argLocalDateTimeNowPerDb(String argName); SqlUpdate argBlobBytes(byte[] arg); diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java index 562472c..06b81e5 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java @@ -49,187 +49,149 @@ public class SqlUpdateImpl implements SqlUpdate { this.options = options; adaptor = new StatementAdaptor(options); } - @Override public SqlUpdate argBoolean(Boolean arg) { return positionalArg(adaptor.nullString(booleanToString(arg))); } - @Override public SqlUpdate argBoolean(String argName, Boolean arg) { return namedArg(argName, adaptor.nullString(booleanToString(arg))); } @Override - public SqlUpdate argInteger(Integer arg) { return positionalArg(adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argInteger(String argName, Integer arg) { return namedArg(argName, adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argLong(Long arg) { return positionalArg(adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argLong(String argName, Long arg) { return namedArg(argName, adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argFloat(Float arg) { return positionalArg(adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argFloat(String argName, Float arg) { return namedArg(argName, adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argDouble(Double arg) { return positionalArg(adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argDouble(String argName, Double arg) { return namedArg(argName, adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argBigDecimal(BigDecimal arg) { return positionalArg(adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argBigDecimal(String argName, BigDecimal arg) { return namedArg(argName, adaptor.nullNumeric(arg)); } @Override - public SqlUpdate argString(String arg) { return positionalArg(adaptor.nullString(arg)); } @Override - public SqlUpdate argString(String argName, String arg) { return namedArg(argName, adaptor.nullString(arg)); } @Override - - public SqlUpdate argDate(LocalDateTime arg) { - return positionalArg(adaptor.nullDate(arg)); + public SqlUpdate argLocalDateTime(LocalDateTime arg) { + return positionalArg(adaptor.nullLocalDateTime(arg)); } @Override - - public SqlUpdate argDate(String argName, LocalDateTime arg) { - return namedArg(argName, adaptor.nullDate(arg)); + public SqlUpdate argLocalDateTime(String argName, LocalDateTime arg) { + return namedArg(argName, adaptor.nullLocalDateTime(arg)); } @Override - public SqlUpdate argLocalDate(LocalDate arg) { return positionalArg(adaptor.nullLocalDate(arg)); } @Override - public SqlUpdate argLocalDate(String argName, LocalDate arg) { return namedArg(argName, adaptor.nullLocalDate(arg)); } - - @Override - public SqlUpdate argDateNowPerApp() { - return positionalArg(adaptor.nullDate(options.currentDate())); - } @Override - - public SqlUpdate argDateNowPerApp(String argName) { - return namedArg(argName, adaptor.nullDate(options.currentDate())); - } - - @Override - public SqlUpdate argDateNowPerDb() { - if (options.useDatePerAppOnly()) { - return positionalArg(adaptor.nullDate(options.currentDate())); + public SqlUpdate argLocalDateTimeNowPerDb() { + if (options.useLocalDateTimeOnly()) { + return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now())); } return positionalArg(new RewriteArg(options.flavor().dbTimeMillis())); } @Override - - public SqlUpdate argDateNowPerDb(String argName) { - if (options.useDatePerAppOnly()) { - return namedArg(argName, adaptor.nullDate(options.currentDate())); + public SqlUpdate argLocalDateTimeNowPerDb(String argName) { + if (options.useLocalDateTimeOnly()) { + return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now())); } return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis())); } @Override - public SqlUpdate argBlobBytes(byte[] arg) { return positionalArg(adaptor.nullBytes(arg)); } @Override - public SqlUpdate argBlobBytes(String argName, byte[] arg) { return namedArg(argName, adaptor.nullBytes(arg)); } @Override - public SqlUpdate argBlobStream(InputStream arg) { return positionalArg(adaptor.nullInputStream(arg)); } @Override - public SqlUpdate argBlobStream(String argName, InputStream arg) { return namedArg(argName, adaptor.nullInputStream(arg)); } @Override - public SqlUpdate argClobString(String arg) { return positionalArg(adaptor.nullClobReader(arg == null ? null : new InternalStringReader(arg))); } @Override - public SqlUpdate argClobString(String argName, String arg) { return namedArg(argName, adaptor.nullClobReader(arg == null ? null : new InternalStringReader(arg))); } @Override - public SqlUpdate argClobReader(Reader arg) { return positionalArg(adaptor.nullClobReader(arg)); } @Override - public SqlUpdate argClobReader(String argName, Reader arg) { return namedArg(argName, adaptor.nullClobReader(arg)); } @@ -244,7 +206,6 @@ public class SqlUpdateImpl implements SqlUpdate { return apply(args); } - @Override public SqlUpdate apply(Apply apply) { apply.apply(this); @@ -419,8 +380,8 @@ public class SqlUpdateImpl implements SqlUpdate { } private static class Batch { - private final List parameterList; // !null ==> traditional ? args - private final Map parameterMap; // !null ==> named :abc args + private final List parameterList; + private final Map parameterMap; public Batch(List parameterList, Map parameterMap) { this.parameterList = parameterList; diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/StatementAdaptor.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/StatementAdaptor.java index e228108..906f0f5 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/StatementAdaptor.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/StatementAdaptor.java @@ -1,6 +1,6 @@ package org.xbib.jdbc.query; -import java.io.ByteArrayOutputStream; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.Reader; @@ -13,75 +13,32 @@ import java.sql.Timestamp; import java.sql.Types; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Date; -import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; /** * Deal with mapping parameters into prepared statements. */ public class StatementAdaptor { + + private static final Logger logger = Logger.getLogger(StatementAdaptor.class.getName()); + private final Options options; public StatementAdaptor(Options options) { this.options = options; } - private static String readerToString(Reader r) { - Scanner s = new Scanner(r).useDelimiter("\\A"); - return s.hasNext() ? s.next() : ""; - } - - private static byte[] streamToBytes(InputStream is) throws SQLException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length; - - try { - while ((length = is.read(buffer)) != -1) { - out.write(buffer, 0, length); - } - } catch (IOException e) { - throw new SQLException("Unable to convert InputStream parameter to bytes", e); - } - - return out.toByteArray(); - } - - /** - * Converts the java.util.Date into a java.sql.Timestamp, following the nanos/millis canonicalization - * required by the spec. If a java.sql.Timestamp is passed in (since it extends java.util.Date), - * it will be checked and canonicalized only if not already correct. - */ - private static Timestamp toSqlTimestamp(Date date) { - long millis = date.getTime(); - int fractionalSecondMillis = (int) (millis % 1000); // guaranteed < 1000 - - if (fractionalSecondMillis == 0) { // this means it's already correct by the spec - if (date instanceof Timestamp) { - return (Timestamp) date; - } else { - return new Timestamp(millis); - } - } else { // the millis are invalid and need to be corrected - int tsNanos = fractionalSecondMillis * 1000000; - long tsMillis = millis - fractionalSecondMillis; - Timestamp timestamp = new Timestamp(tsMillis); - timestamp.setNanos(tsNanos); - return timestamp; - } - } - public void addParameters(PreparedStatement ps, Object[] parameters) throws SQLException { for (int i = 0; i < parameters.length; i++) { Object parameter = parameters[i]; - // Unwrap secret args here so we can use them if (parameter instanceof SecretArg) { parameter = ((SecretArg) parameter).getArg(); } - if (parameter == null) { ParameterMetaData metaData; int parameterType; @@ -106,57 +63,52 @@ public class StatementAdaptor { } else if (parameter instanceof java.sql.Date) { ps.setDate(i + 1, (java.sql.Date) parameter); } else if (parameter instanceof Date) { + Date date = (Date) parameter; + LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + ps.setTimestamp(i + 1, Timestamp.valueOf(localDateTime)); // this will correct the millis and nanos according to the JDBC spec // if a correct Timestamp is passed in, this will detect that and leave it alone - ps.setTimestamp(i + 1, toSqlTimestamp((Date) parameter), options.calendarForTimestamps()); + //ps.setTimestamp(i + 1, toSqlTimestamp((Date) parameter), options.calendarForTimestamps()); } else if (parameter instanceof Reader) { if (options.useStringForClob()) { - ps.setString(i + 1, readerToString((Reader) parameter)); + try (BufferedReader reader = new BufferedReader((Reader) parameter)) { + ps.setString(i + 1, reader.lines().collect(Collectors.joining())); + } catch (IOException e) { + throw new SQLException(e); + } } else { ps.setCharacterStream(i + 1, (Reader) parameter); } } else if (parameter instanceof InputStream) { if (options.useBytesForBlob()) { - ps.setBytes(i + 1, streamToBytes((InputStream) parameter)); + try { + ps.setBytes(i + 1, ((InputStream) parameter).readAllBytes()); + } catch (IOException e) { + throw new SQLException(e); + } } else { ps.setBinaryStream(i + 1, (InputStream) parameter); } } else if (parameter instanceof Float) { - //if (options.flavor() == Flavor.oracle && ps.isWrapperFor(OraclePreparedStatement.class)) { - // The Oracle 11 driver setDouble() first converts the double to NUMBER, causing underflow - // for small values so we need to use the proprietary mechanism - //ps.unwrap(OraclePreparedStatement.class).setBinaryFloat(i + 1, (Float) parameter); - //} else { - ps.setFloat(i + 1, (Float) parameter); - //} + ps.setFloat(i + 1, (Float) parameter); } else if (parameter instanceof Double) { - //if (options.flavor() == Flavor.oracle && ps.isWrapperFor(OraclePreparedStatement.class)) { - // The Oracle 11 driver setDouble() first converts the double to NUMBER, causing underflow - // for small values so we need to use the proprietary mechanism - //ps.unwrap(OraclePreparedStatement.class).setBinaryDouble(i + 1, (Double) parameter); - //} else { - ps.setDouble(i + 1, (Double) parameter); - //} + ps.setDouble(i + 1, (Double) parameter); } else { ps.setObject(i + 1, parameter); } } } - public Object nullDate(LocalDateTime arg) { - if (arg == null) { - return new SqlNull(Types.TIMESTAMP); - } - return Timestamp.valueOf(arg); + public Object nullLocalDateTime(LocalDateTime arg) { + return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg); } - // Processes a true date without time information. - public Object nullLocalDate(LocalDate arg) { - if (arg == null) { - return new SqlNull(Types.DATE); - } + public Object nullLocalDateTime(LocalDateTime arg, ZoneId zoneId) { + return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg.atZone(zoneId).toLocalDateTime()); + } - return java.sql.Date.valueOf(arg); + public Object nullLocalDate(LocalDate arg) { + return arg == null ? new SqlNull(Types.DATE) : java.sql.Date.valueOf(arg); } public Object nullNumeric(Number arg) { @@ -217,5 +169,4 @@ public class StatementAdaptor { } } } - } diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/BigQuery.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/BigQuery.java index 8004eeb..0ae20d3 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/BigQuery.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/BigQuery.java @@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor; import org.xbib.jdbc.query.Flavor; -import java.sql.Date; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.Calendar; - public class BigQuery implements Flavor { @Override @@ -81,7 +76,7 @@ public class BigQuery implements Flavor { } @Override - public String typeDate() { + public String typeLocalDateTime() { return "datetime"; } @@ -150,20 +145,6 @@ public class BigQuery implements Flavor { return ""; } - @Override - public String dateAsSqlFunction(Timestamp date, Calendar calendar) { - // Construct a datetime literal - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); - dateFormat.setCalendar(calendar); - return String.format("datetime '%s'", dateFormat.format(date)); - } - - @Override - public String localDateAsSqlFunction(Date date) { - // Construct a datetime literal - return String.format("datetime '%s'", date.toString()); - } - @Override public String sequenceOptions() { throw new UnsupportedOperationException(); diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java index 4853e01..3d6b213 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java @@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor; import org.xbib.jdbc.query.Flavor; -import java.sql.Date; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.Calendar; - public class Derby implements Flavor { @Override @@ -80,7 +75,7 @@ public class Derby implements Flavor { } @Override - public String typeDate() { + public String typeLocalDateTime() { return "timestamp"; } @@ -149,18 +144,6 @@ public class Derby implements Flavor { return " from sysibm.sysdummy1"; } - @Override - public String dateAsSqlFunction(Timestamp date, Calendar calendar) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - dateFormat.setCalendar(calendar); - return "timestamp('" + dateFormat.format(date) + "')"; - } - - @Override - public String localDateAsSqlFunction(Date date) { - return "'" + date.toString() + "'"; - } - @Override public String sequenceOptions() { return " as bigint"; diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/H2.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/H2.java new file mode 100644 index 0000000..1e4b3f3 --- /dev/null +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/H2.java @@ -0,0 +1,175 @@ +package org.xbib.jdbc.query.flavor; + +import org.xbib.jdbc.query.Flavor; + +public class H2 implements Flavor { + + @Override + public String getName() { + return "h2"; + } + + @Override + public boolean supports(String url) { + return url.startsWith("jdbc:h2:"); + } + + @Override + public String driverClass() { + return "org.h2.Driver"; + } + + @Override + public boolean isNormalizedUpperCase() { + return true; + } + + @Override + public String typeInteger() { + return "integer"; + } + + @Override + public String typeBoolean() { + return "char(1)"; + } + + @Override + public String typeLong() { + return "bigint"; + } + + @Override + public String typeFloat() { + return "double"; + } + + @Override + public String typeDouble() { + return "double"; + } + + @Override + public String typeBigDecimal(int size, int precision) { + return "numeric(" + size + "," + precision + ")"; + } + + @Override + public String typeStringVar(int length) { + return "varchar(" + length + ")"; + } + + @Override + public String typeStringFixed(int length) { + return "char(" + length + ")"; + } + + @Override + public String typeClob() { + return "clob(2G)"; + } + + @Override + public String typeBlob() { + return "blob(2G)"; + } + + @Override + public String typeLocalDateTime() { + return "timestamp(3)"; + } + + @Override + public String typeLocalDate() { + return "date"; + } + + @Override + public boolean useStringForClob() { + return true; + } + + @Override + public boolean useBytesForBlob() { + return true; + } + + @Override + public String sequenceNextVal(String sequenceName) { + return "next value for " + sequenceName + ""; + } + + @Override + public String sequenceSelectNextVal(String sequenceName) { + return "select " + sequenceNextVal(sequenceName) + fromAny(); + } + + @Override + public String sequenceDrop(String dbtestSeq) { + return "drop sequence if exists " + dbtestSeq; + } + + @Override + public String tableDrop(String table) { + return "drop table if exists " + table; + } + + @Override + public String sequenceOrderClause(boolean order) { + return ""; + } + + @Override + public String sequenceCycleClause(boolean cycle) { + return cycle ? " cycle" : " no cycle"; + } + + @Override + public String fromAny() { + return " from (values(0))"; + } + + @Override + public boolean supportsInsertReturning() { + return false; + } + + @Override + public String dbTimeMillis() { + return "localtimestamp"; + } + + @Override + public String sequenceCacheClause(int nbrValuesToCache) { + return ""; + } + + @Override + public String sequenceOptions() { + return " as bigint"; + } + + @Override + public boolean isAutoCommitOnly() { + return false; + } + + @Override + public String writeNextIntoQueue(String table) { + throw new UnsupportedOperationException(); + } + + @Override + public String readNextFromQueue(String table) { + throw new UnsupportedOperationException(); + } + + @Override + public String succeedInQueue(String table) { + throw new UnsupportedOperationException(); + } + @Override + public String failInQueue(String table) { + throw new UnsupportedOperationException(); + } +} diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java index ef4c383..d07d358 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java @@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor; import org.xbib.jdbc.query.Flavor; -import java.sql.Date; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.Calendar; - public class Hsql implements Flavor { @Override @@ -80,8 +75,8 @@ public class Hsql implements Flavor { } @Override - public String typeDate() { - return "timestamp(3)"; + public String typeLocalDateTime() { + return "timestamp with time zone"; } @Override @@ -149,18 +144,6 @@ public class Hsql implements Flavor { return ""; } - @Override - public String dateAsSqlFunction(Timestamp date, Calendar calendar) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000XXX"); - dateFormat.setCalendar(calendar); - return "cast(timestamp '" + dateFormat.format(date) + "' as timestamp without time zone)"; - } - - @Override - public String localDateAsSqlFunction(Date date) { - return "'" + date.toString() + "'"; - } - @Override public String sequenceOptions() { return " as bigint"; diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/MySQL.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/MySQL.java new file mode 100644 index 0000000..a7e1930 --- /dev/null +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/MySQL.java @@ -0,0 +1,177 @@ +package org.xbib.jdbc.query.flavor; + +import org.xbib.jdbc.query.Flavor; + +public class MySQL implements Flavor { + + @Override + public String getName() { + return "mysql"; + } + + @Override + public boolean supports(String url) { + return url.startsWith("jdbc:mysql:"); + } + + @Override + public String driverClass() { + return "com.mysql.jdbc.Driver"; + } + + @Override + public boolean isNormalizedUpperCase() { + return true; + } + + @Override + public String typeInteger() { + return "integer"; + } + + @Override + public String typeBoolean() { + return "char(1)"; + } + + @Override + public String typeLong() { + return "bigint"; + } + + @Override + public String typeFloat() { + return "real"; + } + + @Override + public String typeDouble() { + return "double"; + } + + @Override + public String typeBigDecimal(int size, int precision) { + return "numeric(" + size + "," + precision + ")"; + } + + @Override + public String typeStringVar(int length) { + return "varchar(" + length + ")"; + } + + @Override + public String typeStringFixed(int length) { + return "char(" + length + ")"; + } + + @Override + public String typeClob() { + return "clob"; + } + + @Override + public String typeBlob() { + return "blob"; + } + + @Override + public String typeLocalDateTime() { + return "timestamp"; + } + + @Override + public String typeLocalDate() { + return "date"; + } + + @Override + public boolean useStringForClob() { + return false; + } + + @Override + public boolean useBytesForBlob() { + return false; + } + + @Override + public String sequenceNextVal(String sequenceName) { + return "next value for " + sequenceName; + } + + @Override + public String sequenceSelectNextVal(String sequenceName) { + return "values next value for " + sequenceName; + } + + @Override + public String sequenceDrop(String sequenceName) { + return "drop sequence " + sequenceName + " restrict"; + } + + @Override + public String tableDrop(String table) { + return "drop table " + table; + } + + @Override + public boolean supportsInsertReturning() { + return false; + } + + @Override + public String sequenceCacheClause(int nbrValuesToCache) { + return ""; + } + + @Override + public String sequenceOrderClause(boolean order) { + return ""; + } + + @Override + public String sequenceCycleClause(boolean cycle) { + return cycle ? " cycle" : " no cycle"; + } + + @Override + public String dbTimeMillis() { + return "current_timestamp"; + } + + @Override + public String fromAny() { + return " from sysibm.sysdummy1"; + } + + @Override + public String sequenceOptions() { + return " as bigint"; + } + + @Override + public boolean isAutoCommitOnly() { + return false; + } + + @Override + public String writeNextIntoQueue(String table) { + throw new UnsupportedOperationException(); + } + + @Override + public String readNextFromQueue(String table) { + throw new UnsupportedOperationException(); + } + + @Override + public String succeedInQueue(String table) { + throw new UnsupportedOperationException(); + } + + @Override + public String failInQueue(String table) { + throw new UnsupportedOperationException(); + } + +} diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Oracle.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Oracle.java index 385e352..943c13d 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Oracle.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Oracle.java @@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor; import org.xbib.jdbc.query.Flavor; -import java.sql.Date; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.Calendar; - public class Oracle implements Flavor { @Override @@ -60,7 +55,7 @@ public class Oracle implements Flavor { } @Override - public String typeDate() { + public String typeLocalDateTime() { return "timestamp(3)"; } @@ -152,18 +147,6 @@ public class Oracle implements Flavor { return " from dual"; } - @Override - public String dateAsSqlFunction(Timestamp date, Calendar calendar) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); - dateFormat.setCalendar(calendar); - return "timestamp '" + dateFormat.format(date) + "'"; - } - - @Override - public String localDateAsSqlFunction(Date date) { - return "to_date('" + date.toString() + "', 'yyyy-mm-dd')"; - } - @Override public String sequenceOptions() { return ""; diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Postgresql.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Postgresql.java index 0e5b0a7..e3fac12 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Postgresql.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Postgresql.java @@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor; import org.xbib.jdbc.query.Flavor; -import java.sql.Date; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.Calendar; - public class Postgresql implements Flavor { @Override @@ -80,7 +75,7 @@ public class Postgresql implements Flavor { } @Override - public String typeDate() { + public String typeLocalDateTime() { return "timestamp(3)"; } @@ -149,18 +144,6 @@ public class Postgresql implements Flavor { return " cache " + nbrValuesToCache; } - @Override - public String dateAsSqlFunction(Timestamp date, Calendar calendar) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); - dateFormat.setCalendar(calendar); - return "'" + dateFormat.format(date) + " GMT'::timestamp"; - } - - @Override - public String localDateAsSqlFunction(Date date) { - return "'" + date.toString() + "'"; - } - @Override public String sequenceOptions() { return ""; diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/SqlServer.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/SqlServer.java index 2c1ce32..e533746 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/SqlServer.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/SqlServer.java @@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor; import org.xbib.jdbc.query.Flavor; -import java.sql.Date; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.Calendar; - public class SqlServer implements Flavor { @Override @@ -60,7 +55,7 @@ public class SqlServer implements Flavor { } @Override - public String typeDate() { + public String typeLocalDateTime() { return "datetime2(3)"; } @@ -153,18 +148,6 @@ public class SqlServer implements Flavor { return ""; } - @Override - public String dateAsSqlFunction(Timestamp date, Calendar calendar) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); - dateFormat.setCalendar(calendar); - return "cast('" + dateFormat.format(date) + "' as datetime2(3))"; - } - - @Override - public String localDateAsSqlFunction(Date date) { - return "'" + date.toString() + "'"; - } - @Override public String sequenceOptions() { return ""; diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/util/DebugSql.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/util/DebugSql.java index 9876763..1b3daf8 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/util/DebugSql.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/util/DebugSql.java @@ -6,7 +6,8 @@ import org.xbib.jdbc.query.SqlNull; import java.io.InputStream; import java.io.Reader; -import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; @@ -77,9 +78,15 @@ public class DebugSql { } else if (argToPrint instanceof SqlNull || argToPrint == null) { buf.append("null"); } else if (argToPrint instanceof java.sql.Timestamp) { - buf.append(options.flavor().dateAsSqlFunction((Timestamp) argToPrint, options.calendarForTimestamps())); + java.sql.Timestamp timestamp = (java.sql.Timestamp) argToPrint; + LocalDateTime localDateTime = timestamp.toLocalDateTime(); + buf.append(localDateTime.toString()); + //buf.append(options.flavor().dateAsSqlFunction((Timestamp) argToPrint, options.calendarForTimestamps())); } else if (argToPrint instanceof java.sql.Date) { - buf.append(options.flavor().localDateAsSqlFunction((java.sql.Date) argToPrint)); + java.sql.Date date = (java.sql.Date) argToPrint; + LocalDate localDate = date.toLocalDate(); + buf.append(localDate.toString()); + //buf.append(options.flavor().localDateAsSqlFunction((java.sql.Date) argToPrint)); } else if (argToPrint instanceof Number) { buf.append(argToPrint); } else if (argToPrint instanceof Boolean) { diff --git a/jdbc-query/src/main/resources/META-INF/services/org.xbib.jdbc.query.Flavor b/jdbc-query/src/main/resources/META-INF/services/org.xbib.jdbc.query.Flavor index 84e6cb3..f7efe8d 100644 --- a/jdbc-query/src/main/resources/META-INF/services/org.xbib.jdbc.query.Flavor +++ b/jdbc-query/src/main/resources/META-INF/services/org.xbib.jdbc.query.Flavor @@ -1,6 +1,8 @@ org.xbib.jdbc.query.flavor.BigQuery org.xbib.jdbc.query.flavor.Derby org.xbib.jdbc.query.flavor.Hsql +org.xbib.jdbc.query.flavor.H2 +org.xbib.jdbc.query.flavor.MySQL org.xbib.jdbc.query.flavor.Oracle org.xbib.jdbc.query.flavor.Postgresql org.xbib.jdbc.query.flavor.SqlServer \ No newline at end of file diff --git a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/CommonTest.java b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/CommonTest.java index ba51d4a..87bbdda 100644 --- a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/CommonTest.java +++ b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/CommonTest.java @@ -25,7 +25,6 @@ import java.io.Reader; import java.io.StringReader; import java.math.BigDecimal; import java.sql.ResultSetMetaData; -import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; @@ -36,9 +35,9 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; import java.util.List; import java.util.TimeZone; +import java.util.logging.Level; import java.util.logging.Logger; import static org.hamcrest.MatcherAssert.assertThat; @@ -64,22 +63,15 @@ public abstract class CommonTest { protected Database db; - protected LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + protected LocalDateTime now; - protected LocalDate localDateNow = LocalDate.now(); + protected LocalDate localDateNow; @BeforeEach public void setupJdbc() throws Exception { + now = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + localDateNow = LocalDate.now(); dbp = createDatabaseProvider(new OptionsOverride() { - @Override - public LocalDateTime currentDate() { - return now; - } - - @Override - public Calendar calendarForTimestamps() { - return Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles")); - } }); db = dbp.get(); db.dropTableQuietly(TEST_TABLE_NAME); @@ -153,13 +145,13 @@ public abstract class CommonTest { .addColumn("str_fixed").asStringFixed(1).table() .addColumn("str_lob").asClob().table() .addColumn("bin_blob").asBlob().table() - .addColumn("date_millis").asDate().table() + .addColumn("date_millis").asLocalDateTime().table() .addColumn("local_date").asLocalDate().table().schema().execute(db); BigDecimal bigDecimal = new BigDecimal("5.3"); db.toInsert("insert into dbtest values (?,?,?,?,?,?,?,?,?,?,?)").argInteger(1).argLong(2L).argFloat(3.2f).argDouble(4.2) .argBigDecimal(bigDecimal).argString("Hello").argString("T").argClobString("World") - .argBlobBytes("More".getBytes()).argDate(now).argLocalDate(localDateNow).insert(1); + .argBlobBytes("More".getBytes()).argLocalDateTime(now).argLocalDate(localDateNow).insert(1); db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, " + "bin_blob, date_millis, local_date from dbtest") @@ -286,14 +278,14 @@ public abstract class CommonTest { .argBigDecimal("bd", bigDecimal) .argString("s", "Hello") .argString("sf", "T") - .argDate("date", now) + .argLocalDateTime("date", now) .argLocalDate("local_date", localDateNow) .queryLongOrNull()); List result = db.toSelect("select count(*) from dbtest where nbr_integer=:i and nbr_long=:l and " + "abs(nbr_float-:f)<0.01 and abs(nbr_double-:d)<0.01 and nbr_big_decimal=:bd and str_varchar=:s " + "and str_fixed=:sf and date_millis=:date and local_date=:local_date").argInteger("i", 1).argLong("l", 2L).argFloat("f", 3.2f) .argDouble("d", 4.2).argBigDecimal("bd", bigDecimal).argString("s", "Hello").argString("sf", "T") - .argDate("date", now).argLocalDate("local_date", localDateNow).queryLongs(); + .argLocalDateTime("date", now).argLocalDate("local_date", localDateNow).queryLongs(); assertEquals(1, result.size()); assertEquals(Long.valueOf(1), result.get(0)); } @@ -312,7 +304,7 @@ public abstract class CommonTest { .addColumn("str_fixed").asStringFixed(1).table() .addColumn("str_lob").asClob().table() .addColumn("bin_blob").asBlob().table() - .addColumn("date_millis").asDate().table() + .addColumn("date_millis").asLocalDateTime().table() .addColumn("local_date").asLocalDate().table().schema().execute(db); BigDecimal bigDecimal = new BigDecimal("5.3"); @@ -327,12 +319,12 @@ public abstract class CommonTest { .argString("T") .argClobString("World") .argBlobBytes("More".getBytes()) - .argDate(now) + .argLocalDateTime(now) .argLocalDate(localDateNow).insert()); db.toUpdate("update dbtest set nbr_integer=?, nbr_long=?, nbr_float=?, nbr_double=?, nbr_big_decimal=?, " + "str_varchar=?, str_fixed=?, str_lob=?, bin_blob=?, date_millis=?, local_date=?").argInteger(null).argLong(null) .argFloat(null).argDouble(null).argBigDecimal(null).argString(null).argString(null).argClobString(null) - .argBlobBytes(null).argDate(null).argLocalDate(null).update(1); + .argBlobBytes(null).argLocalDateTime(null).argLocalDate(null).update(1); db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, " + "bin_blob, date_millis, local_date from dbtest").query((RowsHandler) rs -> { assertTrue(rs.next()); @@ -363,7 +355,7 @@ public abstract class CommonTest { assertEquals(1, db.toUpdate("update dbtest set nbr_integer=?, nbr_long=?, nbr_float=?, nbr_double=?, " + "nbr_big_decimal=?, str_varchar=?, str_fixed=?, str_lob=?, bin_blob=?, date_millis=?, local_date=?").argInteger(1) .argLong(2L).argFloat(3.2f).argDouble(4.2).argBigDecimal(bigDecimal).argString("Hello").argString("T") - .argClobString("World").argBlobBytes("More".getBytes()).argDate(now).argLocalDate(localDateNow).update()); + .argClobString("World").argBlobBytes("More".getBytes()).argLocalDateTime(now).argLocalDate(localDateNow).update()); db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, " + "bin_blob, date_millis, local_date from dbtest").query((RowsHandler) rs -> { assertTrue(rs.next()); @@ -426,20 +418,20 @@ public abstract class CommonTest { .addColumn("str_fixed").asStringFixed(1).table() .addColumn("str_lob").asClob().table() .addColumn("bin_blob").asBlob().table() - .addColumn("date_millis").asDate().table() + .addColumn("date_millis").asLocalDateTime().table() .addColumn("local_date").asLocalDate().table().schema().execute(db); BigDecimal bigDecimal = new BigDecimal("5.3"); db.toInsert("insert into dbtest values (:pk,:a,:b,:c,:d,:e,:f,:sf,:g,:h,:i,:j)").argLong(":pk", 1L).argInteger(":a", 1) .argLong(":b", 2L).argFloat(":c", 3.2f).argDouble(":d", 4.2).argBigDecimal(":e", bigDecimal) .argString(":f", "Hello").argString(":sf", "T") .argClobString(":g", "World").argBlobBytes(":h", "More".getBytes()) - .argDate(":i", now).argLocalDate(":j", localDateNow).insert(1); + .argLocalDateTime(":i", now).argLocalDate(":j", localDateNow).insert(1); db.toUpdate("update dbtest set nbr_integer=:a, nbr_long=:b, nbr_float=:c, nbr_double=:d, nbr_big_decimal=:e, " + "str_varchar=:f, str_fixed=:sf, str_lob=:g, bin_blob=:h, date_millis=:i, local_date=:j").argInteger(":a", null) .argLong(":b", null).argFloat(":c", null).argDouble(":d", null).argBigDecimal(":e", null) .argString(":f", null).argString(":sf", null) .argClobString(":g", null).argBlobBytes(":h", null) - .argDate(":i", null).argLocalDate(":j", null).update(1); + .argLocalDateTime(":i", null).argLocalDate(":j", null).update(1); db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, " + "bin_blob, date_millis, local_date from dbtest").query((RowsHandler) rs -> { assertTrue(rs.next()); @@ -472,7 +464,7 @@ public abstract class CommonTest { .argLong(":b", 2L).argFloat(":c", 3.2f).argDouble(":d", 4.2).argBigDecimal(":e", bigDecimal) .argString(":f", "Hello").argString(":sf", "T") .argClobString(":g", "World").argBlobBytes(":h", "More".getBytes()) - .argDate(":i", now).argLocalDate(":j", localDateNow).update(1); + .argLocalDateTime(":i", now).argLocalDate(":j", localDateNow).update(1); db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, " + "bin_blob, date_millis, local_date from dbtest") .query((RowsHandler) rs -> { @@ -537,11 +529,11 @@ public abstract class CommonTest { .addColumn("str_fixed").asStringFixed(1).table() .addColumn("str_lob").asClob().table() .addColumn("bin_blob").asBlob().table() - .addColumn("date_millis").asDate().table() + .addColumn("date_millis").asLocalDateTime().table() .addColumn("local_date").asLocalDate().table().schema().execute(db); db.toInsert("insert into dbtest values (?,?,?,?,?,?,?,?,?,?,?,?)").argLong(1L).argInteger(null).argLong(null) .argFloat(null).argDouble(null).argBigDecimal(null).argString(null).argString(null).argClobString(null) - .argBlobBytes(null).argDate(null).argLocalDate(null).insert(1); + .argBlobBytes(null).argLocalDateTime(null).argLocalDate(null).insert(1); db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, " + "bin_blob, date_millis, local_date from dbtest") .query((RowsHandler) rs -> { @@ -604,7 +596,7 @@ public abstract class CommonTest { String dateColumnName = "local_date"; new Schema() .addTable("dbtest") - .addColumn(timestampColumnName).asDate().table() + .addColumn(timestampColumnName).asLocalDateTime().table() .addColumn(dateColumnName).asLocalDate().table().schema().execute(db); db.toSelect("select * from dbtest").query((RowsHandler) rs -> { ResultSetMetaData metadata = rs.getMetadata(); @@ -612,8 +604,10 @@ public abstract class CommonTest { String columnName = metadata.getColumnName(i); String columnType = metadata.getColumnTypeName(i); if (columnName.equalsIgnoreCase(timestampColumnName)) { - if ("sqlserver".equals(db.flavor().toString())) { + if ("sqlserver".equals(db.flavor().getName())) { assertEquals("DATETIME2", columnType.toUpperCase()); + } else if ("hsqldb".equals(db.flavor().getName())) { + assertEquals("TIMESTAMP WITH TIME ZONE", columnType.toUpperCase()); } else { assertEquals("TIMESTAMP", columnType.toUpperCase()); } @@ -629,11 +623,11 @@ public abstract class CommonTest { @Test public void intervals() { - new Schema().addTable("dbtest").addColumn("d").asDate().schema().execute(db); - db.toInsert("insert into dbtest (d) values (?)").argDate(now).insert(1); + new Schema().addTable("dbtest").addColumn("d").asLocalDateTime().schema().execute(db); + db.toInsert("insert into dbtest (d) values (?)").argLocalDateTime(now).insert(1); assertEquals(1, db.toSelect("select count(1) from dbtest where d - interval '1' hour * ? < ?") .argInteger(2) - .argDate(now) + .argLocalDateTime(now) .queryIntegerOrZero()); } @@ -650,7 +644,7 @@ public abstract class CommonTest { .addColumn("str_lob").asClob().table() .addColumn("bin_blob").asBlob().table() .addColumn("boolean_flag").asBoolean().table() - .addColumn("date_millis").asDate().table() + .addColumn("date_millis").asLocalDateTime().table() .addColumn("local_date").asLocalDate().schema().execute(db); db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar," + " str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date) values (?,?,?,?,?,?,?,?,?,?,?,?)") @@ -658,14 +652,16 @@ public abstract class CommonTest { .argDouble(Double.MAX_VALUE).argBigDecimal(new BigDecimal("123.456")) .argString("hello").argString("Z").argClobString("hello again") .argBlobBytes(new byte[]{'1', '2'}).argBoolean(true) - .argDateNowPerApp().argLocalDate(localDateNow).insert(1); + .argLocalDateTime(now) + .argLocalDate(localDateNow).insert(1); db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar," + " str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date) values (?,?,?,?,?,?,?,?,?,?,?,?)") .argInteger(Integer.MIN_VALUE).argLong(Long.MIN_VALUE).argFloat(0.000001f) .argDouble(Double.MIN_VALUE).argBigDecimal(new BigDecimal("-123.456")) .argString("goodbye").argString("A").argClobString("bye again") .argBlobBytes(new byte[]{'3', '4'}).argBoolean(false) - .argDateNowPerApp().argLocalDate(localDateNow).insert(1); + .argLocalDateTime(now) + .argLocalDate(localDateNow).insert(1); String expectedSchema = new Schema().addTable("dbtest2") .addColumn("nbr_integer").asInteger().table() .addColumn("nbr_long").asLong().table() @@ -677,7 +673,7 @@ public abstract class CommonTest { .addColumn("str_lob").asClob().table() .addColumn("bin_blob").asBlob().table() .addColumn("boolean_flag").asBoolean().table() - .addColumn("date_millis").asDate().table() + .addColumn("date_millis").asLocalDateTime().table() .addColumn("local_date").asLocalDate().schema().print(db.flavor()); List args = db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal," + " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest") @@ -719,7 +715,7 @@ public abstract class CommonTest { .argClobString("str_lob", "bye again") .argBlobBytes("bin_blob", new byte[]{'3', '4'}) .argString("boolean_flag", "N")//.argBoolean("boolean_flag", false) - .argDate("date_millis", now) + .argLocalDateTime("date_millis", now) .argLocalDate("local_date", localDateNow), new SqlArgs().argInteger("nbr_integer", Integer.MAX_VALUE) .argLong("nbr_long", Long.MAX_VALUE) @@ -731,7 +727,7 @@ public abstract class CommonTest { .argClobString("str_lob", "hello again") .argBlobBytes("bin_blob", new byte[]{'1', '2'}) .argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true) - .argDate("date_millis", now) + .argLocalDateTime("date_millis", now) .argLocalDate("local_date", localDateNow)), db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal," + " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest2 order by 1") @@ -1368,25 +1364,26 @@ public abstract class CommonTest { @Test public void insertReturningAppDate() { db.dropSequenceQuietly("dbtest_seq"); - new Schema() .addTable("dbtest") .addColumn("pk").primaryKey().table() - .addColumn("d").asDate().table().schema() + .addColumn("d") + .asLocalDateTime() + .table() + .schema() .addSequence("dbtest_seq").schema() .execute(db); - db.toInsert("insert into dbtest (pk, d) values (:seq, :d)") .argPkSeq(":seq", "dbtest_seq") - .argDateNowPerApp(":d") + .argLocalDateTime(":d", now) .insertReturning("dbtest", "pk", rs -> { assertTrue(rs.next()); assertEquals(Long.valueOf(1L), rs.getLongOrNull(1)); - assertThat(rs.getLocalDateTimeOrNull(2), equalTo(now)); + assertThat(rs.getLocalDateTimeOrNull(2, ZoneId.systemDefault()), equalTo(now)); assertFalse(rs.next()); return null; }, "d"); - assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d=?").argDate(now).queryLongOrNull()); + assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d=?").argLocalDateTime(now).queryLongOrNull()); } @Test @@ -1394,18 +1391,20 @@ public abstract class CommonTest { new Schema() .addTable("dbtest") .addColumn("pk").primaryKey().table() - .addColumn("d").asDate().table() - .addColumn("d2").asDate().table() + .addColumn("d").asLocalDateTime().table() + .addColumn("d2").asLocalDateTime().table() .addColumn("d3").asLocalDate().table() .addColumn("d4").asLocalDate().table() .addColumn("s").asString(5).table() .addColumn("s2").asString(5).table() .addColumn("i").asInteger().table().schema() .execute(db); - db.toInsert("insert into dbtest (pk, d, d3, s) values (?,?,?,?)") - .argLong(1L).argDateNowPerApp().argLocalDate(localDateNow).argString("foo").insert(1); - + .argLong(1L) + .argLocalDateTime(now) + .argLocalDate(localDateNow) + .argString("foo") + .insert(1); assertEquals(Long.valueOf(1L), db.toSelect("select pk from dbtest").queryLongOrNull()); assertNull(db.toSelect("select pk from dbtest where 1=0").queryLongOrNull()); assertNull(db.toSelect("select i from dbtest").queryLongOrNull()); @@ -1415,7 +1414,6 @@ public abstract class CommonTest { assertEquals(1L, (long) db.toSelect("select pk from dbtest").queryLongs().get(0)); assertTrue(db.toSelect("select pk from dbtest where 1=0").queryLongs().isEmpty()); assertTrue(db.toSelect("select i from dbtest").queryLongs().isEmpty()); - assertEquals(Integer.valueOf(1), db.toSelect("select pk from dbtest").queryIntegerOrNull()); assertNull(db.toSelect("select pk from dbtest where 1=0").queryIntegerOrNull()); assertNull(db.toSelect("select i from dbtest").queryIntegerOrNull()); @@ -1425,7 +1423,6 @@ public abstract class CommonTest { assertEquals(1L, (int) db.toSelect("select pk from dbtest").queryIntegers().get(0)); assertTrue(db.toSelect("select pk from dbtest where 1=0").queryIntegers().isEmpty()); assertTrue(db.toSelect("select i from dbtest").queryIntegers().isEmpty()); - assertEquals("foo", db.toSelect("select s from dbtest").queryStringOrNull()); assertNull(db.toSelect("select s from dbtest where 1=0").queryStringOrNull()); assertNull(db.toSelect("select s2 from dbtest").queryStringOrNull()); @@ -1435,13 +1432,12 @@ public abstract class CommonTest { assertEquals("foo", db.toSelect("select s from dbtest").queryStrings().get(0)); assertTrue(db.toSelect("select s from dbtest where 1=0").queryStrings().isEmpty()); assertTrue(db.toSelect("select s2 from dbtest").queryStrings().isEmpty()); - - assertEquals(now, db.toSelect("select d from dbtest").queryDateOrNull()); - assertNull(db.toSelect("select d from dbtest where 1=0").queryDateOrNull()); - assertNull(db.toSelect("select d2 from dbtest").queryDateOrNull()); - assertEquals(db.toSelect("select d from dbtest").queryDates().get(0), now); - assertTrue(db.toSelect("select d from dbtest where 1=0").queryDates().isEmpty()); - assertTrue(db.toSelect("select d2 from dbtest").queryDates().isEmpty()); + assertEquals(now, db.toSelect("select d from dbtest").queryLocalDateTimeOrNull()); + assertNull(db.toSelect("select d from dbtest where 1=0").queryLocalDateTimeOrNull()); + assertNull(db.toSelect("select d2 from dbtest").queryLocalDateTimeOrNull()); + assertEquals(db.toSelect("select d from dbtest").queryLocalDateTimes().get(0), now); + assertTrue(db.toSelect("select d from dbtest where 1=0").queryLocalDateTimes().isEmpty()); + assertTrue(db.toSelect("select d2 from dbtest").queryLocalDateTimes().isEmpty()); assertEquals(localDateNow, db.toSelect("select d3 from dbtest").queryLocalDateOrNull()); assertNull(db.toSelect("select d3 from dbtest where 1=0").queryLocalDateOrNull()); @@ -1511,12 +1507,12 @@ public abstract class CommonTest { new Schema() .addTable("dbtest") .addColumn("pk").primaryKey().table() - .addColumn("d").asDate().table().schema() + .addColumn("d").asLocalDateTime().table().schema() .addSequence("dbtest_seq").schema() .execute(db); LocalDateTime dbNow = db.toInsert("insert into dbtest (pk, d) values (:seq, :d)") .argPkSeq(":seq", "dbtest_seq") - .argDateNowPerDb(":d") + .argLocalDateTimeNowPerDb(":d") .insertReturning("dbtest", "pk", rs -> { assertTrue(rs.next()); assertEquals(Long.valueOf(1L), rs.getLongOrNull(1)); @@ -1524,7 +1520,8 @@ public abstract class CommonTest { assertFalse(rs.next()); return dbDate; }, "d"); - assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d=?").argDate(dbNow).queryLongOrNull()); + assertEquals(Long.valueOf(1L), + db.toSelect("select count(*) from dbtest where d = ?").argLocalDateTime(dbNow).queryLongOrNull()); } @Test @@ -1581,12 +1578,12 @@ public abstract class CommonTest { for (int attempts = 1; attempts <= 10; attempts++) { new Schema() .addTable("dbtest") - .addColumn("d").asDate().table().schema() + .addColumn("d").asLocalDateTime().table().schema() .execute(db); db.toInsert("insert into dbtest (d) values (?)") - .argDateNowPerDb() + .argLocalDateTimeNowPerDb() .insert(1); - LocalDateTime dbNow = db.toSelect("select d from dbtest").queryDateOrNull(); + LocalDateTime dbNow = db.toSelect("select d from dbtest").queryLocalDateTimeOrNull(); if (dbNow != null && dbNow.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() % 10 != 0) { return; } @@ -1600,19 +1597,17 @@ public abstract class CommonTest { public void dateRoundTrip() { new Schema() .addTable("dbtest") - .addColumn("d1").asDate().table() - .addColumn("d2").asDate().table().schema() + .addColumn("d1").asLocalDateTime().table() + .addColumn("d2").asLocalDateTime().table() + .schema() .execute(db); - // Store current time as per the database db.toInsert("insert into dbtest (d1) values (?)") - .argDateNowPerDb() + .argLocalDateTimeNowPerDb() .insert(1); - // Now pull it out, put it back in, and verify it matches in the database - LocalDateTime dbNow = db.toSelect("select d1 from dbtest").queryDateOrNull(); - db.toUpdate("update dbtest set d2=?") - .argDate(dbNow) + LocalDateTime dbNow = db.toSelect("select d1 from dbtest").queryLocalDateTimeOrNull(); + db.toUpdate("update dbtest set d2 = ?") + .argLocalDateTime(dbNow) .update(1); - assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d1=d2").queryLongOrNull()); } @@ -1620,66 +1615,75 @@ public abstract class CommonTest { public void dateRoundTripTimezones() { new Schema() .addTable("dbtest") - .addColumn("d").asDate().table().schema() + .addColumn("d") + .asLocalDateTime() + .table() + .schema() .execute(db); Instant instant = Instant.ofEpochMilli(166656789L); - LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4))); - LocalDateTime datePlus = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4))); - TimeZone defaultTZ = TimeZone.getDefault(); - try { - TimeZone.setDefault(TimeZone.getTimeZone("GMT-4:00")); - db.toInsert("insert into dbtest (d) values (?)").argDate(dateMinus).insert(1); - LocalDateTime localDateTimeMinus = db.toSelect("select d from dbtest").queryDateOrNull(); - assertEquals(dateMinus, localDateTimeMinus); - assertEquals("1970-01-02 18:17:36.789", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimeMinus)); - db.toDelete("delete from dbtest where d=?").argDate(dateMinus).update(1); - TimeZone.setDefault(TimeZone.getTimeZone("GMT+4:00")); - db.toInsert("insert into dbtest (d) values (?)").argDate(datePlus).insert(1); - LocalDateTime localDateTimePlus = db.toSelect("select d from dbtest").queryDateOrNull(); - assertEquals(datePlus, localDateTimePlus); - assertEquals("1970-01-03 02:17:36.789", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimePlus)); - db.toDelete("delete from dbtest where d=?").argDate(datePlus).update(1); - } finally { - TimeZone.setDefault(defaultTZ); - } + ZoneId dateMinusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4)); + LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, dateMinusZone); + ZoneId datePlusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4)); + LocalDateTime datePlus = LocalDateTime.ofInstant(instant,datePlusZone); + logger.log(Level.INFO, "dateMinus = " + dateMinus); + db.toInsert("insert into dbtest (d) values (?)") + .argLocalDateTime(dateMinus) + .insert(1); + LocalDateTime localDateTimeMinus = db.toSelect("select d from dbtest") + .queryLocalDateTimeOrNull(); + assertEquals(dateMinus, localDateTimeMinus); + assertEquals("1970-01-02 18:17:36.789", + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimeMinus)); + db.toDelete("delete from dbtest where d = ?").argLocalDateTime(dateMinus).update(1); + db.toInsert("insert into dbtest (d) values (?)") + .argLocalDateTime(datePlus) + .insert(1); + LocalDateTime localDateTimePlus = db.toSelect("select d from dbtest") + .queryLocalDateTimeOrNull(datePlusZone); + assertEquals(datePlus, localDateTimePlus); + assertEquals("1970-01-03 02:17:36.789", + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimePlus)); + db.toDelete("delete from dbtest where d = ?").argLocalDateTime(datePlus).update(1); } /** - * Verify the appropriate database flavor can correctly convert a {@code Date} + * Verify the appropriate database flavor can correctly convert a {@code LocalDateTime} * into a SQL function representing a conversion from string to timestamp. This * function is used to write debug SQL to the log in a way that could be manually * executed if desired. */ @Test - public void stringDateFunctions() { + public void stringLocalDateTimeFunctions() { Instant instant = Instant.ofEpochMilli(166656789L); - LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4))); - LocalDateTime datePlus = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4))); - logger.info("LocalDateTime: " + dateMinus + " " + datePlus); - TimeZone defaultTZ = TimeZone.getDefault(); - try { - TimeZone.setDefault(TimeZone.getTimeZone("GMT-4:00")); - new Schema().addTable("dbtest").addColumn("d").asDate().schema().execute(db); - db.toInsert("insert into dbtest (d) values (" - + db.flavor().dateAsSqlFunction(Timestamp.valueOf(dateMinus), db.options().calendarForTimestamps()).replace(":", "::") + ")") - .insert(1); - assertEquals("1970-01-02 18:17:36.789", - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(db.toSelect("select d from dbtest").queryDateOrNull())); - // Now do some client operations in a different time zone - TimeZone.setDefault(TimeZone.getTimeZone("GMT+4:00")); - // Verify regular arg maps date the same way even though our TimeZone is now different - db.toDelete("delete from dbtest where d=?").argDate(datePlus).update(1); - db.toInsert("insert into dbtest (d) values (" - + db.flavor().dateAsSqlFunction(Timestamp.valueOf(datePlus), db.options().calendarForTimestamps()).replace(":", "::") + ")") - .insert(1); - assertEquals("1970-01-03 02:17:36.789", - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(db.toSelect("select d from dbtest").queryDateOrNull())); - // Verify the function maps correctly for equals operations as well - db.toDelete("delete from dbtest where d=" + db.flavor().dateAsSqlFunction(Timestamp.valueOf(datePlus), - db.options().calendarForTimestamps()).replace(":", "::")).update(1); - } finally { - TimeZone.setDefault(defaultTZ); - } + ZoneId dateMinusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4)); + LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, dateMinusZone); + ZoneId datePlusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4)); + LocalDateTime datePlus = LocalDateTime.ofInstant(instant, datePlusZone); + logger.info("LocalDateTime: dateMinus=" + dateMinus + " datePlus=" + datePlus); + new Schema().addTable("dbtest").addColumn("d") + .asLocalDateTime() + .schema() + .execute(db); + db.toInsert("insert into dbtest (d) values (?)") + .argLocalDateTime(dateMinus) + .insert(1); + LocalDateTime localDateTime = db.toSelect("select d from dbtest") + .queryLocalDateTimeOrNull(); + assertEquals("1970-01-02 18:17:36.789", + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTime.atZone(dateMinusZone))); + db.toDelete("delete from dbtest where d = ?") + .argLocalDateTime(dateMinus) + .update(1); + db.toInsert("insert into dbtest (d) values (?)") + .argLocalDateTime(datePlus) + .insert(1); + localDateTime = db.toSelect("select d from dbtest") + .queryLocalDateTimeOrNull(datePlusZone); + assertEquals("1970-01-03 02:17:36.789", + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTime.atZone(datePlusZone))); + db.toDelete("delete from dbtest where d = ?") + .argLocalDateTime(datePlus) + .update(1); } @Test @@ -1687,12 +1691,12 @@ public abstract class CommonTest { new Schema() .addTable("dbtest") .addColumn("pk").primaryKey().table() - .addColumn("d").asDate().table() + .addColumn("d").asLocalDateTime().table() .addColumn("a").asInteger().table().schema() .execute(db); db.toSelect("select pk as \"time:: now??\" from dbtest where a=? and d=:now") - .argInteger(1).argDateNowPerDb("now").query(rs -> { + .argInteger(1).argLocalDateTimeNowPerDb("now").query(rs -> { assertFalse(rs.next()); return null; }); diff --git a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/HsqldbTest.java b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/HsqldbTest.java index e52a96a..39eae75 100644 --- a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/HsqldbTest.java +++ b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/HsqldbTest.java @@ -57,12 +57,6 @@ public class HsqldbTest extends CommonTest { }); } - @Disabled("HSQLDB uses always static GMT timezone") - @Test - public void clockSync() { - db.assertTimeSynchronized(); - } - @Disabled("LocalDate implementations should be TimeZone agnostic, but HSQLDB implementation has a bug.") @Test public void argLocalDateTimeZones() { @@ -89,7 +83,7 @@ public class HsqldbTest extends CommonTest { .addColumn("str_lob").asClob().table() .addColumn("bin_blob").asBlob().table() .addColumn("boolean_flag").asBoolean().table() - .addColumn("date_millis").asDate().table() + .addColumn("date_millis").asLocalDateTime().table() .addColumn("local_date").asLocalDate().schema().execute(db); db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar," @@ -104,7 +98,7 @@ public class HsqldbTest extends CommonTest { .argClobString("hello again") .argBlobBytes(new byte[]{'1', '2'}) .argBoolean(true) - .argDateNowPerApp() + .argLocalDateTime(now) .argLocalDate(localDateNow) .insert(1); @@ -120,7 +114,7 @@ public class HsqldbTest extends CommonTest { .argClobString("bye again") .argBlobBytes(new byte[]{'3', '4'}) .argBoolean(false) - .argDateNowPerApp() + .argLocalDateTime(now) .argLocalDate(localDateNow) .insert(1); @@ -135,7 +129,7 @@ public class HsqldbTest extends CommonTest { .addColumn("str_lob").asClob().table() .addColumn("bin_blob").asBlob().table() .addColumn("boolean_flag").asBoolean().table() - .addColumn("date_millis").asDate().table() + .addColumn("date_millis").asLocalDateTime().table() .addColumn("local_date").asLocalDate().schema().print(db.flavor()); List args = db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal," @@ -175,7 +169,7 @@ public class HsqldbTest extends CommonTest { .argClobString("str_lob", "bye again") .argBlobBytes("bin_blob", new byte[]{'3', '4'}) .argString("boolean_flag", "N")//.argBoolean("boolean_flag", false) - .argDate("date_millis", now) + .argLocalDateTime("date_millis", now) .argLocalDate("local_date", localDateNow), new SqlArgs() .argInteger("nbr_integer", Integer.MAX_VALUE) @@ -188,7 +182,7 @@ public class HsqldbTest extends CommonTest { .argClobString("str_lob", "hello again") .argBlobBytes("bin_blob", new byte[]{'1', '2'}) .argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true) - .argDate("date_millis", now) + .argLocalDateTime("date_millis", now) .argLocalDate("local_date", localDateNow)), db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal," + " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest2 order by 1") diff --git a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/RowStub.java b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/RowStub.java index 4c0b6c5..230083e 100644 --- a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/RowStub.java +++ b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/RowStub.java @@ -12,6 +12,7 @@ import java.sql.ResultSetMetaData; import java.sql.Types; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; @@ -628,21 +629,32 @@ public class RowStub { @Override public LocalDateTime getLocalDateTimeOrNull() { - return toDate(rows.get(row)[++col]); + return toLocalDateTime(rows.get(row)[++col]); } @Override public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) { col = columnOneBased; - return toDate(rows.get(row)[columnOneBased - 1]); + return toLocalDateTime(rows.get(row)[columnOneBased - 1]); + } + + @Override + public LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId) { + col = columnOneBased; + return toLocalDateTime(rows.get(row)[columnOneBased - 1], zoneId); } @Override public LocalDateTime getLocalDateTimeOrNull(String columnName) { col = columnIndexByName(columnName) + 1; - return toDate(rows.get(row)[columnIndexByName(columnName)]); + return toLocalDateTime(rows.get(row)[columnIndexByName(columnName)]); + } + + @Override + public LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId) { + return null; } /** @@ -758,14 +770,20 @@ public class RowStub { return (String) o; } - private LocalDateTime toDate(Object o) { + private LocalDateTime toLocalDateTime(Object o) { + return toLocalDateTime(o, ZoneId.systemDefault()); + } + + private LocalDateTime toLocalDateTime(Object o, ZoneId zoneId) { if (o instanceof String) { String s = (String) o; if (s.length() == "yyyy-MM-dd".length()) { - return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd")) + .atZone(zoneId).toLocalDateTime(); } if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) { - return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss")); + return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss")) + .atZone(zoneId).toLocalDateTime(); } throw new DatabaseException("Didn't understand date string: " + s); } diff --git a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/example/InsertReturning.java b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/example/InsertReturning.java index 7d174be..67303c3 100644 --- a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/example/InsertReturning.java +++ b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/example/InsertReturning.java @@ -21,7 +21,7 @@ public class InsertReturning extends DerbyExample { new Schema() .addTable("t") .addColumn("pk").primaryKey().table() - .addColumn("d").asDate().table() + .addColumn("d").asLocalDateTime().table() .addColumn("s").asString(80).schema() .addSequence("pk_seq").schema().execute(db); @@ -33,7 +33,7 @@ public class InsertReturning extends DerbyExample { Long pk = db.toInsert( "insert into t (pk,d,s) values (?,?,?)") .argPkSeq("pk_seq") - .argDateNowPerDb() + .argLocalDateTimeNowPerDb() .argString("Hi") .insertReturningPkSeq("pk"); diff --git a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/example/SampleDao.java b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/example/SampleDao.java index 750b026..1da1f76 100644 --- a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/example/SampleDao.java +++ b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/example/SampleDao.java @@ -3,7 +3,6 @@ package org.xbib.jdbc.query.test.example; import org.xbib.jdbc.query.Database; import java.time.LocalDateTime; -import java.util.Date; import java.util.function.Supplier; /** @@ -19,20 +18,19 @@ public class SampleDao { public void createSample(final Sample sample, Long userIdMakingChange) { Database db = dbp.get(); - - LocalDateTime updateTime = db.nowPerApp(); + LocalDateTime updateTime = LocalDateTime.now(); Long sampleId = db.toInsert( "insert into sample (sample_id, sample_name, update_sequence, update_time) values (?,?,0,?)") .argPkSeq("id_seq") .argString(sample.getName()) - .argDate(updateTime) + .argLocalDateTime(updateTime) .insertReturningPkSeq("sample_id"); db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id," + " is_deleted) values (?,?,0,?,?,'N')") .argLong(sampleId) .argString(sample.getName()) - .argDate(updateTime) + .argLocalDateTime(updateTime) .argLong(userIdMakingChange) .insert(1); @@ -61,20 +59,20 @@ public class SampleDao { // Insert the history row first, so it will fail (non-unique sample_id + update_sequence) // if someone else modified the row. This is an optimistic locking strategy. int newUpdateSequence = sample.getUpdateSequence() + 1; - LocalDateTime newUpdateTime = db.nowPerApp(); + LocalDateTime newUpdateTime = LocalDateTime.now(); db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id," + " is_deleted) values (?,?,?,?,?,'N')") .argLong(sample.getSampleId()) .argString(sample.getName()) .argInteger(newUpdateSequence) - .argDate(newUpdateTime) + .argLocalDateTime(newUpdateTime) .argLong(userIdMakingChange) .insert(1); db.toUpdate("update sample set sample_name=?, update_sequence=?, update_time=? where sample_id=?") .argString(sample.getName()) .argInteger(newUpdateSequence) - .argDate(newUpdateTime) + .argLocalDateTime(newUpdateTime) .argLong(sample.getSampleId()) .update(1); @@ -89,13 +87,13 @@ public class SampleDao { // Insert the history row first, so it will fail (non-unique sample_id + update_sequence) // if someone else modified the row. This is an optimistic locking strategy. int newUpdateSequence = sample.getUpdateSequence() + 1; - LocalDateTime newUpdateTime = db.nowPerApp(); + LocalDateTime newUpdateTime = LocalDateTime.now(); db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id," + " is_deleted) values (?,?,?,?,?,'Y')") .argLong(sample.getSampleId()) .argString(sample.getName()) .argInteger(newUpdateSequence) - .argDate(newUpdateTime) + .argLocalDateTime(newUpdateTime) .argLong(userIdMakingChange) .insert(1);