【问题标题】:Using setDate in PreparedStatement在 PreparedStatement 中使用 setDate
【发布时间】:2013-09-08 00:27:51
【问题描述】:

为了使我们的代码更加标准,我们被要求将我们硬编码 SQL 变量的所有位置更改为预准备语句,并改为绑定变量。

然而,我遇到了setDate() 的问题。

代码如下:

        DateFormat dateFormatYMD = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        DateFormat dateFormatMDY = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
        Date now = new Date();
        String vDateYMD = dateFormatYMD.format(now);
        String vDateMDY = dateFormatMDY.format(now);
        String vDateMDYSQL =  vDateMDY ;
        java.sql.Date date = new java.sql.Date(0000-00-00);

   requestSQL = "INSERT INTO CREDIT_REQ_TITLE_ORDER (REQUEST_ID," + 
                " ORDER_DT, FOLLOWUP_DT) " +  "values(?,?,?,)";


                prs = conn.prepareStatement(requestSQL);

                prs.setInt(1,new Integer(requestID));

                prs.setDate(2,date.valueOf(vDateMDYSQL));
                prs.setDate(3,date.valueOf(sqlFollowupDT));

执行 SQL 时出现此错误:

    java.lang.IllegalArgumentException
    at java.sql.Date.valueOf(Date.java:138)
    at com.cmsi.eValuate.TAF.TAFModuleMain.CallTAF(TAFModuleMain.java:1211)

我应该用setString() 代替to_date() 吗?

【问题讨论】:

标签: java sql oracle jdbc prepared-statement


【解决方案1】:

tl;博士

使用 JDBC 4.2 或更高版本以及 java 8 或更高版本:

myPreparedStatement.setObject( … , myLocalDate  )

……和……

myResultSet.getObject( … , LocalDate.class )

详情

Vargas 的答案很好地提到了 java.time 类型,但仅指转换为 java.sql.Date。如果您的驱动程序已更新,则无需转换。

java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了旧的麻烦的日期时间类,例如java.util.Date.Calendarjava.text.SimpleDateFormatJoda-Time 团队还建议迁移到 java.time。

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

大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7,并在 ThreeTenABP 中进一步适应 Android。

LocalDate

在 java.time 中,java.time.LocalDate 类表示没有时间和时区的仅日期值。

如果使用符合JDBC 4.2 或更高规范的JDBC driver,则无需使用旧的java.sql.Date 类。您可以通过PreparedStatement::setObjectResultSet::getObjectLocalDate 对象直接传入/传出数据库。

LocalDate localDate = LocalDate.now( ZoneId.of( "America/Montreal" ) );
myPreparedStatement.setObject( 1 , localDate  );

……和……

LocalDate localDate = myResultSet.getObject( 1 , LocalDate.class );

在 JDBC 4.2 之前,转换

如果您的驱动程序无法直接处理 java.time 类型,请退回到转换为 java.sql 类型。但尽量减少它们的使用,您的业务逻辑仅使用 java.time 类型。

新方法已添加到旧类中,用于与 java.time 类型之间的转换。对于java.sql.Date,请参阅valueOftoLocalDate 方法。

java.sql.Date sqlDate = java.sql.Date.valueOf( localDate );

……和……

LocalDate localDate = sqlDate.toLocalDate();

占位符值

小心使用0000-00-00 作为占位符值,如问题代码中所示。并非所有数据库和其他软件都可以处理这么久的回溯。我建议使用类似 1970 年常用的 Unix/Posix epoch reference date1970-01-01

LocalDate EPOCH_DATE = LocalDate.ofEpochDay( 0 ); // 1970-01-01 is day 0 in Epoch counting.

关于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

【讨论】:

    【解决方案2】:

    ❐ 使用java.sql.Date

    如果您的表有DATE 类型的列:

    • java.lang.String

      方法java.sql.Date.valueOf(java.lang.String) 接收到一个表示日期的字符串,格式为yyyy-[m]m-[d]d。例如:

      ps.setDate(2, java.sql.Date.valueOf("2013-09-04"));
      
    • java.util.Date

      假设您有一个java.util.Date 类型的变量endDate,您可以这样进行转换:

      ps.setDate(2, new java.sql.Date(endDate.getTime());
      
    • 当前

      如果要插入当前日期:

      ps.setDate(2, new java.sql.Date(System.currentTimeMillis()));
      
      // Since Java 8
      ps.setDate(2, java.sql.Date.valueOf(java.time.LocalDate.now()));
      

    ❐ 使用java.sql.Timestamp

    如果您的表有TIMESTAMPDATETIME 类型的列:

    • java.lang.String

      方法java.sql.Timestamp.valueOf(java.lang.String) 接收到一个表示日期的字符串,格式为yyyy-[m]m-[d]d hh:mm:ss[.f...]。例如:

      ps.setTimestamp(2, java.sql.Timestamp.valueOf("2013-09-04 13:30:00");
      
    • java.util.Date

      假设您有一个endDate 类型为java.util.Date 的变量,您可以这样进行转换:

      ps.setTimestamp(2, new java.sql.Timestamp(endDate.getTime()));
      
    • 当前

      如果您需要当前时间戳:

      ps.setTimestamp(2, new java.sql.Timestamp(System.currentTimeMillis()));
      
      // Since Java 8
      ps.setTimestamp(2, java.sql.Timestamp.from(java.time.Instant.now()));
      ps.setTimestamp(2, java.sql.Timestamp.valueOf(java.time.LocalDateTime.now()));
      

    【讨论】:

    • +1 我也认为值得注意的是,如果日期不是当前日期而是 java Date 对象,您可以使用 getTime() 方法使其以类似的方式工作。跨度>
    • 我在写评论时看到添加了类似信息的答案 :)
    • 仅供参考,这个答案是正确的,但现在已经过时了。这些麻烦且设计糟糕的旧日期时间类现在已被遗留,被 java.time 类所取代。
    • 我在运行 ps.setTimestamp(2, java.sql.Timestamp.valueOf("2013-09-04 13:30:00"); 时收到错误提示 TypeError: Cannot read property "sql" from undefined.
    【解决方案3】:

    不确定,但我认为您正在寻找的是从字符串创建 java.util.Date,然后将该 java.util.Date 转换为 java.sql.Date。

    试试这个:

    private static java.sql.Date getCurrentDate(String date) {
    
        java.util.Date today;
        java.sql.Date rv = null;
        try {
    
            SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
            today = format.parse(date);
            rv = new java.sql.Date(today.getTime());
            System.out.println(rv.getTime());
    
        } catch (Exception e) {
            System.out.println("Exception: " + e.getMessage());
        } finally {
            return rv;
        }
    
    }    
    

    将为 setDate() 返回一个 java.sql.Date 对象;

    上面的函数会打印出一个long值:

    1375934400000

    【讨论】:

      【解决方案4】:

      如果你想将当前日期添加到数据库中,我会避​​免在 Java 中计算日期。如果客户端配置错误、时间错误、时区错误等,在 Java(客户端)端确定“现在”可能会导致数据库中的不一致。相反,可以在服务器端以方式如下:

      requestSQL = "INSERT INTO CREDIT_REQ_TITLE_ORDER ("   +
                      "REQUEST_ID, ORDER_DT, FOLLOWUP_DT) " +
                      "VALUES(?, SYSDATE, SYSDATE + 30)";
      
      ...
      
      prs.setInt(1, new Integer(requestID));
      

      这样,只需要一个绑定参数,服务器端计算的日期就会一致。更好的是向CREDIT_REQ_TITLE_ORDER 添加一个插入触发器,并让触发器插入日期。这有助于加强不同客户端应用程序之间的一致性(例如,有人试图通过 sqlplus 进行修复。

      【讨论】:

      • 这是否仍然允许 Oracle 重用相同的执行计划。根据这个link :- 重复使用执行计划- 只有在 SQL 语句完全相同的情况下,才能节省时间,以防万一、preparedStatements 或绑定变量起作用。如果将不同的值放入 SQL 语句中,数据库会像处理不同的语句一样处理它并重新创建执行计划。 SYSDATE 会被视为不同的值吗?谢谢!
      • @JavaTec 最终的答案是查看 sql 缓存并运行一些测试。我不再有可用于进行一些测试的 Oracle 服务器,但是,我确实记得我使用的 Oracle 版本(
      【解决方案5】:

      您遇到的问题是您从格式化的 java.util.Date 传递不兼容的格式来构造 java.sql.Date 的实例,在使用 valueOf() 时它们的行为方式不同,因为它们使用不同的格式。

      我还可以看到您的目标是保留小时和分钟,我认为您最好将数据类型更改为支持小时和分钟的java.sql.Timestamp,同时将您的数据库字段更改为 DATETIME 或类似(取决于您的数据库供应商)。

      无论如何,如果你想从java.util.Date to java.sql.Date更改,我建议使用

      java.util.Date date = Calendar.getInstance().getTime();
      java.sql.Date sqlDate = new java.sql.Date(date.getTime()); 
      // ... more code here
      prs.setDate(sqlDate);
      

      【讨论】:

        【解决方案6】:

        docs 明确表示java.sql.Date 将抛出:

        • IllegalArgumentException - 如果给定的日期不是 JDBC 日期转义格式 (yyyy-[m]m-[d]d)

        此外,您不需要将日期转换为String,然后再转换为sql.date,这似乎是多余的(而且容易出错!)。相反,您可以:

        java.sql.Date sqlDate := new java.sql.Date(now.getTime());
        prs.setDate(2, sqlDate);
        prs.setDate(3, sqlDate);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-09-21
          • 1970-01-01
          • 2015-01-22
          • 2010-12-29
          • 1970-01-01
          相关资源
          最近更新 更多