【问题标题】:Select 7 days ago JDBC选择 7 天前 JDBC
【发布时间】:2017-04-24 03:30:36
【问题描述】:

我正在为餐厅数据库程序创建一个 JDBC,用户可以在其中看到最近 7 天的利润报告。

我不知道如何选择最近 7 天,所以程序显示 7 天前的总利润。

我想知道如何实现这一点,我的代码如下所示:

   public static void showSevenDaysAgoSells() {
            double value = 0.0;
            try {       
                System.out.println("----Showing last seven days profit----");
                PreparedStatement stmt = conn.prepareStatement("SELECT sum(total) FROM sells WHERE date > date_from_7_days_ago");
                java.sql.Date date= new java.sql.Date(new java.util.Date().getTime());
                stmt.setObject(1, LocalDate.now(ZoneId.of("America/Caracas")).minusWeeks(1));               
                stmt.executeQuery();
                ResultSet rs = stmt.executeQuery();
                rs.next();
                String sum = rs.getString(1);
                value = Double.parseDouble(sum);
                System.out.println("DAYS" + "\t" +  "TOTAL");
                System.out.println(date+ "\t" + sum);
                } catch(Exception e) {
                e.printStackTrace();
            }
        }

【问题讨论】:

  • 只要使用WHERE date > date_from_7_days_ago
  • 它给了我这个错误:org.postgresql.util.PSQLException: The column index is out of range: 1, number of columns: 0.
  • 顺便说一句,我在发布之前看到了您在此处链接的帖子,它对我不起作用
  • 这当然会失败,因为在您更新后准备好的语句没有? 占位符。使用WHERE date = ?,它应该会重新开始工作。
  • 如果您使用的是 Java 8 和最新的 JDBC 驱动程序,您可以使用 setObject(1, LocalDate.now().minusDays(7)) 设置它并使用 getObject(1, LocalDate.class) 检索日期。最好不要依赖java.util.Date,如果可以避免它,它是子类。

标签: java sql postgresql date jdbc


【解决方案1】:

tl;博士

PreparedStatement stmt = conn.prepareStatement(
    "SELECT sum( total_ ) FROM sells_ WHERE date_ > ? ;"
);

……和……

stmt.setObject(                            // Use JDBC 4.2 method for passing/fetching java.time types.
    1 ,                                    // Specify which placeholder `?` in PreparedStatement (1, 2, 3, etc.). In this case, `1`. 
    LocalDate.now(                         // Get current date (today) in desired/expected time zone. 
        ZoneId.of( "America/Montreal" )
    ).minusWeeks( 1 )                      // Go back in time a week (7 days).
)

?

您在 SQL 字符串中嵌入了变量 date_from_7_days_ago。因此它被视为字符串文字而不是 Java 变量的传递值。将该变量名称替换为PreparedStatement 的问号? 占位符。

PreparedStatement stmt = conn.prepareStatement( "SELECT sum( total_ ) FROM sells_ WHERE date_ > ? ;" );

提示:避免使用保留字(例如 datetotal)来命名列和其他数据库标识符。各种 SQL 数据库总共保留了上千个保留字,因此碰撞比人们想象的要容易。 SQL 规范明确承诺永远不会保留任何带有下划线的单词。因此,将所有标识符命名为 date_total_ 等,以免发生冲突。

避免使用旧的日期时间类

问题和另一个答案都使用了麻烦的旧日期时间类,这些类已被证明设计不良、令人困惑且存在缺陷。它们现在是遗留的,被 java.time 类所取代。

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

时区

问题和其他答案都忽略了时区的关键问题。

时区是确定日期所必需的。对于任何给定的时刻,日期在全球范围内因区域而异。例如,Paris France 中午夜后几分钟是新的一天,而 Montréal Québec 中仍然是“昨天”。

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

如果您的业务逻辑要求指定UTC 而不是特定时区,请使用常量ZoneOffset.UTC

LocalDate today = LocalDate.now( ZoneOffset.UTC ) ;

要获取 7 天前(一周),让对象进行日期数学运算。

LocalDate weekAgo = LocalDate.minusWeeks( 1 );

JDBC

如果您的JDBC driver 符合JDBC 4.2 及更高版本,您可以通过PreparedStatement.setObjectResultSet.getObject 直接使用java.time 类型。

myPreparedStatement.setObject( … , weekAgo );

并检索:

LocalDate ld = myResultSet.getObject( … , LocalDate.class );

至少这两个用于 Postgres 的 JDBC 驱动程序可能已经针对 JDBC 4.2 进行了更新,尽管我还没有测试它们是否支持 java.time 类型。

如果您的 JDBC 驱动程序不符合要求,则回退到使用 java.sql。但尽可能简短地执行此操作,立即转换为 java.time 或从 java.time 转换。要进行转换,请查看添加到旧类的新方法。在这种情况下,我们将不得不使用valueOftoLocalDate 方法退回到使用java.sql.Date

myPreparedStatement.setObject( … , java.sql.Date.valueOf( weekAgo ) );

并检索:

LocalDate ld = myResultSet.getDate( … ).toLocalDate() ;

关于java.time

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

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

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

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

  • 它给了我以下错误:org.postgresql.util.PSQLException:无法推断用于 java.time.LocalDate 实例的 SQL 类型。使用带有显式 Types 值的 setObject() 来指定要使用的类型。
【解决方案2】:

    final long OneDayMillis = 86400000l;
    PreparedStatement stmt = conn.prepareStatement("SELECT sum(total) FROM sells WHERE date > ?");
    java.sql.Date date = new java.sql.Date(System.currentTimeMillis()-OneDayMillis*7l);
    stmt.setDate(1, date);

考虑到您的服务器的当前日期可能与您的客户端计算机的当前日期不同,因此最好使用带有 SQL 语句的服务器端日期

SELECT sum(total) FROM sells WHERE date > current_date - integer '7'

没有来自客户端的任何参数绑定。

【讨论】:

  • 在没有java.util.Calendar 的情况下进行这样的毫秒计算可能会成为 DST 更改的问题。我知道这是一个极端情况,但这就是你应该使用它的原因。
猜你喜欢
  • 1970-01-01
  • 2011-12-07
  • 2014-08-07
  • 2013-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多