replace Date by LocalDateTime

This commit is contained in:
Jörg Prante 2022-02-15 12:02:20 +01:00
parent 436ae935b8
commit d35071b910
22 changed files with 181 additions and 298 deletions

View file

@ -1,7 +1,7 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.sql.Connection; import java.sql.Connection;
import java.util.Date; import java.time.LocalDateTime;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
@ -90,7 +90,7 @@ public interface Database extends Supplier<Database> {
/** /**
* Get the value that would be used if you specify an argNowPerApp() parameter. * Get the value that would be used if you specify an argNowPerApp() parameter.
*/ */
Date nowPerApp(); LocalDateTime nowPerApp();
/** /**
* Cause the underlying connection to commit its transaction immediately. This * Cause the underlying connection to commit its transaction immediately. This

View file

@ -5,8 +5,9 @@ import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
@ -90,7 +91,7 @@ public class DatabaseImpl implements Database {
} }
@Override @Override
public Date nowPerApp() { public LocalDateTime nowPerApp() {
return options.currentDate(); return options.currentDate();
} }
@ -249,25 +250,26 @@ public class DatabaseImpl implements Database {
public void assertTimeSynchronized(long millisToWarn, long millisToError) { public void assertTimeSynchronized(long millisToWarn, long millisToError) {
toSelect("select ?" + flavor().fromAny()) toSelect("select ?" + flavor().fromAny())
.argDateNowPerDb().queryFirstOrNull(r -> { .argDateNowPerDb().queryFirstOrNull(r -> {
Date appDate = nowPerApp(); LocalDateTime appDate = nowPerApp();
Date dbDate = r.getDateOrNull(); LocalDateTime dbDate = r.getLocalDateTimeOrNull();
if (dbDate == null) { if (dbDate == null) {
throw new DatabaseException("Expecting a date in the result"); throw new DatabaseException("Expecting a date in the result");
} }
if (Math.abs(appDate.getTime() - dbDate.getTime()) > 3600000) { Duration duration = Duration.between(appDate, dbDate).abs();
if (duration.getSeconds() > 3600) {
throw new DatabaseException("App and db time are over an hour apart (check your timezones) app: " throw new DatabaseException("App and db time are over an hour apart (check your timezones) app: "
+ DateTimeFormatter.ISO_INSTANT.format(appDate.toInstant()) + " db: " + DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
+ DateTimeFormatter.ISO_INSTANT.format(dbDate.toInstant())); + DateTimeFormatter.ISO_INSTANT.format(dbDate));
} }
if (Math.abs(appDate.getTime() - dbDate.getTime()) > millisToError) { if (duration.getSeconds() * 1000 > millisToError) {
throw new DatabaseException("App and db time over " + millisToError + " millis apart (check your clocks) app: " throw new DatabaseException("App and db time over " + millisToError + " millis apart (check your clocks) app: "
+ DateTimeFormatter.ISO_INSTANT.format(appDate.toInstant()) + " db: " + DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
+ DateTimeFormatter.ISO_INSTANT.format(dbDate.toInstant())); + DateTimeFormatter.ISO_INSTANT.format(dbDate));
} }
if (Math.abs(appDate.getTime() - dbDate.getTime()) > millisToWarn) { if (duration.getSeconds() * 1000 > millisToWarn) {
log.warning("App and db time are over " + millisToWarn + " millis apart (check your clocks) app: " log.warning("App and db time are over " + millisToWarn + " millis apart (check your clocks) app: "
+ DateTimeFormatter.ISO_INSTANT.format(appDate.toInstant()) + " db: " + DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
+ DateTimeFormatter.ISO_INSTANT.format(dbDate.toInstant())); + DateTimeFormatter.ISO_INSTANT.format(dbDate));
} }
return null; return null;
}); });

View file

@ -1,8 +1,9 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.sql.Date;
import java.sql.Timestamp;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
/** /**
* Enumeration of supported databases with various compatibility settings. * Enumeration of supported databases with various compatibility settings.
@ -131,8 +132,8 @@ public enum Flavor {
} }
@Override @Override
public String dateAsSqlFunction(Date date, Calendar calendar) { public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
dateFormat.setCalendar(calendar); dateFormat.setCalendar(calendar);
return "timestamp('" + dateFormat.format(date) + "')"; return "timestamp('" + dateFormat.format(date) + "')";
} }
@ -279,7 +280,7 @@ public enum Flavor {
} }
@Override @Override
public String dateAsSqlFunction(Date date, Calendar calendar) { public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
dateFormat.setCalendar(calendar); dateFormat.setCalendar(calendar);
return "cast('" + dateFormat.format(date) + "' as datetime2(3))"; return "cast('" + dateFormat.format(date) + "' as datetime2(3))";
@ -425,7 +426,7 @@ public enum Flavor {
} }
@Override @Override
public String dateAsSqlFunction(Date date, Calendar calendar) { public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
dateFormat.setCalendar(calendar); dateFormat.setCalendar(calendar);
return "timestamp '" + dateFormat.format(date) + "'"; return "timestamp '" + dateFormat.format(date) + "'";
@ -568,7 +569,7 @@ public enum Flavor {
} }
@Override @Override
public String dateAsSqlFunction(Date date, Calendar calendar) { public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
dateFormat.setCalendar(calendar); dateFormat.setCalendar(calendar);
return "'" + dateFormat.format(date) + " GMT'::timestamp"; return "'" + dateFormat.format(date) + " GMT'::timestamp";
@ -711,7 +712,7 @@ public enum Flavor {
} }
@Override @Override
public String dateAsSqlFunction(Date date, Calendar calendar) { public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000XXX"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000XXX");
dateFormat.setCalendar(calendar); dateFormat.setCalendar(calendar);
return "cast(timestamp '" + dateFormat.format(date) + "' as timestamp without time zone)"; return "cast(timestamp '" + dateFormat.format(date) + "' as timestamp without time zone)";
@ -855,7 +856,7 @@ public enum Flavor {
} }
@Override @Override
public String dateAsSqlFunction(Date date, Calendar calendar) { public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
// Construct a datetime literal // Construct a datetime literal
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
dateFormat.setCalendar(calendar); dateFormat.setCalendar(calendar);
@ -980,7 +981,7 @@ public enum Flavor {
* Return a SQL function representing the specified date. For example, in PostgreSQL this * Return a SQL function representing the specified date. For example, in PostgreSQL this
* looks like "'1970-01-02 02:17:36.789000 GMT'::timestamp". * looks like "'1970-01-02 02:17:36.789000 GMT'::timestamp".
*/ */
public abstract String dateAsSqlFunction(Date date, Calendar calendar); public abstract String dateAsSqlFunction(Timestamp timestamp, Calendar calendar);
/** /**
* Return a SQL function representing the specified date without time. * Return a SQL function representing the specified date without time.

View file

@ -1,7 +1,7 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.time.LocalDateTime;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
/** /**
* Control various optional behavior for the database interactions. * Control various optional behavior for the database interactions.
@ -98,7 +98,7 @@ public interface Options {
* The value returned by this method will be used for argDateNowPerApp() calls. It * 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. * may also be used for argDateNowPerDb() calls if you have enabled that.
*/ */
Date currentDate(); LocalDateTime currentDate();
/** /**
* Wherever argDateNowPerDb() is specified, use argDateNowPerApp() instead. This is * Wherever argDateNowPerDb() is specified, use argDateNowPerApp() instead. This is

View file

@ -1,6 +1,7 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
@ -63,8 +64,8 @@ public class OptionsDefault implements Options {
} }
@Override @Override
public Date currentDate() { public LocalDateTime currentDate() {
return new Date(); return LocalDateTime.now();
} }
@Override @Override

View file

@ -1,5 +1,6 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.time.LocalDateTime;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@ -87,7 +88,7 @@ public class OptionsOverride implements Options {
} }
@Override @Override
public Date currentDate() { public LocalDateTime currentDate() {
return parent.currentDate(); return parent.currentDate();
} }

View file

@ -5,7 +5,7 @@ import java.io.Reader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date; import java.time.LocalDateTime;
/** /**
* Interface for reading results from a database query. * Interface for reading results from a database query.
@ -387,32 +387,19 @@ public interface Row {
InputStream getBlobInputStreamOrEmpty(int columnOneBased); InputStream getBlobInputStreamOrEmpty(int columnOneBased);
InputStream getBlobInputStreamOrEmpty(String columnName); InputStream getBlobInputStreamOrEmpty(String columnName);
/** LocalDateTime getLocalDateTimeOrNull();
* Return the millisecond precision Date, which should be represented as a TIMESTAMP
* in the database. The nanoseconds are truncated.
*/
Date getDateOrNull(); LocalDateTime getLocalDateTimeOrNull(int columnOneBased);
/** LocalDateTime getLocalDateTimeOrNull(String columnName);
* Return the millisecond precision Date, which should be represented as a TIMESTAMP
* in the database. The nanoseconds are truncated.
*/
Date getDateOrNull(int columnOneBased);
Date getDateOrNull(String columnName);
/** /**
* Retrieve column as LocalDate, .i.e, date with no time. * Retrieve column as LocalDate, .i.e, date with no time.
* *
* @return LocalDate of the database column value * @return LocalDate of the database column value
*/ */
LocalDate getLocalDateOrNull(); LocalDate getLocalDateOrNull();
/** /**

View file

@ -10,7 +10,7 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date; import java.time.LocalDateTime;
/** /**
* Safely wrap a ResultSet and provide access to the data it contains. * Safely wrap a ResultSet and provide access to the data it contains.
@ -53,7 +53,6 @@ class RowsAdaptor implements Rows {
} }
} }
@Override @Override
public ResultSetMetaData getMetadata() { public ResultSetMetaData getMetadata() {
try { try {
@ -63,13 +62,11 @@ class RowsAdaptor implements Rows {
} }
} }
@Override @Override
public Boolean getBooleanOrNull() { public Boolean getBooleanOrNull() {
return getBooleanOrNull(column++); return getBooleanOrNull(column++);
} }
@Override @Override
public Boolean getBooleanOrNull(int columnOneBased) { public Boolean getBooleanOrNull(int columnOneBased) {
try { try {
@ -80,7 +77,6 @@ class RowsAdaptor implements Rows {
} }
} }
@Override @Override
public Boolean getBooleanOrNull(String columnName) { public Boolean getBooleanOrNull(String columnName) {
try { try {
@ -661,35 +657,31 @@ class RowsAdaptor implements Rows {
return result; return result;
} }
@Override @Override
public Date getDateOrNull() { public LocalDateTime getLocalDateTimeOrNull() {
return getDateOrNull(column++); return getLocalDateTimeOrNull(column++);
} }
@Override @Override
public Date getDateOrNull(int columnOneBased) { public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) {
try { try {
column = columnOneBased + 1; column = columnOneBased + 1;
return toDate(rs, columnOneBased); return toLocalDateTime(rs, columnOneBased);
} catch (SQLException e) { } catch (SQLException e) {
throw new DatabaseException(e); throw new DatabaseException(e);
} }
} }
@Override @Override
public Date getDateOrNull(String columnName) { public LocalDateTime getLocalDateTimeOrNull(String columnName) {
try { try {
column = rs.findColumn(columnName) + 1; column = rs.findColumn(columnName) + 1;
return toDate(rs, columnName); return toLocalDateTime(rs, columnName);
} catch (SQLException e) { } catch (SQLException e) {
throw new DatabaseException(e); throw new DatabaseException(e);
} }
} }
@Override @Override
public LocalDate getLocalDateOrNull() { public LocalDate getLocalDateOrNull() {
return getLocalDateOrNull(column++); return getLocalDateOrNull(column++);
@ -720,31 +712,21 @@ class RowsAdaptor implements Rows {
@Override @Override
public Integer rowCount() { public Integer rowCount() {
try { try {
//rs.last(); rs.last();
return rs.getRow(); return rs.getRow();
} catch (SQLException e) { } catch (SQLException e) {
throw new DatabaseException(e); throw new DatabaseException(e);
} }
} }
/** private LocalDateTime toLocalDateTime(ResultSet rs, int col) throws SQLException {
* Make sure the Timestamp will return getTime() accurate to the millisecond Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps());
* (if possible) and truncate away nanoseconds. return val == null ? null : val.toLocalDateTime();
*/
private Date timestampToDate(Timestamp ts) {
long millis = ts.getTime();
int nanos = ts.getNanos();
return new Date(millis / 1000 * 1000 + nanos / 1000000);
} }
private Date toDate(ResultSet rs, int col) throws SQLException { private LocalDateTime toLocalDateTime(ResultSet rs, String col) throws SQLException {
Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps()); Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps());
return val == null ? null : timestampToDate(val); return val == null ? null : val.toLocalDateTime();
}
private Date toDate(ResultSet rs, String col) throws SQLException {
Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps());
return val == null ? null : timestampToDate(val);
} }
private LocalDate toLocalDate(ResultSet rs, int col) throws SQLException { private LocalDate toLocalDate(ResultSet rs, int col) throws SQLException {

View file

@ -3,9 +3,9 @@ package org.xbib.jdbc.query;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
@ -310,14 +310,13 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
return this; return this;
} }
public Sql argDate(LocalDateTime arg) {
public Sql argDate(Date arg) {
sqlArgs.argDate(arg); sqlArgs.argDate(arg);
return this; return this;
} }
public Sql argDate( String argName, Date arg) { public Sql argDate( String argName, LocalDateTime arg) {
sqlArgs.argDate(argName, arg); sqlArgs.argDate(argName, arg);
return this; return this;
} }

View file

@ -7,8 +7,8 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -21,7 +21,6 @@ import java.util.Set;
public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply { public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
private final List<Invocation> invocations = new ArrayList<>(); private final List<Invocation> invocations = new ArrayList<>();
public static Builder fromMetadata(Row r) { public static Builder fromMetadata(Row r) {
return new Builder(r); return new Builder(r);
} }
@ -147,14 +146,14 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
} }
public SqlArgs argDate(Date arg) { public SqlArgs argDate(LocalDateTime arg) {
// date argument with a time on it // date argument with a time on it
invocations.add(new Invocation(ColumnType.Date, null, arg)); invocations.add(new Invocation(ColumnType.Date, null, arg));
return this; return this;
} }
public SqlArgs argDate( String argName, Date arg) { public SqlArgs argDate( String argName, LocalDateTime arg) {
// date argument with a time on it // date argument with a time on it
invocations.add(new Invocation(ColumnType.Date, argName, arg)); invocations.add(new Invocation(ColumnType.Date, argName, arg));
return this; return this;
@ -347,7 +346,6 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
case BlobStream: case BlobStream:
throw new DatabaseException("Don't use Blob parameters with select statements"); throw new DatabaseException("Don't use Blob parameters with select statements");
case LocalDate: case LocalDate:
// date argument with no time on it
if (i.argName == null) { if (i.argName == null) {
select.argLocalDate((LocalDate) i.arg); select.argLocalDate((LocalDate) i.arg);
} else { } else {
@ -355,11 +353,10 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
} }
break; break;
case Date: case Date:
// date argument with a time on it
if (i.argName == null) { if (i.argName == null) {
select.argDate((Date) i.arg); select.argDate((LocalDateTime) i.arg);
} else { } else {
select.argDate(i.argName, (Date) i.arg); select.argDate(i.argName, (LocalDateTime) i.arg);
} }
break; break;
case DateNowPerApp: case DateNowPerApp:
@ -473,9 +470,9 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
case Date: case Date:
// date argument with a time on it // date argument with a time on it
if (i.argName == null) { if (i.argName == null) {
insert.argDate((Date) i.arg); insert.argDate((LocalDateTime) i.arg);
} else { } else {
insert.argDate(i.argName, (Date) i.arg); insert.argDate(i.argName, (LocalDateTime) i.arg);
} }
break; break;
case DateNowPerApp: case DateNowPerApp:
@ -579,7 +576,6 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
} }
break; break;
case LocalDate: case LocalDate:
// date argument with no time on it
if (i.argName == null) { if (i.argName == null) {
update.argLocalDate((LocalDate) i.arg); update.argLocalDate((LocalDate) i.arg);
} else { } else {
@ -587,11 +583,10 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
} }
break; break;
case Date: case Date:
// date argument with a time on it
if (i.argName == null) { if (i.argName == null) {
update.argDate((Date) i.arg); update.argDate((LocalDateTime) i.arg);
} else { } else {
update.argDate(i.argName, (Date) i.arg); update.argDate(i.argName, (LocalDateTime) i.arg);
} }
break; break;
case DateNowPerApp: case DateNowPerApp:
@ -748,7 +743,7 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
// Anything with a time will have a non-zero scale // Anything with a time will have a non-zero scale
args.argLocalDate(names[i], r.getLocalDateOrNull()); args.argLocalDate(names[i], r.getLocalDateOrNull());
} else { } else {
args.argDate(names[i], r.getDateOrNull()); args.argDate(names[i], r.getLocalDateTimeOrNull());
} }
break; break;

View file

@ -4,7 +4,7 @@ import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date; import java.time.LocalDateTime;
/** /**
* Interface for configuring (setting parameters) and executing a chunk of SQL. * Interface for configuring (setting parameters) and executing a chunk of SQL.
@ -33,15 +33,15 @@ public interface SqlInsert {
SqlInsert argBigDecimal(BigDecimal arg); SqlInsert argBigDecimal(BigDecimal arg);
SqlInsert argBigDecimal( String argName, BigDecimal arg); SqlInsert argBigDecimal(String argName, BigDecimal arg);
SqlInsert argString(String arg); SqlInsert argString(String arg);
SqlInsert argString( String argName, String arg); SqlInsert argString( String argName, String arg);
SqlInsert argDate(Date arg); // date with time SqlInsert argDate(LocalDateTime arg); // date with time
SqlInsert argDate( String argName, Date arg); // date with time SqlInsert argDate(String argName, LocalDateTime arg); // date with time
SqlInsert argLocalDate(LocalDate arg); // date only - no timestamp SqlInsert argLocalDate(LocalDate arg); // date only - no timestamp

View file

@ -13,9 +13,9 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Statement; import java.sql.Statement;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -125,43 +125,36 @@ public class SqlInsertImpl implements SqlInsert {
} }
@Override @Override
public SqlInsert argBigDecimal( String argName, BigDecimal arg) { public SqlInsert argBigDecimal( String argName, BigDecimal arg) {
return namedArg(argName, adaptor.nullNumeric(arg)); return namedArg(argName, adaptor.nullNumeric(arg));
} }
@Override @Override
public SqlInsert argString(String arg) { public SqlInsert argString(String arg) {
return positionalArg(adaptor.nullString(arg)); return positionalArg(adaptor.nullString(arg));
} }
@Override @Override
public SqlInsert argString( String argName, String arg) { public SqlInsert argString( String argName, String arg) {
return namedArg(argName, adaptor.nullString(arg)); return namedArg(argName, adaptor.nullString(arg));
} }
@Override @Override
public SqlInsert argDate(LocalDateTime arg) {
public SqlInsert argDate(Date arg) {
return positionalArg(adaptor.nullDate(arg)); return positionalArg(adaptor.nullDate(arg));
} }
@Override @Override
public SqlInsert argDate( String argName, LocalDateTime arg) {
public SqlInsert argDate( String argName, Date arg) {
return namedArg(argName, adaptor.nullDate(arg)); return namedArg(argName, adaptor.nullDate(arg));
} }
@Override @Override
public SqlInsert argLocalDate( String argName, LocalDate arg) { public SqlInsert argLocalDate( String argName, LocalDate arg) {
return namedArg(argName, adaptor.nullLocalDate(arg)); return namedArg(argName, adaptor.nullLocalDate(arg));
} }
@Override @Override
public SqlInsert argLocalDate(LocalDate arg) { public SqlInsert argLocalDate(LocalDate arg) {
return positionalArg(adaptor.nullLocalDate(arg)); return positionalArg(adaptor.nullLocalDate(arg));
} }
@ -173,12 +166,10 @@ public class SqlInsertImpl implements SqlInsert {
} }
@Override @Override
public SqlInsert argDateNowPerApp( String argName) { public SqlInsert argDateNowPerApp( String argName) {
return namedArg(argName, adaptor.nullDate(options.currentDate())); return namedArg(argName, adaptor.nullDate(options.currentDate()));
} }
@Override @Override
public SqlInsert argDateNowPerDb() { public SqlInsert argDateNowPerDb() {
if (options.useDatePerAppOnly()) { if (options.useDatePerAppOnly()) {

View file

@ -2,7 +2,7 @@ package org.xbib.jdbc.query;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -67,11 +67,9 @@ public interface SqlSelect {
SqlSelect argDate(Date arg); // Date with time SqlSelect argDate(LocalDateTime arg); // Date with time
SqlSelect argDate(String argName, LocalDateTime arg); // Date with time
SqlSelect argDate( String argName, Date arg); // Date with time
@ -207,11 +205,11 @@ public interface SqlSelect {
Date queryDateOrNull(); // Date with time LocalDateTime queryDateOrNull(); // Date with time
List<Date> queryDates(); // Date with time List<LocalDateTime> queryDates(); // Date with time

View file

@ -10,8 +10,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -140,14 +140,14 @@ public class SqlSelectImpl implements SqlSelect {
@Override @Override
public SqlSelect argDate(Date arg) { public SqlSelect argDate(LocalDateTime arg) {
// Date with time // Date with time
return positionalArg(adaptor.nullDate(arg)); return positionalArg(adaptor.nullDate(arg));
} }
@Override @Override
public SqlSelect argDate( String argName, Date arg) { public SqlSelect argDate( String argName, LocalDateTime arg) {
// Date with time // Date with time
return namedArg(argName, adaptor.nullDate(arg)); return namedArg(argName, adaptor.nullDate(arg));
} }
@ -467,21 +467,21 @@ public class SqlSelectImpl implements SqlSelect {
} }
@Override @Override
public Date queryDateOrNull() { public LocalDateTime queryDateOrNull() {
return queryWithTimeout(rs -> { return queryWithTimeout(rs -> {
if (rs.next()) { if (rs.next()) {
return rs.getDateOrNull(1); return rs.getLocalDateTimeOrNull(1);
} }
return null; return null;
}); });
} }
@Override @Override
public List<Date> queryDates() { public List<LocalDateTime> queryDates() {
return queryWithTimeout(rs -> { return queryWithTimeout(rs -> {
List<Date> result = new ArrayList<>(); List<LocalDateTime> result = new ArrayList<>();
while (rs.next()) { while (rs.next()) {
Date value = rs.getDateOrNull(1); LocalDateTime value = rs.getLocalDateTimeOrNull(1);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -619,7 +619,9 @@ public class SqlSelectImpl implements SqlSelect {
if (connection != null) { if (connection != null) {
synchronized (cancelLock) { synchronized (cancelLock) {
ps = connection.prepareStatement(executeSql); ps = connection.prepareStatement(executeSql,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
} }
if (timeoutSeconds >= 0) { if (timeoutSeconds >= 0) {

View file

@ -4,138 +4,75 @@ import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date; import java.time.LocalDateTime;
/** /**
* Interface for configuring (setting parameters) and executing a chunk of SQL. * Interface for configuring (setting parameters) and executing a chunk of SQL.
*/ */
public interface SqlUpdate { public interface SqlUpdate {
SqlUpdate argBoolean(Boolean arg); SqlUpdate argBoolean(Boolean arg);
SqlUpdate argBoolean( String argName, Boolean arg); SqlUpdate argBoolean( String argName, Boolean arg);
SqlUpdate argInteger(Integer arg); SqlUpdate argInteger(Integer arg);
SqlUpdate argInteger( String argName, Integer arg); SqlUpdate argInteger( String argName, Integer arg);
SqlUpdate argLong(Long arg); SqlUpdate argLong(Long arg);
SqlUpdate argLong( String argName, Long arg); SqlUpdate argLong( String argName, Long arg);
SqlUpdate argFloat(Float arg); SqlUpdate argFloat(Float arg);
SqlUpdate argFloat( String argName, Float arg); SqlUpdate argFloat( String argName, Float arg);
SqlUpdate argDouble(Double arg); SqlUpdate argDouble(Double arg);
SqlUpdate argDouble( String argName, Double arg); SqlUpdate argDouble( String argName, Double arg);
SqlUpdate argBigDecimal(BigDecimal arg); SqlUpdate argBigDecimal(BigDecimal arg);
SqlUpdate argBigDecimal( String argName, BigDecimal arg); SqlUpdate argBigDecimal( String argName, BigDecimal arg);
SqlUpdate argString(String arg); SqlUpdate argString(String arg);
SqlUpdate argString( String argName, String arg); SqlUpdate argString( String argName, String arg);
SqlUpdate argDate(LocalDateTime arg);
SqlUpdate argDate( String argName, LocalDateTime arg);
SqlUpdate argDate(Date arg); // Date with timestamp SqlUpdate argLocalDate(LocalDate arg);
SqlUpdate argDate( String argName, Date arg); // Date with timestamp
SqlUpdate argLocalDate(LocalDate arg); // Date only - no timestamp
SqlUpdate argLocalDate( String argName, LocalDate arg); // Date only - no timestamp
SqlUpdate argLocalDate( String argName, LocalDate arg);
SqlUpdate argDateNowPerApp(); SqlUpdate argDateNowPerApp();
SqlUpdate argDateNowPerApp( String argName); SqlUpdate argDateNowPerApp( String argName);
SqlUpdate argDateNowPerDb(); SqlUpdate argDateNowPerDb();
SqlUpdate argDateNowPerDb( String argName); SqlUpdate argDateNowPerDb( String argName);
SqlUpdate argBlobBytes(byte[] arg); SqlUpdate argBlobBytes(byte[] arg);
SqlUpdate argBlobBytes( String argName, byte[] arg); SqlUpdate argBlobBytes( String argName, byte[] arg);
SqlUpdate argBlobStream(InputStream arg); SqlUpdate argBlobStream(InputStream arg);
SqlUpdate argBlobStream( String argName, InputStream arg); SqlUpdate argBlobStream( String argName, InputStream arg);
SqlUpdate argClobString(String arg); SqlUpdate argClobString(String arg);
SqlUpdate argClobString( String argName, String arg); SqlUpdate argClobString( String argName, String arg);
SqlUpdate argClobReader(Reader arg); SqlUpdate argClobReader(Reader arg);
SqlUpdate argClobReader( String argName, Reader arg); SqlUpdate argClobReader( String argName, Reader arg);
SqlUpdate withArgs(SqlArgs args); SqlUpdate withArgs(SqlArgs args);
SqlUpdate apply(Apply apply); SqlUpdate apply(Apply apply);
/** /**
@ -151,14 +88,6 @@ public interface SqlUpdate {
*/ */
void update(int expectedRowsUpdated); void update(int expectedRowsUpdated);
/**
* Call this between setting rows of parameters for a SQL statement. You may call it before
* setting any parameters, after setting all, or multiple times between rows.
*/
// SqlUpdate batch();
// SqlUpdate withTimeoutSeconds(int seconds);
interface Apply { interface Apply {
void apply(SqlUpdate update); void apply(SqlUpdate update);
} }

View file

@ -11,8 +11,8 @@ import java.math.BigDecimal;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -131,33 +131,28 @@ public class SqlUpdateImpl implements SqlUpdate {
@Override @Override
public SqlUpdate argDate(Date arg) { public SqlUpdate argDate(LocalDateTime arg) {
// Date with time
return positionalArg(adaptor.nullDate(arg)); return positionalArg(adaptor.nullDate(arg));
} }
@Override @Override
public SqlUpdate argDate(String argName, Date arg) { public SqlUpdate argDate(String argName, LocalDateTime arg) {
// Date with time
return namedArg(argName, adaptor.nullDate(arg)); return namedArg(argName, adaptor.nullDate(arg));
} }
@Override @Override
public SqlUpdate argLocalDate(LocalDate arg) { public SqlUpdate argLocalDate(LocalDate arg) {
// Date with no time
return positionalArg(adaptor.nullLocalDate(arg)); return positionalArg(adaptor.nullLocalDate(arg));
} }
@Override @Override
public SqlUpdate argLocalDate(String argName, LocalDate arg) { public SqlUpdate argLocalDate(String argName, LocalDate arg) {
// Date with no time
return namedArg(argName, adaptor.nullLocalDate(arg)); return namedArg(argName, adaptor.nullLocalDate(arg));
} }
@Override @Override
public SqlUpdate argDateNowPerApp() { public SqlUpdate argDateNowPerApp() {
return positionalArg(adaptor.nullDate(options.currentDate())); return positionalArg(adaptor.nullDate(options.currentDate()));
@ -169,7 +164,6 @@ public class SqlUpdateImpl implements SqlUpdate {
return namedArg(argName, adaptor.nullDate(options.currentDate())); return namedArg(argName, adaptor.nullDate(options.currentDate()));
} }
@Override @Override
public SqlUpdate argDateNowPerDb() { public SqlUpdate argDateNowPerDb() {
if (options.useDatePerAppOnly()) { if (options.useDatePerAppOnly()) {

View file

@ -12,6 +12,7 @@ import java.sql.Statement;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date; import java.util.Date;
import java.util.Scanner; import java.util.Scanner;
import java.util.logging.Level; import java.util.logging.Level;
@ -142,11 +143,11 @@ public class StatementAdaptor {
} }
} }
public Object nullDate(Date arg) { public Object nullDate(LocalDateTime arg) {
if (arg == null) { if (arg == null) {
return new SqlNull(Types.TIMESTAMP); return new SqlNull(Types.TIMESTAMP);
} }
return new Timestamp(arg.getTime()); return Timestamp.valueOf(arg);
} }
// Processes a true date without time information. // Processes a true date without time information.

View file

@ -6,8 +6,8 @@ import org.xbib.jdbc.query.SqlNull;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.sql.Timestamp;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -77,9 +77,9 @@ public class DebugSql {
} else if (argToPrint instanceof SqlNull || argToPrint == null) { } else if (argToPrint instanceof SqlNull || argToPrint == null) {
buf.append("null"); buf.append("null");
} else if (argToPrint instanceof java.sql.Timestamp) { } else if (argToPrint instanceof java.sql.Timestamp) {
buf.append(options.flavor().dateAsSqlFunction((Date) argToPrint, options.calendarForTimestamps())); buf.append(options.flavor().dateAsSqlFunction((Timestamp) argToPrint, options.calendarForTimestamps()));
} else if (argToPrint instanceof java.sql.Date) { } else if (argToPrint instanceof java.sql.Date) {
buf.append(options.flavor().localDateAsSqlFunction((Date) argToPrint)); buf.append(options.flavor().localDateAsSqlFunction((java.sql.Date) argToPrint));
} else if (argToPrint instanceof Number) { } else if (argToPrint instanceof Number) {
buf.append(argToPrint); buf.append(argToPrint);
} else if (argToPrint instanceof Boolean) { } else if (argToPrint instanceof Boolean) {

View file

@ -25,15 +25,23 @@ import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.Timestamp;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month; import java.time.Month;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.logging.Logger;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ -50,6 +58,8 @@ import static org.junit.jupiter.api.Assertions.fail;
*/ */
public abstract class CommonTest { public abstract class CommonTest {
private static final Logger logger = Logger.getLogger(CommonTest.class.getName());
final static String TEST_TABLE_NAME = "dbtest"; final static String TEST_TABLE_NAME = "dbtest";
/** /**
@ -60,7 +70,7 @@ public abstract class CommonTest {
protected Database db; protected Database db;
protected Date now = new Date(); protected LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);
protected LocalDate localDateNow = LocalDate.now(); protected LocalDate localDateNow = LocalDate.now();
@ -68,7 +78,7 @@ public abstract class CommonTest {
public void setupJdbc() throws Exception { public void setupJdbc() throws Exception {
dbp = createDatabaseProvider(new OptionsOverride() { dbp = createDatabaseProvider(new OptionsOverride() {
@Override @Override
public Date currentDate() { public LocalDateTime currentDate() {
return now; return now;
} }
@ -197,8 +207,8 @@ public abstract class CommonTest {
assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob")); assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob"));
assertArrayEquals("More".getBytes(), rs.getBlobBytesOrZeroLen(9)); assertArrayEquals("More".getBytes(), rs.getBlobBytesOrZeroLen(9));
assertArrayEquals("More".getBytes(), rs.getBlobBytesOrZeroLen("bin_blob")); assertArrayEquals("More".getBytes(), rs.getBlobBytesOrZeroLen("bin_blob"));
assertEquals(now, rs.getDateOrNull(10)); assertEquals(now, rs.getLocalDateTimeOrNull(10));
assertEquals(now, rs.getDateOrNull("date_millis")); assertEquals(now, rs.getLocalDateTimeOrNull("date_millis"));
assertEquals(localDateNow, rs.getLocalDateOrNull(11)); assertEquals(localDateNow, rs.getLocalDateOrNull(11));
assertEquals(localDateNow, rs.getLocalDateOrNull("local_date")); assertEquals(localDateNow, rs.getLocalDateOrNull("local_date"));
return null; return null;
@ -217,7 +227,7 @@ public abstract class CommonTest {
assertEquals("T", rs.getStringOrNull()); assertEquals("T", rs.getStringOrNull());
assertEquals("World", rs.getClobStringOrNull()); assertEquals("World", rs.getClobStringOrNull());
assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull()); assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull());
assertEquals(now, rs.getDateOrNull()); assertEquals(now, rs.getLocalDateTimeOrNull());
assertEquals(localDateNow, rs.getLocalDateOrNull()); assertEquals(localDateNow, rs.getLocalDateOrNull());
return null; return null;
}); });
@ -350,8 +360,8 @@ public abstract class CommonTest {
assertNull(rs.getClobStringOrNull("str_lob")); assertNull(rs.getClobStringOrNull("str_lob"));
assertNull(rs.getBlobBytesOrNull(9)); assertNull(rs.getBlobBytesOrNull(9));
assertNull(rs.getBlobBytesOrNull("bin_blob")); assertNull(rs.getBlobBytesOrNull("bin_blob"));
assertNull(rs.getDateOrNull(10)); assertNull(rs.getLocalDateTimeOrNull(10));
assertNull(rs.getDateOrNull("date_millis")); assertNull(rs.getLocalDateTimeOrNull("date_millis"));
assertNull(rs.getLocalDateOrNull(11)); assertNull(rs.getLocalDateOrNull(11));
assertNull(rs.getLocalDateOrNull("local_date")); assertNull(rs.getLocalDateOrNull("local_date"));
return null; return null;
@ -381,8 +391,8 @@ public abstract class CommonTest {
assertEquals("World", rs.getClobStringOrNull("str_lob")); assertEquals("World", rs.getClobStringOrNull("str_lob"));
assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull(9)); assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull(9));
assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob")); assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob"));
assertEquals(now, rs.getDateOrNull(10)); assertEquals(now, rs.getLocalDateTimeOrNull(10));
assertEquals(now, rs.getDateOrNull("date_millis")); assertEquals(now, rs.getLocalDateTimeOrNull("date_millis"));
assertEquals(localDateNow, rs.getLocalDateOrNull(11)); assertEquals(localDateNow, rs.getLocalDateOrNull(11));
assertEquals(localDateNow, rs.getLocalDateOrNull("local_date")); assertEquals(localDateNow, rs.getLocalDateOrNull("local_date"));
return null; return null;
@ -457,8 +467,8 @@ public abstract class CommonTest {
assertNull(rs.getClobStringOrNull("str_lob")); assertNull(rs.getClobStringOrNull("str_lob"));
assertNull(rs.getBlobBytesOrNull(9)); assertNull(rs.getBlobBytesOrNull(9));
assertNull(rs.getBlobBytesOrNull("bin_blob")); assertNull(rs.getBlobBytesOrNull("bin_blob"));
assertNull(rs.getDateOrNull(10)); assertNull(rs.getLocalDateTimeOrNull(10));
assertNull(rs.getDateOrNull("date_millis")); assertNull(rs.getLocalDateTimeOrNull("date_millis"));
assertNull(rs.getLocalDateOrNull(11)); assertNull(rs.getLocalDateOrNull(11));
assertNull(rs.getLocalDateOrNull("local_date")); assertNull(rs.getLocalDateOrNull("local_date"));
return null; return null;
@ -491,8 +501,8 @@ public abstract class CommonTest {
assertEquals("World", rs.getClobStringOrNull("str_lob")); assertEquals("World", rs.getClobStringOrNull("str_lob"));
assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull(9)); assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull(9));
assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob")); assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob"));
assertEquals(now, rs.getDateOrNull(10)); assertEquals(now, rs.getLocalDateTimeOrNull(10));
assertEquals(now, rs.getDateOrNull("date_millis")); assertEquals(now, rs.getLocalDateTimeOrNull("date_millis"));
assertEquals(localDateNow, rs.getLocalDateOrNull(11)); assertEquals(localDateNow, rs.getLocalDateOrNull(11));
assertEquals(localDateNow, rs.getLocalDateOrNull("local_date")); assertEquals(localDateNow, rs.getLocalDateOrNull("local_date"));
return null; return null;
@ -560,8 +570,8 @@ public abstract class CommonTest {
assertNull(rs.getClobStringOrNull("str_lob")); assertNull(rs.getClobStringOrNull("str_lob"));
assertNull(rs.getBlobBytesOrNull(9)); assertNull(rs.getBlobBytesOrNull(9));
assertNull(rs.getBlobBytesOrNull("bin_blob")); assertNull(rs.getBlobBytesOrNull("bin_blob"));
assertNull(rs.getDateOrNull(10)); assertNull(rs.getLocalDateTimeOrNull(10));
assertNull(rs.getDateOrNull("date_millis")); assertNull(rs.getLocalDateTimeOrNull("date_millis"));
assertNull(rs.getLocalDateOrNull(11)); assertNull(rs.getLocalDateOrNull(11));
assertNull(rs.getLocalDateOrNull("local_date")); assertNull(rs.getLocalDateOrNull("local_date"));
return null; return null;
@ -1324,10 +1334,9 @@ public abstract class CommonTest {
@Test @Test
public void argBigDecimal38Precision38() { public void argBigDecimal38Precision38() {
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(38, 38).schema().execute(db); new Schema().addTable("dbtest").addColumn("i").asBigDecimal(38, 38).schema().execute(db);
BigDecimal value = new BigDecimal("0.99999999999999999999999999999999999999"); // 38 digits BigDecimal value = new BigDecimal("0.99999999999999999999999999999999999999"); // 38 digits
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1); db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
System.out.println(db.toSelect("select i from dbtest").queryBigDecimalOrNull()); logger.info(db.toSelect("select i from dbtest").queryBigDecimalOrNull().toString());
assertEquals(value, assertEquals(value,
db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull()); db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
} }
@ -1379,7 +1388,7 @@ public abstract class CommonTest {
.insertReturning("dbtest", "pk", rs -> { .insertReturning("dbtest", "pk", rs -> {
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(Long.valueOf(1L), rs.getLongOrNull(1)); assertEquals(Long.valueOf(1L), rs.getLongOrNull(1));
assertThat(rs.getDateOrNull(2), equalTo(now)); assertThat(rs.getLocalDateTimeOrNull(2), equalTo(now));
assertFalse(rs.next()); assertFalse(rs.next());
return null; return null;
}, "d"); }, "d");
@ -1511,13 +1520,13 @@ public abstract class CommonTest {
.addColumn("d").asDate().table().schema() .addColumn("d").asDate().table().schema()
.addSequence("dbtest_seq").schema() .addSequence("dbtest_seq").schema()
.execute(db); .execute(db);
Date dbNow = db.toInsert("insert into dbtest (pk, d) values (:seq, :d)") LocalDateTime dbNow = db.toInsert("insert into dbtest (pk, d) values (:seq, :d)")
.argPkSeq(":seq", "dbtest_seq") .argPkSeq(":seq", "dbtest_seq")
.argDateNowPerDb(":d") .argDateNowPerDb(":d")
.insertReturning("dbtest", "pk", rs -> { .insertReturning("dbtest", "pk", rs -> {
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(Long.valueOf(1L), rs.getLongOrNull(1)); assertEquals(Long.valueOf(1L), rs.getLongOrNull(1));
Date dbDate = rs.getDateOrNull(2); LocalDateTime dbDate = rs.getLocalDateTimeOrNull(2);
assertFalse(rs.next()); assertFalse(rs.next());
return dbDate; return dbDate;
}, "d"); }, "d");
@ -1583,11 +1592,11 @@ public abstract class CommonTest {
db.toInsert("insert into dbtest (d) values (?)") db.toInsert("insert into dbtest (d) values (?)")
.argDateNowPerDb() .argDateNowPerDb()
.insert(1); .insert(1);
Date dbNow = db.toSelect("select d from dbtest").queryDateOrNull(); LocalDateTime dbNow = db.toSelect("select d from dbtest").queryDateOrNull();
if (dbNow != null && dbNow.getTime() % 10 != 0) { if (dbNow != null && dbNow.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() % 10 != 0) {
return; return;
} }
System.out.println("Zero in least significant digit (attempt " + attempts + ")"); logger.info("Zero in least significant digit (attempt " + attempts + ")");
db.dropTableQuietly(TEST_TABLE_NAME); db.dropTableQuietly(TEST_TABLE_NAME);
} }
fail("Timestamp had zero in the least significant digit"); fail("Timestamp had zero in the least significant digit");
@ -1605,7 +1614,7 @@ public abstract class CommonTest {
.argDateNowPerDb() .argDateNowPerDb()
.insert(1); .insert(1);
// Now pull it out, put it back in, and verify it matches in the database // Now pull it out, put it back in, and verify it matches in the database
Date dbNow = db.toSelect("select d1 from dbtest").queryDateOrNull(); LocalDateTime dbNow = db.toSelect("select d1 from dbtest").queryDateOrNull();
db.toUpdate("update dbtest set d2=?") db.toUpdate("update dbtest set d2=?")
.argDate(dbNow) .argDate(dbNow)
.update(1); .update(1);
@ -1619,21 +1628,23 @@ public abstract class CommonTest {
.addTable("dbtest") .addTable("dbtest")
.addColumn("d").asDate().table().schema() .addColumn("d").asDate().table().schema()
.execute(db); .execute(db);
Date date = new Date(166656789L); 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(); TimeZone defaultTZ = TimeZone.getDefault();
try { try {
TimeZone.setDefault(TimeZone.getTimeZone("GMT-4:00")); TimeZone.setDefault(TimeZone.getTimeZone("GMT-4:00"));
db.toInsert("insert into dbtest (d) values (?)").argDate(date).insert(1); db.toInsert("insert into dbtest (d) values (?)").argDate(dateMinus).insert(1);
assertEquals(date, db.toSelect("select d from dbtest").queryDateOrNull()); LocalDateTime localDateTimeMinus = db.toSelect("select d from dbtest").queryDateOrNull();
assertEquals("1970-01-02 18:17:36.789000-0400", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000Z").format( assertEquals(dateMinus, localDateTimeMinus);
db.toSelect("select d from dbtest").queryDateOrNull())); 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(date).update(1); db.toDelete("delete from dbtest where d=?").argDate(dateMinus).update(1);
TimeZone.setDefault(TimeZone.getTimeZone("GMT+4:00")); TimeZone.setDefault(TimeZone.getTimeZone("GMT+4:00"));
db.toInsert("insert into dbtest (d) values (?)").argDate(date).insert(1); db.toInsert("insert into dbtest (d) values (?)").argDate(datePlus).insert(1);
assertEquals(date, db.toSelect("select d from dbtest").queryDateOrNull()); LocalDateTime localDateTimePlus = db.toSelect("select d from dbtest").queryDateOrNull();
assertEquals("1970-01-03 02:17:36.789000+0400", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000Z").format( assertEquals(datePlus, localDateTimePlus);
db.toSelect("select d from dbtest").queryDateOrNull())); 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(date).update(1); db.toDelete("delete from dbtest where d=?").argDate(datePlus).update(1);
} finally { } finally {
TimeZone.setDefault(defaultTZ); TimeZone.setDefault(defaultTZ);
} }
@ -1647,30 +1658,30 @@ public abstract class CommonTest {
*/ */
@Test @Test
public void stringDateFunctions() { public void stringDateFunctions() {
Date date = new Date(166656789L); Instant instant = Instant.ofEpochMilli(166656789L);
System.out.println("Date: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000Z").format(date)); 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(); TimeZone defaultTZ = TimeZone.getDefault();
try { try {
TimeZone.setDefault(TimeZone.getTimeZone("GMT-4:00")); TimeZone.setDefault(TimeZone.getTimeZone("GMT-4:00"));
new Schema() new Schema().addTable("dbtest").addColumn("d").asDate().schema().execute(db);
.addTable("dbtest")
.addColumn("d").asDate().schema().execute(db);
db.toInsert("insert into dbtest (d) values (" db.toInsert("insert into dbtest (d) values ("
+ db.flavor().dateAsSqlFunction(date, db.options().calendarForTimestamps()).replace(":", "::") + ")") + db.flavor().dateAsSqlFunction(Timestamp.valueOf(dateMinus), db.options().calendarForTimestamps()).replace(":", "::") + ")")
.insert(1); .insert(1);
assertEquals("1970-01-02 18:17:36.789000-0400", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000Z").format( assertEquals("1970-01-02 18:17:36.789",
db.toSelect("select d from dbtest").queryDateOrNull())); 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 // Now do some client operations in a different time zone
TimeZone.setDefault(TimeZone.getTimeZone("GMT+4:00")); TimeZone.setDefault(TimeZone.getTimeZone("GMT+4:00"));
// Verify regular arg maps date the same way even though our TimeZone is now different // Verify regular arg maps date the same way even though our TimeZone is now different
db.toDelete("delete from dbtest where d=?").argDate(date).update(1); db.toDelete("delete from dbtest where d=?").argDate(datePlus).update(1);
db.toInsert("insert into dbtest (d) values (" db.toInsert("insert into dbtest (d) values ("
+ db.flavor().dateAsSqlFunction(date, db.options().calendarForTimestamps()).replace(":", "::") + ")") + db.flavor().dateAsSqlFunction(Timestamp.valueOf(datePlus), db.options().calendarForTimestamps()).replace(":", "::") + ")")
.insert(1); .insert(1);
assertEquals("1970-01-03 02:17:36.789000+0400", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000Z").format( assertEquals("1970-01-03 02:17:36.789",
db.toSelect("select d from dbtest").queryDateOrNull())); 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 // Verify the function maps correctly for equals operations as well
db.toDelete("delete from dbtest where d=" + db.flavor().dateAsSqlFunction(date, db.toDelete("delete from dbtest where d=" + db.flavor().dateAsSqlFunction(Timestamp.valueOf(datePlus),
db.options().calendarForTimestamps()).replace(":", "::")).update(1); db.options().calendarForTimestamps()).replace(":", "::")).update(1);
} finally { } finally {
TimeZone.setDefault(defaultTZ); TimeZone.setDefault(defaultTZ);

View file

@ -10,11 +10,10 @@ import java.io.StringReader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.Types; import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
/** /**
@ -628,20 +627,20 @@ public class RowStub {
@Override @Override
public Date getDateOrNull() { public LocalDateTime getLocalDateTimeOrNull() {
return toDate(rows.get(row)[++col]); return toDate(rows.get(row)[++col]);
} }
@Override @Override
public Date getDateOrNull(int columnOneBased) { public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) {
col = columnOneBased; col = columnOneBased;
return toDate(rows.get(row)[columnOneBased - 1]); return toDate(rows.get(row)[columnOneBased - 1]);
} }
@Override @Override
public Date getDateOrNull(String columnName) { public LocalDateTime getLocalDateTimeOrNull(String columnName) {
col = columnIndexByName(columnName) + 1; col = columnIndexByName(columnName) + 1;
return toDate(rows.get(row)[columnIndexByName(columnName)]); return toDate(rows.get(row)[columnIndexByName(columnName)]);
} }
@ -759,29 +758,18 @@ public class RowStub {
return (String) o; return (String) o;
} }
/** private LocalDateTime toDate(Object o) {
* Returns a java.util.Date. It may be used for dates or times.
*/
private Date toDate(Object o) {
if (o instanceof String) { if (o instanceof String) {
String s = (String) o; String s = (String) o;
if (s.length() == "yyyy-MM-dd".length()) { if (s.length() == "yyyy-MM-dd".length()) {
try { return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
return new SimpleDateFormat("yyyy-MM-dd").parse(s);
} catch (ParseException e) {
throw new DatabaseException("Could not parse date as yyyy-MM-dd for " + s);
}
} }
if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) { if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) {
try { return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss"));
return new SimpleDateFormat("yyyy-MM-ddThh:mm:ss").parse(s);
} catch (ParseException e) {
throw new DatabaseException("Could not parse date as yyyy-MM-ddThh:mm:ss for " + s);
}
} }
throw new DatabaseException("Didn't understand date string: " + s); throw new DatabaseException("Didn't understand date string: " + s);
} }
return (Date) o; return (LocalDateTime) o;
} }
/** /**

View file

@ -1,6 +1,6 @@
package org.xbib.jdbc.query.test.example; package org.xbib.jdbc.query.test.example;
import java.util.Date; import java.time.LocalDateTime;
/** /**
* Simple bean for use with SampleDao. * Simple bean for use with SampleDao.
@ -13,7 +13,7 @@ public class Sample {
private Integer updateSequence; private Integer updateSequence;
private Date updateTime; private LocalDateTime updateTime;
public Long getSampleId() { public Long getSampleId() {
return sampleId; return sampleId;
@ -39,11 +39,11 @@ public class Sample {
this.updateSequence = updateSequence; this.updateSequence = updateSequence;
} }
public Date getUpdateTime() { public LocalDateTime getUpdateTime() {
return updateTime; return updateTime;
} }
public void setUpdateTime(Date updateTime) { public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime; this.updateTime = updateTime;
} }
} }

View file

@ -2,6 +2,7 @@ package org.xbib.jdbc.query.test.example;
import org.xbib.jdbc.query.Database; import org.xbib.jdbc.query.Database;
import java.time.LocalDateTime;
import java.util.Date; import java.util.Date;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -19,7 +20,7 @@ public class SampleDao {
public void createSample(final Sample sample, Long userIdMakingChange) { public void createSample(final Sample sample, Long userIdMakingChange) {
Database db = dbp.get(); Database db = dbp.get();
Date updateTime = db.nowPerApp(); LocalDateTime updateTime = db.nowPerApp();
Long sampleId = db.toInsert( Long sampleId = db.toInsert(
"insert into sample (sample_id, sample_name, update_sequence, update_time) values (?,?,0,?)") "insert into sample (sample_id, sample_name, update_sequence, update_time) values (?,?,0,?)")
.argPkSeq("id_seq") .argPkSeq("id_seq")
@ -49,7 +50,7 @@ public class SampleDao {
result.setSampleId(sampleId); result.setSampleId(sampleId);
result.setName(r.getStringOrNull()); result.setName(r.getStringOrNull());
result.setUpdateSequence(r.getIntegerOrNull()); result.setUpdateSequence(r.getIntegerOrNull());
result.setUpdateTime(r.getDateOrNull()); result.setUpdateTime(r.getLocalDateTimeOrNull());
return result; return result;
}); });
} }
@ -60,7 +61,7 @@ public class SampleDao {
// Insert the history row first, so it will fail (non-unique sample_id + update_sequence) // 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. // if someone else modified the row. This is an optimistic locking strategy.
int newUpdateSequence = sample.getUpdateSequence() + 1; int newUpdateSequence = sample.getUpdateSequence() + 1;
Date newUpdateTime = db.nowPerApp(); LocalDateTime newUpdateTime = db.nowPerApp();
db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id," db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id,"
+ " is_deleted) values (?,?,?,?,?,'N')") + " is_deleted) values (?,?,?,?,?,'N')")
.argLong(sample.getSampleId()) .argLong(sample.getSampleId())
@ -88,7 +89,7 @@ public class SampleDao {
// Insert the history row first, so it will fail (non-unique sample_id + update_sequence) // 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. // if someone else modified the row. This is an optimistic locking strategy.
int newUpdateSequence = sample.getUpdateSequence() + 1; int newUpdateSequence = sample.getUpdateSequence() + 1;
Date newUpdateTime = db.nowPerApp(); LocalDateTime newUpdateTime = db.nowPerApp();
db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id," db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id,"
+ " is_deleted) values (?,?,?,?,?,'Y')") + " is_deleted) values (?,?,?,?,?,'Y')")
.argLong(sample.getSampleId()) .argLong(sample.getSampleId())