如何将多个时间戳作为动态参数传递到Derby查询?

时间:2014-03-07 16:36:02

标签: java sql prepared-statement derby

我正在将一个Java应用程序从PostGresSQL转换为Derby(10.10.1.1)。 PG数据库有许多理想将转移到Derby程序的程序。

其中一个PG存储过程传递一个Timestamps数组,类似于这个Procedure / SQL:

CREATE FUNCTION getDownloads(_download_array timestamp without time zone[])
    LANGUAGE plpgsql AS $$
DECLARE mycurs refcursor;
    BEGIN
        SELECT * FROM download_time d
        WHERE d.downloadtime = ANY(_download_array);
    END
RETURN mycurs;

Derby过程基本上是声明,它引用包含公共静态Java方法的过程类。这些方法通常使用java.SQL PreparedStatement对象,并且可能包含动态参数。该过程通过java.SQL CallableStatement对象调用,并使用set param值执行以返回ResultSet。

我想将上面的PG过程转换为接受多个Timestamp值的Derby过程,可能使用ANY或IN语句。在有限的搜索中,似乎Derby不支持arrays as dynamic parameters

使用Squirrel SQL客户端,这种语法证明是可以接受的:

SELECT * FROM download_time d 
WHERE d.downloadtime 
IN('2011-11-13 13:24:00.0', '2011-11-13 13:28:00.0', '2014-05-06 07:08:09.0')

但实际上,将逗号分隔的时间戳传递给IN或ANY语句不起作用,伪代码如下:

try {
    Connection conn = getConnection();
    CallableStatement cstmt = null;
    cstmt = conn.prepareCall("{ call getDownloads(?) }");
    cstmt.setTimestamp(3, "'2011-11-13 13:24:00.0', '2011-11-13 13:28:00.0'");
    //Also tried this:
    cstmt.setString(3, "2011-11-13 13:24:00.0, 2011-11-13 13:28:00.0");

    cstmt.execute();
    rs = cstmt.getResultSet();
    while (null != rs && rs.next()) {
            ...
            }
    } catch (SQLException sqle) {
      ...handle errors
            }

按照上面的示例,会发生此错误:

java.sql.SQLException中:
日期/时间值的字符串表示的语法不正确。

我正在寻找替代方法,并且正在考虑我在StackOverflow上的一篇优秀文章中找到的解决方案,PreparedStatement IN clause alternatives? 我愿意考虑简单地编写动态SQL而不是参数化过程,但真正的查询是相当野蛮的。 :)

1 个答案:

答案 0 :(得分:0)

由于没有人提供答案,我发布了解决问题的方法。解决方案是传递一个String变量," downloadTimes"包含以逗号分隔的格式的连接日期/时间。为简洁起见,排除了NULL检查条件。如果传递NULL,则只排除该行。

以下是程序:

public static void getDownloads(int theId, String downloadTimes, ResultSet[] rs)
throws SQLException {
    String DML = null;
    PreparedStatement ps = null;
    DML = "SELECT d.* FROM download_time d WHERE d.id = ? " + 
    "AND d.downloadtime IN(" + downloadTimes + ") " : "") + //Add chk null condition
"ORDER BY 1, 2 DESC, 3 ";
    ps = conn.prepareStatement(DML);
    ps.setInt(1, theId);
    rs[0] = ps.executeQuery();
    }

请注意" getDownloads"过程在稍后的同一个类中在Derby中声明(请参阅我原始问题中的声明),为简单起见省略。该过程由不同类中的方法调用:

public Map<GregorianCalendar, List<Fault>> getDownloadFaultList(
        Integer theId, String subsystem, List<GregorianCalendar> downloadTimes) {
    CallableStatement cstmt = null;
    ResultSet rs = null;
    String downloadCalListToCsv = null;

    // parseGregorianCalListToCsv() creates a CSV string out of dates.
    // I.e., "2011-11-13 13:24:00.0, 2011-11-13 13:28:00.0"
    if (false == downloadTimes.isEmpty()) {
            downloadCalListToCsv = DataTypeConverter
            .parseGregorianCalListToCsv(downloadTimes, timestampFormat);
    }
    try {
        cstmt = getConn().prepareCall("{ call getDownloads(?, ?) }");

        // Register the parameters
        cstmt.setInt(1, theId);

        // Get timezone from first entry, assuming all same timezone
        if (! downloadTimes.isEmpty()) {
            cal.setTimeZone(downloadTimes.get(0).getTimeZone());
        }
        cstmt.setString(2, downloadCalListToCsv);
        cstmt.execute();
        rs = cstmt.getResultSet();
        while (null != rs && rs.next()) {
            //Use the download timestamps here
        }
    } catch (SQLException sqle) {
        //error handling here
    } finally {
        //Close resources
        close(rs, cstmt);
    }
    return faultMap;
}

解决方案不优雅,但在实践中有效。