我正在尝试通过Java和JDBC在PostgreSQL中运行查询。
postgresql中“表”的“活动日期”列为“无时区的时间戳”。我尝试使用String传递日期,但没有成功(org.postgresql.util.PSQLException:错误:运算符不存在:没有时区的时间戳>字符变化)。
我应该使用什么类型而不是String来存储“开始日期”和“结束日期”并将它们传递给查询?
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);
您必须使用java.sql.Timestamp
或java.sql.Date
。
对于使用半开时间跨度的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 ) ;
一天的开始
从表WHERE活动日期>'2014-11-20'和活动日期
您在这里还有另一个不匹配之处。您提供的是仅日期的值,但是您的列中包含日期与时间的值。另一个问题:您在应使用智能对象的地方使用了哑字符串。从JDBC 4.2开始,我们可以与数据库交换
java.time
对象。使用带占位符的PreparedStatement
,并通过`set >>传递对象要解决日期中的时间问题,我们需要确定一天的开始时间。使用
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中,开头为,而结尾为inclusive
exclusive
。因此,对于20日的一整天,请搜索等于或晚于20日的日期,直到(包括)21日为止。至于第一部分,说“等于或晚于”等于“不早于”,因此我们使用!<
。String sql = "SELECT * FROM tbl WHERE when !< ? AND when < ? ; " ;
将sql
字符串输入到准备好的语句中。然后将两个LocalDateTime
对象传递给占位符。myPreparedStatement.setObject( 1 , start ) ; myPreparedStatement.setObject( 2 , stop ) ;