Jim*_*ans 1 java oracle ibatis date ora-02290
我遇到了一个我认为纯粹是UTC的代码中意外的夏令时问题.我正在使用Java 1.6,iBatis SQL映射器(2.3.3)和Oracle XE(Oracle 10.2的eval版本)和Oracle瘦驱动程序.
该数据库包含表示电视广播时间表的表.每个"资产"(程序)都有一个start_time和结束时间.这是相关的切片:
create table Asset
(
asset_id integer not null, -- The unique id of the Asset.
[...]
start_time timestamp, -- The start time.
end_time timestamp, -- The end time.
[...]
constraint asset_primary_key primary key (asset_id),
constraint asset_time check (end_time >= start_time)
);
Run Code Online (Sandbox Code Playgroud)
2009年11 asset_time月1日这个即将到来的星期天早上,跨越美国中央夏令时调整的节目正在开启oracle 约束.
我有这个数据传输对象(日期是java.util.Dates):
public class Asset
{
protected Long asset_id;
[...]
protected Date start_time;
protected Date end_time;
public Date getStart_time() { return start_time; }
public Date getEnd_time() { return end_time; }
public void setStart_time(Date start_time) { this.start_time = start_time; }
public void setEnd_time(Date end_time) { this.end_time = end_time; }
[...]
}
Run Code Online (Sandbox Code Playgroud)
在iBatis SQL map中,我有一个将Asset DTO插入Oracle Asset表的语句:
<insert id="Asset.insert" parameterClass="com.acme.Asset">
insert into Asset
( asset_id, [...] start_time, end_time )
values
( #asset_id#, [...] #start_time#, #end_time# )
</insert>
Run Code Online (Sandbox Code Playgroud)
在Java方面,我已经验证我通过这个预插入断言给iBatis正确的UTC日期输入,这不会被抛出:
System.err.println("Inserting asset " + program_id);
System.err.println(" "+asset.getStart_time_str()+"--"+asset.getEnd_time_str());
if ( !asset.getEnd_time().after(asset.getStart_time())) {
System.err.println("Invalid datetime range in asset.");
throw new AssertionError("Invalid datetime range in asset.");
}
Run Code Online (Sandbox Code Playgroud)
就在Oracle约束失败之前,上面的代码打印出来:
Inserting asset EP011453960004
2009-11-01T06:30:00Z--2009-11-01T07:00:00Z
Run Code Online (Sandbox Code Playgroud)
我在美国中部时区,格林威治标准时间-5:00,所以这个节目从凌晨1:30开始,到凌晨2:00结束.夏令时的变化在凌晨2点发生,并将时钟恢复到凌晨1:00.
iBatis报告Oracle约束失败(已编辑):
2009-10-30 22:58:42,238 [...] Executing Statement:
insert into Asset ( asset_id, [...] start_time, end_time )
values ( ?, [...] ?, ? )
2009-10-30 22:58:42,238 [...] Parameters:
[EP011453960004, [...] 2009-11-01 01:30:00.0, 2009-11-01 01:00:00.0]
2009-10-30 22:58:42,238 [..] Types:
[java.lang.Long, [...] java.sql.Timestamp, java.sql.Timestamp]
2009-10-30 22:58:42,285 [...] - Failed with a SQLException:
--- The error occurred in com/acme/data/dao/Asset-Write.xml.
--- The error occurred while applying a parameter map.
--- Check the Asset.insert-InlineParameterMap.
--- Check the statement (update failed).
--- Cause: java.sql.SQLException: ORA-02290: check constraint (ACME.ASSET_TIME)
violated
Run Code Online (Sandbox Code Playgroud)
您会注意到,在Oracle方面,它看到了带有夏令时调整的start_time/end_time,因此iBatis映射逻辑或Oracle驱动程序中的某些内容没有达到我的预期.驱动程序是ojdbc14.jar,瘦驱动程序:
JDBCReadWrite.Driver = oracle.jdbc.OracleDriver
JDBCReadWrite.ConnectionURL = jdbc:oracle:thin:@localhost:1521:XE
Run Code Online (Sandbox Code Playgroud)
确保此代码纯粹是UTC的正确方法是什么?
提前致谢!
我有一个似乎可以解决问题的解决方案.尽管应用程序和数据库使用的类型存储了格林威治标准时间1970年1月1日午夜的时间偏移量,但JDBC规范要求对JVM的默认时区进行调整.iBatis使用JDBC默认值映射日期.只要数据没有跨越夏令时边界,或者默认情况下机器或JVM设置为GMT,调整始终是对称的,因此无害.
作为实验,我将JVM默认时区切换为GMT:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
Run Code Online (Sandbox Code Playgroud)
这解决了这个问题,虽然是以非常严厉的方式(JVM中的其他代码可能不会期望这样).
但是iBatis允许您在任何粒度级别覆盖默认类型处理.我编写了一个GMT保留类型处理程序,并为我所有的java.util.Dates注册了它:
<typeHandler callback="com.acme.GMTDateTypeHandler" javaType="java.util.Date"/>
Run Code Online (Sandbox Code Playgroud)
我的类型处理程序如下所示:
public class GMTDateTypeHandler implements TypeHandlerCallback
{
@Override
public void setParameter(ParameterSetter setter, Object parameter)
throws SQLException
{
java.util.Date date = (java.util.Date) parameter;
if ( date == null )
setter.setNull(Types.TIMESTAMP);
else
{
Timestamp timestamp = new Timestamp(date.getTime());
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
setter.setTimestamp(timestamp, calendar);
}
}
@Override
public Object getResult(ResultGetter getter) throws SQLException
{
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
return getter.getTimestamp(calendar);
}
@Override
public Object valueOf(String s)
{
throw new UnsupportedOperationException(
"GMTDateTypeHandler.valueOf() is not supported.");
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12644 次 |
| 最近记录: |