【问题标题】:JDBC ResultSet: I need a getDateTime, but there is only getDate and getTimeStampJDBC ResultSet:我需要一个 getDateTime,但只有 getDate 和 getTimeStamp
【发布时间】:2014-02-05 10:00:48
【问题描述】:

我想使用 JDBC 从 Oracle 数据库表中获取 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,但它实际上是数据库中的 DATETIME,因为它具有以下格式:“07.05.2009 13:49:32”

getDate 截断时间:“07.05.2009” getString 将“.0”附加到它:“07.05.2009 13:49:32.0”

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

有什么想法吗?我正在寻找一个 getDateTime 方法。

【问题讨论】:

  • 很确定getTimestamp() 应该可以满足您的需求。
  • 那么类似于 if(columnType == Types.DATE) 然后使用 rs.getTimestamp(i) ?我会尝试 - 但这不会破坏不同类型的目的吗?
  • 我刚试过,没用——它返回的结果和getString一样:valueToInsert = 2009-05-07 13:49:32.0
  • 你为什么要转换成字符串?从 ResultSet 中检索到该值后,我真的不明白您要如何处理它。
  • 我把它写在一个文本文件中。此代码是报告生成软件的一部分。其他人在其他地方使用的报告,包括导入其他数据库(这就是漏洞被发现的方式)

标签: java sql oracle resultset getdate


【解决方案1】:

这个答案已经过时了。继续Basil Bourque's answer

java.util.Date date;
Timestamp timestamp = resultSet.getTimestamp(i);
if (timestamp != null)
    date = new java.util.Date(timestamp.getTime()));

然后按照你喜欢的方式格式化它。

【讨论】:

  • 一个简单的演员阵容不也可以吗? public class Timestamp extends java.util.Date
  • 使用此答案时请注意时区。某些驱动程序(绝对是 JTDS)的 getTimestamp() 可以默认为当前计算机的时区,如果您的数据库将时间戳存储为年-月-日时:分:秒,这将使您得到错误的值。请注意,有一个超载的日历可能不会出现问题。
  • @Trejkaz 你建议如何克服你提到的问题?
  • @theyuv 我们打算通过将所有时间戳列切换到BIGINT 并在客户端代码中使用Instant 来避免它,这样时区就不必出现在图片中。 (而且,正如我已经提到的,有 Calendar 重载,有些人声称它适用于某些数据库,但我更愿意尽可能放弃旧的日期/时间 API。)
  • 但是你仍然需要知道 bigint 值的时区,不是吗?
【解决方案2】:

answer by Leos Literak 是正确的,但现在已过时,使用的是麻烦的旧日期时间类之一,java.sql.Timestamp

tl;博士

这确实是数据库中的 DATETIME

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

我正在寻找一个 getDateTime 方法。

在 JDBC 4.2 及更高版本中使用 java.time 类,而不是在您的问题中看到麻烦的遗留类。特别是,暂时不要使用java.sql.TIMESTAMP,而是使用Instant 类,例如SQL 标准类型TIMESTAMP WITH TIME ZONE

人为的代码sn-p:

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

奇怪的是,JDBC 4.2 规范不需要支持两个最常用的 java.time 类,InstantZonedDateTime。因此,如果您的 JDBC 不支持上述代码,请改用 OffsetDateTime

OffsetDateTime offsetDateTime = myResultSet.getObject( … , OffsetDateTime.class ) ;

详情

我想使用 JDBC 从 Oracle 数据库表中获取 DATETIME 列。

根据this doc,Oracle数据库中没有列数据类型DATETIME。该术语似乎是 Oracle 将所有日期时间类型称为一个组的词。

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

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

valueToInsert = aDate.toString();

您似乎试图以文本形式与数据库交换日期时间值,例如 String 对象。别。

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

各种类型系统

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

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

SQL 标准类型

SQL 标准defines five types

  • DATE
  • TIME WITHOUT TIME ZONE
  • TIME WITH TIME ZONE
  • TIMESTAMP WITHOUT TIME ZONE
  • TIMESTAMP WITH TIME ZONE

仅限日期

  • DATE
    只有日期,没有时间,没有时区。

仅限时间

  • TIMETIME WITHOUT TIME ZONE
    只有时间,没有日期。静默忽略作为输入的一部分指定的任何时区。
  • TIME WITH TIME ZONE(或TIMETZ
    只有时间,没有日期。如果输入中包含足够的数据,则应用时区和夏令时规则。考虑到其他数据类型(如 discussed in Postgres doc),其有用性值得怀疑。

日期和时间

  • TIMESTAMPTIMESTAMP WITHOUT TIME ZONE
    日期和时间,但忽略时区。传递给数据库的任何时区信息都会被忽略,不会对 UTC 进行调整。因此,这代表时间轴上的特定时刻,而是代表大约 26-27 小时内的一系列可能时刻。如果时区或偏移量 (a) 未知或 (b) 不相关,例如“我们在世界各地的所有工厂在中午关闭午餐”,请使用此选项。 If you have any doubts, not likely the right type
  • TIMESTAMP WITH TIME ZONE(或TIMESTAMPTZ
    与时区相关的日期和时间。请注意,根据实现的不同,这个名称有点用词不当。某些系统可能会存储给定的时区信息。在其他系统(例如Postgres)中,时区信息存储,而是使用传递到数据库的时区信息将日期时间调整为 UTC。

专有

许多数据库提供自己的日期时间相关类型。专有类型变化很大。有些是应该避免的旧的遗留类型。供应商认为有些可以提供某些好处;您决定是否仅使用标准类型。注意:某些专有类型的名称与标准类型冲突;我在看着你Oracle DATE

JDBC

Java 平台处理日期时间的内部细节与 SQL 标准或特定数据库不同。 JDBC driver 的工作是在这些差异之间进行调解,充当桥梁,根据需要翻译类型及其实际实现的数据值。 java.sql.* package 就是那座桥。

JDBC 遗留类

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

  • java.sql.Date
    模拟只有日期,假装没有时间,没有时区。可能会令人困惑,因为此类是 java.util.Date 的包装器,它跟踪日期 时间。在内部,时间部分设置为零(UTC 午夜)。
  • java.sql.Time
    只有时间,假装没有日期,没有时区。也可能令人困惑,因为这个类也是 java.util.Date 的一个薄包装器,它跟踪日期 时间。在内部,日期设置为零(1970 年 1 月 1 日)。
  • java.sql.TimeStamp
    日期和时间,但没有时区。这也是 java.util.Date 的薄包装。

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

请注意,第一个缺少任何时区或与 UTC 偏移的概念。最后一个,java.sql.Timestamp 始终使用 UTC,尽管它的 toString 方法告诉你什么。

JDBC 现代类

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

  • 请使用LocalDate,而不是java.sql.Date。适合 SQL 标准的 DATE 类型。
  • 使用LocalTime 代替java.sql.Time。适合 SQL 标准的 TIME WITHOUT TIME ZONE 类型。
  • 请使用Instant,而不是java.sql.Timestamp。适合 SQL 标准的 TIMESTAMP WITH TIME ZONE 类型。

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

插入/更新。

myPreparedStatement.setObject( … , instant ) ;

检索。

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

Instant 类表示UTC 中时间轴上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

调整时区

如果您想通过特定地区(时区)的人们使用的挂钟时间而不是 UTC 来查看 Instant 的时刻,请通过应用 ZoneId 进行调整以获得ZonedDateTime 对象。

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

生成的ZonedDateTime 对象是同一时刻,时间轴上的同一同时点。新的一天在东方更早黎明,因此日期和时间会有所不同。例如,新西兰的午夜过后几分钟仍然是 UTC 中的“昨天”。

您可以将另一个时区应用于InstantZonedDateTime,以通过其他地区的人们使用的另一个挂钟时间查看同一时刻。

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

所以现在我们有了三个对象(instantzdtAucklandzMontréal),它们都代表时间轴上的同一时刻、同一点。

检测类型

回到问题中关于检测数据库数据类型的代码:(a)不是我的专业领域,(b)我会避免上面提到的这种情况,以及(c)如果你坚持请注意,从 Java 8 及更高版本开始,java.sql.Types 类已过时。该类现在被一个正确的Java Enum 替换为JDBCType,它实现了新的接口SQLType。有关相关问题,请参阅 this Answer

此更改列于JDBC Maintenance Release 4.2,第 3 和 4 节。引用:

增加 java.sql.JDBCType 枚举

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

枚举与旧类具有相同的值,但现在提供type-safety

关于语法的说明:在现代 Java 中,您可以在 Enum 对象上使用 switch。因此,无需使用您的问题中看到的级联 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 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore


更新:Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。本节原封不动地保留为历史。

乔达时间

在 Java 8(java.time.* 包)之前,与 java 捆绑的日期时间类(java.util.Date & Calendar、java.text.SimpleDateFormat)出了名的麻烦、混乱和缺陷。

更好的做法是获取 JDBC 驱动程序提供的内容并从中创建 Joda-Time 对象,或者在 Java 8 中,java.time.* package。最终,您应该会看到自动使用新 java.time.* 类的新 JDBC 驱动程序。在此之前,一些方法已添加到 java.sql.Timestamp 等类中以插入 java.time,例如 toInstantfromInstant

字符串

至于问题的后半部分,渲染一个String……应该使用formatter对象来生成字符串值。

老式的方法是使用 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

【讨论】:

  • 我将 JTDS 与 MSSQL 一起使用,当我执行 rs.getString("000123450") 时,它会删除所有前导零并在输出中给我 123450。我怎样才能得到像 000123450 这样的整个值?有什么帮助吗?谢谢,Harshita
  • @user4567570 请重新阅读我的回答。通常,您不应该将字符串用于日期时间工作。在数据库中使用日期时间类型,在 JDBC 代码中使用日期时间类型 (java.sql.*),在 Java 代码中使用日期时间类型 (java.time.*)。这是一般建议,因为我不熟悉 JTDS 和 MSSQL。
  • Java 8 的更新,因为它现在已经成为现实,这将使这个答案真正闪耀......轻推轻推眨眼。
  • @Namphibian 问,你会收到的。
  • 我知道这是几年后的事了,但对于其他找到这个答案的人来说......这是一个很好的答案,但有一个问题。 java.time.Instant 的用途很少。作为一般规则,不要使用它。请改用 OffsetDateTime。 Instant 与 UTC 时间标准基本相同(不是 UTC 零偏移时区)。 OffsetDateTime 是指日历和时钟指示的时间。这与 UTC 不同。几乎所有时间的使用都与日历和时钟时间有关,而不是原子时间标准。 OffsetDateTime 也更有能力。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    这行得通:

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

    使用 SimpleDateFormat。

    【讨论】:

      猜你喜欢
      • 2011-03-17
      • 1970-01-01
      • 2012-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多