我写了两个简单的函数来获取MySQL表中日期的值。开始日期和结束日期列均为Date
数据类型。因此,在我的这两个函数中,内容如下:
public Date get_startdate(long nodeid,String ts) {
try {
String sql="Select STARTDT FROM urllink WHERE URL='f0="+nodeid+"&ts="+ts + "'";
if (em == null) {
throw new Exception("could not found URL object.");
}
return (Date) em.createNativeQuery(sql).getSingleResult();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public Date get_enddate(long nodeid,String ts) {
try {
String sql="Select ENDDT FROM urllink WHERE URL='f0="+nodeid+"&ts="+ts + "'";
if (em == null) {
throw new Exception("could not found URL object.");
}
return (Date) em.createNativeQuery(sql).getSingleResult();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
现在我像这样在主页中调用这些函数,我想检查日期条件,如果URL在这两个日期之间,则该URL应该有效并执行某些操作。我在下面强调我的意思:
Date file_getstartdate=fileFacade1.get_startdate(fileID,hash);
Date file_getenddate=fileFacade1.get_enddate(fileID,hash);
String currentDate = CoreUtil.parseDate(new Date());
if( file_getstartdate<= currentDate<= file_getendDate){
//URL is valid, do something
}else {
//do nothing
}
我存储在表中的日期的格式为YYYY-MM-DD
,而我遇到的问题在上面的if
语句中进行比较。我无法使用这些运算符进行检查。有什么方法可以实现我想要的?
答案 0 :(得分:3)
不要使用String串联来构造SQL查询
这很容易受到SQL注入的攻击,并且确实很危险:
String sql="Select STARTDT FROM urllink WHERE URL='f0="+nodeid+"&ts="+ts + "'";
...
return (Date) em.createNativeQuery(sql).getSingleResult();
假设em
引用了EntityManager对象,那么您可以构建一个基于Criteria
的查询,或者如果您确实需要坚持使用本机SQL,则应该使用{{3} }。
对于您的比较问题,您遇到三个问题:
String
和Date
)。<=
)的运算符。a <= b <= c
不是有效的Java语句。它必须为a <= b && b <= c
)。 这是一种使用Date
类进行比较的方法(请注意,由于Java 8 LocalDate
是更好的选择,因此可以使用)。
Date fileStartDate = fileFacade1.get_startdate(fileID, hash);
Date fileEndDate = fileFacade1.get_enddate(fileID, hash);
Date currentDate = new Date();
if (fileStartDate.before(currentDate) && fileEndDate.after(currentDate) {
...
回应您的评论
好的,但是确实使用prepareStatement可以帮助解决这种SQL错误。实体经理会阻止注入吗?有人告诉我不要使用preparedstatement,还有其他选择吗?
如果有人告诉您使用字符串串联(代码的+nodeid+
和+ts +
部分)而不是使用PreparedStatement进行本机查询,那么它们是错误的。 EntityManager不会保护您免受上面代码的干扰,但是PreparedStatement以及更改查询的构造方式都可以。
PreparedStatement看起来像
String url = "f0=" + nodeid + "&ts=" + ts;
PreparedStatement preparedStatement = connection.prepareStatement("Select STARTDT FROM urllink WHERE URL= ?");
preparedStatement.setString(1, url);
ResultSet resultSet = preparedStatement.executeQuery();
如果系统提示您使用EntityManager
而不是编写本机SQL,那么这实际上是个好建议。您需要使用Criteria
抽象来构造查询。该怎么做可能是一个单独的问题。
答案 1 :(得分:1)
如果使用字符串而不是日期对象,则假设您以类似(a <= b && b <= c)的格式书写,则if语句将适用于该格式
否则,我不确定如何将getSingleResult()
转换为Date对象,因为那可能是任何东西,您实际上必须解析该值,然后使用适当的Date方法检查isBefore和isAfter
答案 2 :(得分:1)
if( file_getstartdate<= currentDate<= file_getendDate) { … }
LocalDateRange // Represent a span-of-time as a pair of `LocalDate` (date-only) objects.
.ofClosed( startLocalDate , stopLocalDate ) // Making the date range in fully-closed approach, against my advice of using half-open approach.
.contains( // Compares a specified `LocalDate` against the start and stop dates of the range.
LocalDate.now( // Use `java.time.LocalDate` to represent a date-only value without a time-of-day and without a time zone.
ZoneId.of( "Africa/Tunis" ) // Specify the time zone by which we want to perceive the calendar date for the current moment. "Tomorrow" arrives in Paris France while still "yesterday" in Montréal Québec.
) // Returns a `LocalDate` object.
) // Returns a `boolean` primitive.
Answer by Player One是正确的,但是可以使用现代的 java.time 类而不是可怕的旧类(Date
等)来改进。
LocalDate
对于SQL标准DATE
类型的列,请使用LocalDate
类。 LocalDate
类表示没有日期,没有time zone或offset-from-UTC的仅日期值。
时区对于确定日期至关重要。在任何给定时刻,日期都会在全球范围内变化。例如,Paris France午夜之后的几分钟是新的一天,而Montréal Québec仍然是“昨天”。
如果未指定时区,则JVM隐式应用其当前的默认时区。该默认值可能在运行时(!)期间change at any moment,因此您的结果可能会有所不同。最好将您的期望/期望时区明确指定为参数。如果紧急,请与您的用户确认区域。
ZoneID
以Continent/Region
的格式指定proper time zone name,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用2-4个字母的缩写,例如EST
或IST
,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
如果要使用JVM的当前默认时区,请提出要求并作为参数传递。如果省略,代码将变得难以理解,因为我们不确定您是否打算使用默认值,还是像许多程序员一样不知道该问题。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
if(file_getstartdate <= currentDate <= file_getendDate){
如果您始终使用“半开放式”方法来定义时间跨度,您会发现工作变得更加轻松。在这种方法中,开始为包含,而结束为排他。因此,一个月从第一个月开始,一直持续到但不包括以下个月的第一个月。
因此,您的查询逻辑将与<=
和<
相同。
if(file_getstartdate <= currentDate
这意味着在SQL中,不要将BETWEEN
用于日期时间工作。
String sql = "SELECT * from tbl WHERE when >= ? AND when < ? ;" ;
…
myPreparedStatement.setObject( 1 , today ) ;
myPreparedStatement.setObject( 2 , today ) ;
要检索DATE
,请输入以下值:
LocalDate localDate = myResultSet.getObject( … , LocalDate.class ) ;
Java代码中相同类型的半开逻辑看起来像下面的代码。注意,说“等于或晚于”的简短方式是“不在……之前”。
boolean isTodayWithinDateRange =
( ! today.isBefore( startDate ) ) // Is today same or later than start…
&& // AND
today.isBefore( stopDate ) // Is today before the stop (for Half-Open approach).
;
我存储在表格中的日期的格式为YYYY-MM-DD
不,不是。 MySQL中的DATE
类型通过其内部定义的机制而非纯文本存储日期。
日期时间对象(例如数据库中的DATE
列或Java中的LocalDate
)可以将输入的字符串解析为日期值,并可以从该日期值生成字符串。但是日期值本身不是字符串。不要混淆两者。换句话说,日期值不具有“格式”,只有其文本表示具有格式。
LocalDateRange
如果要完成很多这项工作,请将ThreeTen-Extra库添加到您的项目中。这使您可以访问LocalDateRange
类。
LocalDate start = … ;
LocalDate stop = … ;
LocalDateRange range = LocalDateRange.of( start , stop ) ;
boolean rangeContainsToday = range.contains( today ) ;
默认情况下,LocalDateRange
类使用Half-Open方法工作。但是,如果您坚持采用全封闭方法,请使用LocalDateRange.ofClosed
方法覆盖其默认行为。
java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
目前位于Joda-Time的maintenance mode项目建议迁移到java.time类。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
在哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如Interval
,YearWeek
,YearQuarter
和more。