【发布时间】:2017-07-10 23:13:32
【问题描述】:
我们有一个 Spring + Hibernate Java 7 web 应用程序。我们目前使用.hbm.xml Hibernate 文件(而不是更常见的已知注释)作为 Java 实体和数据库表之间的映射。
我们的一个 Java 实体类有一个 java.util.Date,精度为毫秒:
public class SomeEntity extends AbstractEntity{
...
private java.util.Date someDate; // with ms precision
...
}
在我们的.hbm.xml 文件中,我们有以下日期:
...
<hibernate-mapping>
<class name="com.namespace.Entity" table="entity" batch-size="10">
...
<property name="SomeDate" column="somedate" type="com.namespace.CustomDateType" />
...
它链接到具有以下行的数据库表:
SOMEDATE TIMESTAMP(2) NULLABLE=Yes
我们的CustomDateType 班级:
package com.namespace.type;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Date;
import org.hibernate.usertype.UserType;
public class CustomDateType implements UserType {
private static final int[] SQL_TYPES = { Types.TIMESTAMP };
@Override
public Object assemble(final Serializable serializable, final Object o) {
return deepCopy(serializable);
}
@Override
public Object deepCopy(final Object value) {
return value;
}
@Override
public Serializable disassemble(final Object o) {
return (Serializable) deepCopy(o);
}
@Override
public boolean equals(final Object x, final Object y) {
if (x == y) {
return true;
} else if ((x == null) || (y == null)) {
return false;
}
return x.equals(y);
}
@Override
public int hashCode(final Object o) {
return ((CustomDateType) o).hashCode();
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Object nullSafeGet(final ResultSet resultSet, final String[] names, final Object owner) throws SQLException {
Date result = null;
final Timestamp timestamp = resultSet.getTimestamp(names[0]); // This is where the problem is
if (!resultSet.wasNull()) {
result = new Date(timestamp.getTime());
}
return result;
}
@Override
public void nullSafeSet(final PreparedStatement statement, final Object value, final int index) throws SQLException {
final Date dateToSet = (Date) value;
statement.setTimestamp(index, dateToSet == null ? null : new Timestamp(dateToSet.getTime()));
}
@Override
public Object replace(final Object o, final Object o1, final Object o2) {
return o;
}
@Override
public Class<Date> returnedClass() {
return Date.class;
}
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
}
在调试someDate 时,Java 类中确实有毫秒。但是,在 CustomDateType#nullSafeGet 方法中,生成的 TimeStamp 不再有任何毫秒数。
resultSet参数是oracle.jdbc.driver.ForwardOnlyResultSet,延续到oracle.jdbc.driver.T4CPreparedStatement,延续到问题核心所在的类&方法:oracle.jdbc.driver.DateTimeCommonAccessor#getTime(int, Calendar)
在此方法中,milliseconds 被剥离,而您只剩下 second-precision。
在这种情况下,有什么办法可以保持毫秒数吗?从java.util.Date 到数据库TIMESTAMP-typed 列时如何保存毫秒精度?
注意:进入数据库表之前的毫秒数已经过去。如果我在final Timestamp timestamp = resultSet.getTimestamp(names[0]); 处设置断点并跳过,生成的timestamp 将不再有毫秒数。
编辑:调试期间hava.util.Date 对象和java.sql.Timestamp 对象的内容:
fraction 和 millis 都被删除并放到 0..
【问题讨论】:
-
您应该将
java.sql.Timestamp用于时间戳列。不是java.util.Date -
你为什么先得到
java.sqlTimestamp,然后得到java.sql.Time。请注意java.sql.Time的精度以秒为单位,而不是毫秒(请注意,文档并不完全清楚,您需要在两行之间阅读此限制),如果您想要亚秒级精度,则必须使用java.sql.Timestamp。 -
@MarkRotteveel 嗯好吧,我确实明白
Time和Timestamp具有不同的精度,这个Oracle JDBCgetTime调用是问题的根源。但如果我严格查看我们自己的代码,我会看到Timestamp timestamp = resultSet.getTimeStamp(names[0]);。之后,oracle.jdbc调用了这些东西,由于某种原因,它最终以DateTimeCommonAccessor#getTime(...)而不是getTimestamp(...)结束。我将调查它从原始 getTimestamp 转换到 getTime 的位置,但您是否知道这可能发生在哪里,更重要的是:为什么/如何? -
对不起,我在看眼睛;我以为您的代码调用了
resultSet.getTime,但事实并非如此。列的类型是什么,如果是DATE,那么Oracle 没有小数秒,但最大秒精度。您需要一个TIMESTAMP列用于小数秒(然后确保它不是TIMESTAMP(0),默认为TIMESTAMP(6)btw。还要检查您的数据库驱动程序的版本,如果它是一个非常旧的版本,它可能只是尝试将其作为DATE处理 -
您的屏幕截图显示时间为 00:00:00.001(我假设这是您存储的),但是由于您有一个
TIMESTAMP(2),那么您实际上存储了 00:00:00.00(仅亚秒2) 的精度,这也是您检索到的。您可能想要检查 - 例如 - 00:00:00.12,或使用TIMESTAMP(3)甚至更高的精度。
标签: java oracle hibernate date jdbc