我们有一个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
,继续进入班级&amp;方法的核心是:oracle.jdbc.driver.DateTimeCommonAccessor#getTime(int, Calendar)
在此方法中,milliseconds
被剥离,您将获得second
- 精度。
有没有办法在这种情况下保持毫秒?从java.util.Date
到数据库TIMESTAMP
- 类型列时,如何节省毫秒精度?
注意:进入数据库表之前,毫秒数已经消失。如果我在final Timestamp timestamp = resultSet.getTimestamp(names[0]);
放置一个断点并跳过,结果timestamp
将不再有毫秒。
编辑:调试期间hava.util.Date
对象和java.sql.Timestamp
对象的内容:
fraction
和millis
都被删除并存入0
..
答案 0 :(得分:2)
java.sql.Timestamp
在java.util.Date
fastTime
字段中存储第二个精度(而不是普通java.util.Date
的毫秒数),它有一个单独的nanos
字段亚秒级精度。当你调用java.sql.Timestamp.getTime()
时,它会将这两个重新组合成一个毫秒精度值。
由于您已声明您的字段为TIMESTAMP(2)
,这意味着您只有2位小数的亚秒精度。如果您尝试存储00:00:00.001
,实际存储的内容为00:00:00:00
,这也是您再次检索的内容。换句话说,您需要通过将字段声明为TIMESTAMP(3)
(或者甚至更高,最多支持9)来提高精度,或者您需要使用两位小数的精度降低。
请注意,如果使用高于毫秒的精度(超过3位小数),则只能使用java.sql.Timestamp.getNanos()
获得完全精度,其中包含完整的亚秒精度(nanos
只是次要分数,应始终小于1秒),或将Timestamp
转换为java.time.LocalDateTime
。