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;
import java.sql.Connection;
import java.util.Date;
import java.time.LocalDateTime;
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.
*/
Date nowPerApp();
LocalDateTime nowPerApp();
/**
* 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.ResultSet;
import java.sql.SQLException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.logging.Logger;
/**
@ -90,7 +91,7 @@ public class DatabaseImpl implements Database {
}
@Override
public Date nowPerApp() {
public LocalDateTime nowPerApp() {
return options.currentDate();
}
@ -249,25 +250,26 @@ public class DatabaseImpl implements Database {
public void assertTimeSynchronized(long millisToWarn, long millisToError) {
toSelect("select ?" + flavor().fromAny())
.argDateNowPerDb().queryFirstOrNull(r -> {
Date appDate = nowPerApp();
Date dbDate = r.getDateOrNull();
LocalDateTime appDate = nowPerApp();
LocalDateTime dbDate = r.getLocalDateTimeOrNull();
if (dbDate == null) {
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: "
+ DateTimeFormatter.ISO_INSTANT.format(appDate.toInstant()) + " db: "
+ DateTimeFormatter.ISO_INSTANT.format(dbDate.toInstant()));
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
+ 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: "
+ DateTimeFormatter.ISO_INSTANT.format(appDate.toInstant()) + " db: "
+ DateTimeFormatter.ISO_INSTANT.format(dbDate.toInstant()));
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
+ 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: "
+ DateTimeFormatter.ISO_INSTANT.format(appDate.toInstant()) + " db: "
+ DateTimeFormatter.ISO_INSTANT.format(dbDate.toInstant()));
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
+ DateTimeFormatter.ISO_INSTANT.format(dbDate));
}
return null;
});

View file

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

View file

@ -1,7 +1,7 @@
package org.xbib.jdbc.query;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
/**
* 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
* 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

View file

@ -1,6 +1,7 @@
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;
@ -63,8 +64,8 @@ public class OptionsDefault implements Options {
}
@Override
public Date currentDate() {
return new Date();
public LocalDateTime currentDate() {
return LocalDateTime.now();
}
@Override

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
import java.time.LocalDateTime;
/**
* Interface for configuring (setting parameters) and executing a chunk of SQL.
@ -33,15 +33,15 @@ public interface SqlInsert {
SqlInsert argBigDecimal(BigDecimal arg);
SqlInsert argBigDecimal( String argName, BigDecimal arg);
SqlInsert argBigDecimal(String argName, BigDecimal arg);
SqlInsert argString(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

View file

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

View file

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

View file

@ -4,138 +4,75 @@ import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
import java.time.LocalDateTime;
/**
* Interface for configuring (setting parameters) and executing a chunk of SQL.
*/
public interface SqlUpdate {
SqlUpdate argBoolean(Boolean arg);
SqlUpdate argBoolean( String argName, Boolean arg);
SqlUpdate argInteger(Integer arg);
SqlUpdate argInteger( String argName, Integer arg);
SqlUpdate argLong(Long arg);
SqlUpdate argLong( String argName, Long arg);
SqlUpdate argFloat(Float arg);
SqlUpdate argFloat( String argName, Float arg);
SqlUpdate argDouble(Double arg);
SqlUpdate argDouble( String argName, Double arg);
SqlUpdate argBigDecimal(BigDecimal arg);
SqlUpdate argBigDecimal( String argName, BigDecimal arg);
SqlUpdate argString(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 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(LocalDate arg);
SqlUpdate argLocalDate( String argName, LocalDate arg);
SqlUpdate argDateNowPerApp();
SqlUpdate argDateNowPerApp( String argName);
SqlUpdate argDateNowPerDb();
SqlUpdate argDateNowPerDb( String argName);
SqlUpdate argBlobBytes(byte[] arg);
SqlUpdate argBlobBytes( String argName, byte[] arg);
SqlUpdate argBlobStream(InputStream arg);
SqlUpdate argBlobStream( String argName, InputStream arg);
SqlUpdate argClobString(String arg);
SqlUpdate argClobString( String argName, String arg);
SqlUpdate argClobReader(Reader arg);
SqlUpdate argClobReader( String argName, Reader arg);
SqlUpdate withArgs(SqlArgs args);
SqlUpdate apply(Apply apply);
/**
@ -151,14 +88,6 @@ public interface SqlUpdate {
*/
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 {
void apply(SqlUpdate update);
}

View file

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

View file

@ -12,6 +12,7 @@ import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Scanner;
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) {
return new SqlNull(Types.TIMESTAMP);
}
return new Timestamp(arg.getTime());
return Timestamp.valueOf(arg);
}
// 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.Reader;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -77,9 +77,9 @@ 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((Date) argToPrint, options.calendarForTimestamps()));
buf.append(options.flavor().dateAsSqlFunction((Timestamp) argToPrint, options.calendarForTimestamps()));
} 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) {
buf.append(argToPrint);
} else if (argToPrint instanceof Boolean) {

View file

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

View file

@ -10,11 +10,10 @@ import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.ResultSetMetaData;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
@ -628,20 +627,20 @@ public class RowStub {
@Override
public Date getDateOrNull() {
public LocalDateTime getLocalDateTimeOrNull() {
return toDate(rows.get(row)[++col]);
}
@Override
public Date getDateOrNull(int columnOneBased) {
public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) {
col = columnOneBased;
return toDate(rows.get(row)[columnOneBased - 1]);
}
@Override
public Date getDateOrNull(String columnName) {
public LocalDateTime getLocalDateTimeOrNull(String columnName) {
col = columnIndexByName(columnName) + 1;
return toDate(rows.get(row)[columnIndexByName(columnName)]);
}
@ -759,29 +758,18 @@ public class RowStub {
return (String) o;
}
/**
* Returns a java.util.Date. It may be used for dates or times.
*/
private Date toDate(Object o) {
private LocalDateTime toDate(Object o) {
if (o instanceof String) {
String s = (String) o;
if (s.length() == "yyyy-MM-dd".length()) {
try {
return new SimpleDateFormat("yyyy-MM-dd").parse(s);
} catch (ParseException e) {
throw new DatabaseException("Could not parse date as yyyy-MM-dd for " + s);
}
return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) {
try {
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);
}
return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss"));
}
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;
import java.util.Date;
import java.time.LocalDateTime;
/**
* Simple bean for use with SampleDao.
@ -13,7 +13,7 @@ public class Sample {
private Integer updateSequence;
private Date updateTime;
private LocalDateTime updateTime;
public Long getSampleId() {
return sampleId;
@ -39,11 +39,11 @@ public class Sample {
this.updateSequence = updateSequence;
}
public Date getUpdateTime() {
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
}

View file

@ -2,6 +2,7 @@ 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,7 +20,7 @@ public class SampleDao {
public void createSample(final Sample sample, Long userIdMakingChange) {
Database db = dbp.get();
Date updateTime = db.nowPerApp();
LocalDateTime updateTime = db.nowPerApp();
Long sampleId = db.toInsert(
"insert into sample (sample_id, sample_name, update_sequence, update_time) values (?,?,0,?)")
.argPkSeq("id_seq")
@ -49,7 +50,7 @@ public class SampleDao {
result.setSampleId(sampleId);
result.setName(r.getStringOrNull());
result.setUpdateSequence(r.getIntegerOrNull());
result.setUpdateTime(r.getDateOrNull());
result.setUpdateTime(r.getLocalDateTimeOrNull());
return result;
});
}
@ -60,7 +61,7 @@ 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;
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,"
+ " is_deleted) values (?,?,?,?,?,'N')")
.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)
// if someone else modified the row. This is an optimistic locking strategy.
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,"
+ " is_deleted) values (?,?,?,?,?,'Y')")
.argLong(sample.getSampleId())