当Java是没有时区的"时间戳时,参数的类型应该是什么?#34;在postgresql?

时间:2015-02-03 07:15:38

标签: java postgresql jdbc

我正在尝试通过Java和JDBC在po​​stgresql中运行查询。

&#34;活动日期&#34; &#34;表&#34;的列在postgresql中是&#34;时区没有时区&#34;。我尝试使用String来传递日期,但没有工作( org.postgresql.util.PSQLException:ERROR:运算符不存在:没有时区的时间戳&gt;字符变化)。< / p>

我应该用什么类型代替字符串来存储&#34; start_date&#34;和&#34; end_date&#34;并将它们传递给查询?

BTW SELECT COUNT(*) FROM table WHERE activitydate > '2014-11-20' AND activitydate < '2014-11-21' AND zipcode = 12345在pgadmin中完美运行。

public Response query(
          @QueryParam("start_date") String start_date,
          @QueryParam("end_date") String end_date,
          @QueryParam("zipcode") String zipcode
          ) throws Exception {
...
...

pstmt = conn.prepareStatement("SELECT COUNT(*) FROM table WHERE activitydate > ? AND activitydate < ? AND zipcode = ?");

pstmt.setString(1, start_date); 
pstmt.setString(2, end_date); 
pstmt.setString(3, zipcode); 

更新: 我将其更改为java.sql.Timestamp但仍然无法工作:

public Response query(
          @QueryParam("start_date") Timestamp start_date,
          @QueryParam("end_date") Timestamp end_date,
          @QueryParam("zipcode") String zipcode
          ) throws Exception {
...
...

pstmt = conn.prepareStatement("SELECT COUNT(*) FROM table WHERE activitydate > ? AND activitydate < ? AND zipcode = ?");

pstmt.setTimestamp(1, start_date); 
pstmt.setTimestamp(2, end_date); 
pstmt.setString(3, zipcode);

2 个答案:

答案 0 :(得分:3)

您必须使用java.sql.Timestampjava.sql.Date

答案 1 :(得分:1)

tl; dr

对于使用半开时间跨度的SQL:
"SELECT * FROM tbl WHERE when !< ? AND when < ? ; "

myPreparedStatement.setObject(       // Use a prepared statement so you can pass smart objects rather than dumb strings.
    1 ,                              // Specify which placeholder is being fulfilled.
    LocalDate                        // Represent a date-only value, without time-of-day and without time zone or offset-from-UTC.
    .parse( "2014-11-20" )           // Parse an input string in standard ISO 8601 format to get a `LocalDate` object.
    .atStartOfDay()                  // Determine the first moment of the day on that date. Returns a `LocalDateTime` object representing a date with time-of-day but lacking any concept of time zone or offset-from-UTC.
) ;
myPreparedStatement.setObject( 
    2 , 
    LocalDate
    .parse( "2014-11-21" )
    .atStartOfDay()
) ;

当心:我猜您在数据库中使用的列类型错误。 只能使用TIMESTAMP WITH TIME ZONE 跟踪时刻,不能 TIMESTAMP WITHOUT TIME ZONE跟踪时刻。搜索堆栈溢出以获取更多信息。

不要紧

Jens的答案现在已过时。如今,您应该使用现代的 java.time 类,该类取代了麻烦的旧的传统日期时间类。

  

postgresql中的“无时区的时间戳”?

Postgres和SQL标准上的此数据类型故意缺少从UTC偏移或时区的上下文。因此,请注意,这种类型不能表示时刻,不是时间线上的一点。

  

我将其更改为java.sql.Timestamp

不是,数据类型错误。 java.sql.Timestamp类不仅是传统,而且在设计上存在严重缺陷,它代表时刻,是时间轴上的特定点。因此,这与您的TIMESTAMP WITHOUT TIME ZONE类型的列不匹配。

LocalDateTime

您应该改用LocalDateTime类。此类表示带有日期的日期,但是缺少偏移或时区的任何概念。

要从数据库中检索值。

LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;

向数据库发送值。

myPreparedStatement.setObject( … , ldt ) ;

一天的开始

  

从表中选择COUNT(*)个活动日期>'2014-11-20'和活动日期<'2014-11-21'

您在这里还有另一个不匹配之处。您提供的是仅日期的值,但是您的列中包含日期与时间的值。

另一个问题:您在应使用智能对象的地方使用了哑字符串。从JDBC 4.2开始,我们可以与数据库交换 java.time 对象。将PreparedStatement与占位符一起使用,并通过`set

传递对象

Table of date-time types in Java (both legacy and modern) and in standard SQL

要解决带有日期时间的日期问题,我们需要确定一天的开始时间。使用LocalDateTime,我们无需考虑任何时区异常。因此,一天总是从00:00开始。不过,我们应该养成要求 java.time 确定一天中开始时间的习惯。

LocalDate startDate = LocalDate.parse( "2014-11-20" ) ;
LocalDate stopDate = LocalDate.parse( "2014-11-21" ) ;

LocalDateTime start = startDate.atStartOfDay() ;
LocalDateTime stop = stopDate.atStartOfDay() ;

使用占位符为您编写SQL。

我建议您改变思维方式,以习惯于定义时间跨度的半开放式方法。在Half-Open中,开头为包含,而结尾为专有。因此,对于20日的一整天,请搜索等于或晚于20日的日期,直到(包括)21日为止。对于第一部分,说“等于或晚于”等于“不早于”,因此我们使用!<

String sql = "SELECT * FROM tbl WHERE when !< ? AND when < ? ; " ;

sql字符串输入到准备好的语句中。然后为占位符传递两个LocalDateTime对象。

myPreparedStatement.setObject( 1 , start ) ;
myPreparedStatement.setObject( 2 , stop ) ;