java-JDBC ResultSet:我需要一个getDateTime,但是只有getDate和getTimeStamp

我想从带有JDBC的Oracle DB表中获取DATETIME列。 这是我的代码:

int columnType = rsmd.getColumnType(i);
if(columnType == Types.DATE)
{
    Date aDate = rs.getDate(i);
    valueToInsert = aDate.toString();
}
else if(columnType == Types.TIMESTAMP)
{
    Timestamp aTimeStamp = rs.getTimestamp(i);
    valueToInsert = aTimeStamp.toString();
}
else
{
    valueToInsert = rs.getString(i);
}

我必须先确定列类型。 我感兴趣的字段被识别为Types.DATE,但实际上它是DB中的DATETIME,因为它具有以下格式:“ 07.05.2009 13:49:32”

getDate缩短时间:“ 07.05.2009”和getString附加“ .0”到它:“ 07.05.2009 13:49:32.0”

当然,我可以只删除最终的.0并始终使用getString,但这是一个肮脏的解决方法。

有任何想法吗 ? 我一直在寻找getDateTime方法。

干杯,提姆

Tim asked 2020-07-12T02:50:23Z
4个解决方案
87 votes
java.util.Date date;
Timestamp timestamp = resultSet.getTimestamp(i);
if (timestamp != null)
    date = new java.util.Date(timestamp.getTime()));

然后以您喜欢的方式对其进行格式化。

Leos Literak answered 2020-07-12T02:50:31Z
39 votes

Leos Literak的答案是正确的,但现在已经过时了,使用了麻烦的旧日期时间类之一getTime

tl;博士

这实际上是数据库中的DATETIME

不,不是。 Oracle数据库中没有getTime这样的数据类型。

我一直在寻找getDateTime方法。

在JDBC 4.2和更高版本中使用java.time类,而不要在Question中看到麻烦的旧类。 特别是,暂时不要使用getTime,而是使用TIMESTAMPTZ类,例如SQL标准类型TIMESTAMP WITH TIME ZONE

人为设计的代码段:

if( 
    JDBCType.valueOf( 
        myResultSetMetaData.getColumnType( … )
    )
    .equals( JDBCType.TIMESTAMP_WITH_TIMEZONE ) 
) {
    Instant instant = myResultSet.getObject( … , Instant.class ) ;
}

细节

我想从带有JDBC的Oracle DB表中获取DATETIME列。

根据此文档,Oracle数据库中没有列数据类型getTime。 该术语似乎是Oracle的用语,将其所有日期时间类型作为一个组引用。

我看不到您的代码可以检测类型并在其上分支的数据类型。 通常,我认为您应该在特定表和特定业务问题的上下文中显式地编写代码。 也许这在某种通用框架中很有用。 如果您坚持要求,请继续阅读以了解各种类型,并了解Java 8和更高版本中内置的极其有用的新java.time类,这些类取代了Question中使用的类。

智能对象,而不是哑字符串

valueToInsert = aDate.toString();

您似乎试图与数据库以文本形式交换日期时间值(作为getTime对象)。 别。

要与数据库交换日期时间值,请使用日期时间对象。 现在在Java 8和更高版本中,这意味着java.time对象,如下所述。

各种类型的系统

您可能会混淆三组与日期时间相关的数据类型:

  • 标准SQL类型
  • 专有类型
  • JDBC类型

SQL标准类型

SQL标准定义了五种类型:

  • getTime
  • getTime
  • getTime
  • getTime
  • getTime

仅日期

  • getTime
    仅日期,没有时间,没有时区。

只有一天中的时间

  • getTimeTIMESTAMPTZ
    只有时间,没有日期。 静默忽略指定为输入一部分的任何时区。
  • getTime(或TIMESTAMPTZ
    只有时间,没有日期。 如果输入中包含足够的数据,则应用时区和夏时制规则。 如Postgres文档中所述,对于其他数据类型,其有用性令人质疑。

日期和时间

  • getTimeTIMESTAMPTZ
    日期和时间,但忽略时区。 传递给数据库的任何时区信息都将被忽略,不会对UTC进行任何调整。 因此,这并不代表时间轴上的特定时刻,而是代表大约26-27小时内的一系列可能时刻。 如果时区或偏移量是(a)未知或(b)不相关的,例如“我们在世界各地的所有工厂都在中午午餐时使用”,请使用此选项。 如果您有任何疑问,不太可能是正确的类型。
  • getTime(或TIMESTAMPTZ
    关于时区的日期和时间。 请注意,根据实现方式,此名称有点用词不当。 某些系统可能会存储给定的时区信息。 在其他系统(例如Postgres)中,不存储时区信息,而是使用传递到数据库的时区信息将日期时间调整为UTC。

所有权

许多数据库提供了自己的日期时间相关类型。 专有类型差异很大。 一些旧的,遗留的类型应该避免。 供应商相信其中一些提供某些好处; 您可以决定是否只使用标准类型。 注意:某些专有类型的名称与标准类型冲突; 我在找你Oracle getTime

JDBC

Java平台处理日期时间的内部细节的方式与SQL标准或特定数据库的方式不同。 JDBC驱动程序的工作是在这些差异之间进行中介,充当桥梁,根据需要转换类型及其实际实现的数据值。 java.sql。*包就是那个桥梁。

JDBC旧版类

在Java 8之前,JDBC规范为日期时间工作定义了3种类型。 前两个是与版本8之前一样的黑客,Java缺少任何类来表示仅日期或仅时间值。

  • java.sql.Date
    模拟仅日期,假装没有时间,没有时区。 此类可能会混淆java.util.Date,该类同时跟踪日期和时间,因此可能会造成混淆。 在内部,时间部分设置为零(UTC午夜)。
  • java.sql.Time
    仅时间,假装没有日期,没有时区。 也可能会引起混淆,因为此类也是围绕java.util.Date的瘦包装器,该包装器同时跟踪日期和时间。 在内部,日期设置为零(1970年1月1日)。
  • java.sql.TimeStamp
    日期和时间,但没有时区。 这也是java.util.Date的一个瘦包装。

这样就可以回答您有关ResultSet接口中没有“ getDateTime”方法的问题。 该接口为JDBC中定义的三种桥接数据类型提供了getter方法:

  • 适用于java.sql.Date的getTime
  • 适用于java.sql.Time的getTime
  • 适用于java.sql.Timestamp的fromInstant

请注意,第一个缺少时区或从UTC偏移的概念。 最后一个fromInstant始终使用UTC,尽管其Enum方法可以告诉您。

JDBC现代类

您应该避免上面列出的那些设计欠佳的JDBC类。 它们被java.time类型取代。

  • 代替fromInstant,使用Enum。适合SQL标准2962597075453740040034类型。
  • Instead of fromInstant, use Enum. Suits SQL-standard switch type.
  • Instead of fromInstant, use Enum. Suits SQL-standard switch type.

从JDBC 4.2及更高版本开始,您可以直接与数据库交换java.time对象。 使用fromInstant/Enum方法。

插入/更新。

myPreparedStatement.setObject( … , instant ) ;

恢复。

Instant instant = myResultSet.getObject( … , Instant.class ) ;

fromInstant类表示UTC时间轴上的时刻,其分辨率为纳秒(最多十进制的九(9)位数字)。

调整时区

如果要通过特定区域(时区)的人(而不是UTC)的人使用挂钟时间查看fromInstant的时刻,请应用Enum进行调整以获取switch对象。

ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;

结果2962597075453753740032对象是时间轴上的相同时刻,相同同时点。 新的一天比东方早得多,因此日期和时间会有所不同。 例如,在纽西兰午夜后的几分钟仍然是UTC的“昨天”。

您可以将另一个时区应用于29625970754537400400或2962597075453740040033,以查看其他地区人们使用的另一壁钟时间中的同一时刻。

ZoneId zMontréal = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdtMontréal = zdtAuckland.withZoneSameInstant( zMontréal ) ;  // Or, for the same effect: instant.atZone( zMontréal ) 

因此,现在我们有了三个对象(fromInstantEnum、2962597075453753740034),它们全部代表时间轴上的同一时刻,同一点。

检测类型

回到“问题”中有关检测数据库数据类型的代码:(a)不是我的专业领域,(b)我会避免像上面提到的那样,并且(c)如果您坚持这样做,请当心 从Java 8及更高版本开始,该fromInstant类已过时。 现在,该类已由fromInstant的适当Java fromInstant代替,该Java实现新接口fromInstant。请参阅对相关问题的解答。

JDBC维护版本4.2的第3和4节中列出了此更改。要引用:

java.sql.JDBCType枚举的添加

一个用于标识通用SQL类型的Enum,称为JDBC类型。 目的是使用JDBCType代替Types.java中定义的常量。

枚举具有与旧类相同的值,但是现在提供了类型安全性。

关于语法的注释:在现代Java中,可以在2962597075453740040033对象上使用fromInstant。 因此,无需使用在Question中看到的级联if-then语句。 一个陷阱是,出于某种晦涩的技术原因,在切换时必须使用无限制的枚举对象的名称,因此您必须在TIMESTAMP_WITH_TIMEZONE上执行switch,而不是合格的JDBCType.TIMESTAMP_WITH_TIMEZONE。使用static import语句。

因此,这就是说,我猜(我尚未尝试),您可以执行类似以下代码示例的操作。

final int columnType = myResultSetMetaData.getColumnType( … ) ;
final JDBCType jdbcType = JDBCType.valueOf( columnType ) ;

switch( jdbcType ) {  

    case DATE :  // FYI: Qualified type name `JDBCType.DATE` not allowed in a switch, because of an obscure technical issue. Use a `static import` statement.
        …
        break ;

    case TIMESTAMP_WITH_TIMEZONE :
        …
        break ;

    default :
        … 
        break ;

}

关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧旧式日期时间类,例如fromInstant、29625970754537400400和fromInstant

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参见Oracle教程。 并在Stack Overflow中搜索许多示例和说明。 规格为JSR 310。

您可以直接与数据库交换java.time对象。 使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。 不需要字符串,不需要fromInstant类。

在哪里获取java.time类?

  • Java SE 8,Java SE 9,Java SE 10和更高版本
    • 内置的
    • 标准Java API的一部分,具有捆绑的实现。
    • Java 9添加了一些次要功能和修复。
  • Java SE 6和Java SE 7
    • 很多java.time功能都在ThreeTen-Backport中反向移植到Java 6和7。
  • 安卓系统
    • 更高版本的Android捆绑了java.time类的实现。
    • 对于早期的Android(<26),ThreeTenABP项目改编了ThreeTen-Backport(如上所述)。 请参阅如何使用ThreeTenABP…。

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目为将来可能在java.time中添加内容提供了一个试验场。 您可能会在这里找到一些有用的类,例如fromInstantfromInstantfromInstant等。


更新:现在处于维护模式的Joda-Time项目建议迁移到java.time类。 此部分保留为历史记录。

乔达时代

在Java 8(java.time。*软件包)之前,与Java捆绑在一起的日期时间类(java.util.Date和Calendar,java.text.SimpleDateFormat)众所周知是麻烦,混乱和有缺陷的。

更好的做法是采用JDBC驱动程序提供的功能,并从中创建Joda-Time对象,或者使用Java 8,java.time。*包。 最终,您应该看到自动使用新的java.time。*类的新的JDBC驱动程序。 在此之前,已将某些方法添加到类(例如java.sql.Timestamp)中,以与java.time进行交互,例如fromInstantfromInstant

至于问题的后半部分,则呈现一个String…格式化程序对象应用于生成字符串值。

老式的方法是使用java.text.SimpleDateFormat。 不建议。

Joda-Time提供了各种内置的格式化程序,您也可以定义自己的格式化程序。 但是,如您所述,要写入日志或报告,最好的选择是ISO 8601格式。 该格式恰好是Joda-Time和java.time使用的默认格式。

范例程式码

//java.sql.Timestamp timestamp = resultSet.getTimestamp(i);
// Or, fake it 
// long m = DateTime.now().getMillis();
// java.sql.Timestamp timestamp = new java.sql.Timestamp( m );

//DateTime dateTimeUtc = new DateTime( timestamp.getTime(), DateTimeZone.UTC );
DateTime dateTimeUtc = new DateTime( DateTimeZone.UTC ); // Defaults to now, this moment.

// Convert as needed for presentation to user in local time zone.
DateTimeZone timeZone = DateTimeZone.forID("Europe/Paris");
DateTime dateTimeZoned = dateTimeUtc.toDateTime( timeZone );

转储到控制台...

System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "dateTimeZoned: " + dateTimeZoned );

运行时...

dateTimeUtc: 2014-01-16T22:48:46.840Z
dateTimeZoned: 2014-01-16T23:48:46.840+01:00
Basil Bourque answered 2020-07-12T03:00:04Z
0 votes

这工作:

    Date date = null;
    String dateStr = rs.getString("doc_date");
    if (dateStr != null) {
        date = dateFormat.parse(dateStr);
    }

使用SimpleDateFormat。

Nikolay Kombarov answered 2020-07-12T03:00:28Z
0 votes

我选择的解决方案是使用mysql query格式化日期:

String l_mysqlQuery = "SELECT DATE_FORMAT(time, '%Y-%m-%d %H:%i:%s') FROM uld_departure;"
l_importedTable = fStatement.executeQuery( l_mysqlQuery );
System.out.println(l_importedTable.getString( timeIndex));

我有完全相同的问题。即使我的mysql表包含格式如下的日期:2017-01-01 21:02:50

String l_mysqlQuery = "SELECT time FROM uld_departure;"
l_importedTable = fStatement.executeQuery( l_mysqlQuery );
System.out.println(l_importedTable.getString( timeIndex));

返回的日期格式如下:2017-01-01 21:02:50.0

Albizia answered 2020-07-12T03:00:57Z
translate from https://stackoverflow.com:/questions/21162753/jdbc-resultset-i-need-a-getdatetime-but-there-is-only-getdate-and-gettimestamp