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

时间:2014-01-16 13:10:04

标签: java sql oracle resultset getdate

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

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

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

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

干杯, 添

4 个答案:

答案 0 :(得分:83)

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

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

答案 1 :(得分:37)

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

受控代码段:

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

详细

  

我想从带有JDBC的Oracle DB表中获取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之前的黑客攻击,Java缺少任何类来表示仅限日期或仅限时间的值。

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

这样就可以回答你关于no" getDateTime"的问题。 ResultSet interface中的方法。该接口为JDBC中定义的三种桥接数据类型提供getter methods

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

JDBC现代类

您应该避免使用上面列出的设计糟糕的JDBC类。它们被 java.time 类型取代。

  • 使用java.sql.Date而不是LocalDate。适合SQL标准DATE类型。
  • 使用java.sql.Time而不是LocalTime。适合SQL标准TIME WITHOUT TIME ZONE类型。
  • 使用java.sql.Timestamp而不是Instant。适合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的时刻,请通过应用{{1}进行调整获取ZoneId对象。

ZonedDateTime

生成的ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ; ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ; 对象是同一时刻,即时间轴上的同一点。新的一天早些时候在东方开始,所以日期和时间会有所不同。例如,新西兰午夜后的几分钟仍然是UTC的“昨天”。

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

ZonedDateTime

现在我们有三个对象(ZoneId zMontréal = ZoneId.of( "America/Montreal" ) ; ZonedDateTime zdtMontréal = zdtAuckland.withZoneSameInstant( zMontréal ) ; // Or, for the same effect: instant.atZone( zMontréal ) instantzdtAuckland)都代表同一时刻,时间轴上的同一点。

检测类型

回到有关检测数据库数据类型的问题中的代码:(a)不是我的专业领域,(b)如上所述我会避免这种情况,(c)如果你坚持这一点,请注意,从Java 8及更高版本开始,zMontréal类已经过时了。该类现在由实现新接口Java EnumJDBCType SQLType替换。请参阅this Answer相关问题。

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

  

添加java.sql.JDBCType枚举

     

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

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

关于语法的注释:在现代Java中,您可以在java.sql.Types对象上使用switch。因此,无需使用您的问题中所示的级联if-then语句。唯一的问题是,在切换某些模糊的技术原因时,必须使用enum对象的名称不合格,因此您必须在Enum而不是合格的switch上执行TIMESTAMP_WITH_TIMEZONE。使用JDBCType.TIMESTAMP_WITH_TIMEZONE声明。

所以,这就是说我猜(我还没有尝试过)你可以做类似下面的代码示例。

static import

关于 java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

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

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要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类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore


更新:现在位于Joda-Timemaintenance mode项目建议迁移到java.time课程。这部分原封不动。

约达时间

在Java 8(java.time。*包)之前,与java捆绑的日期时间类(java.util.Date&amp; Calendar,java.text.SimpleDateFormat)出了名的麻烦,令人困惑和有缺陷。< / p>

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

字符串

至于问题的后半部分,渲染String ...应该使用格式化程序对象来生成字符串值。

老式的方法是使用java.text.SimpleDateFormat。不推荐。

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

使用的默认格式

示例代码

fromInstant

转储到控制台...

//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 );

答案 2 :(得分:0)

这有效:

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

使用SimpleDateFormat。

答案 3 :(得分:0)

我选择的解决方案是使用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