如何在JPA中将java.sql.Timestamp用作真正的java.util.Date
我对毫秒日期的管理有问题。我理解需要使用时间戳来存储毫秒:如何在JPA中将java.sql.Timestamp用作真正的java.util.Date,java,datetime,jpa,persistence,Java,Datetime,Jpa,Persistence,我对毫秒日期的管理有问题。我理解需要使用时间戳来存储毫秒: @Temporal(TIMESTAMP) @Column(name="DATE_COLUMN", nullable = false) @Override public java.util.Date getDate() { return this.date; } 但是如果我不能将这个日期与java.util.date的另一个实例进行比较,除非我注意equals()调用的顺序,因为this.date实例是java.sql.Timestam
@Temporal(TIMESTAMP)
@Column(name="DATE_COLUMN", nullable = false)
@Override public java.util.Date getDate() { return this.date; }
但是如果我不能将这个日期与java.util.date的另一个实例进行比较,除非我注意equals()调用的顺序,因为this.date
实例是java.sql.Timestamp。如何从JPA获取java.util.Date?因为来自JPA的日期,即使方法签名是java.util.date,实际上也是java.sql.Timestamp的实例
java.util.Date newDate = new Date(this.date.getTime());
this.date.equals(newDate) == false
newDate.equals(this.date) == true
我已尝试在持久性类中修改我的方法:
@Override
public Date getDate() {
return this.date == null ? null : new Date(this.date.getTime());
}
这是可行的,但对于大量数据来说效率不高
还有其他选择:
- 我可以使用
修改我的持久性类的设计,以便在检索到persisted日期后从该日期创建java.util.Date@PostLoad
- 我想知道是否使用
无法得到结果ClassTransformer
你曾经遇到过这个问题吗?我不正确的地方是什么?处理此问题的最佳方法是什么?java.sql.Timestamp覆盖
compareTo(Date)
方法,因此使用compareTo(…)
简而言之,java.util.Date
和java.sql.Timestamp
是相互比较的
此外,您始终可以比较date.getTime()
,而不是对象本身
更进一步,您可以使用
long
字段来存储日期。或者甚至是一个DateTime
(来自joda time)根据我的经验,您不希望java.sql.Timestamp出现在您的逻辑中-正如您所指出的,它会产生许多奇怪的错误,如果您的应用程序进行序列化,它也不会变得更好
如果它与返回新java.util.Date的覆盖一起工作,那么就使用该覆盖。或者更好的选择JodaTime。你会在网上找到很多这样做的例子。我不担心这里的性能,因为您的数据库比创建新的java.util.Date对象要慢得多
编辑:
我看到您正在使用Hibernate。如果使用注释,则可以执行以下操作:
@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
public DateTime getProvisionByTime() {
return provisionByTime;
}
然后,您将从持久对象中的Jodatime获得漂亮的DateTime对象。如果您只希望有一个日期,可以像这样使用LocalDate:
@Type(type = "org.joda.time.contrib.hibernate.PersistentLocalDate")
public LocalDate getCloudExpireDate() {
return cloudExpireDate;
}
如果您使用maven,以下依赖项应该为您设置此设置(您可能需要更新hibernate版本)
org.hibernate
冬眠
3.2.6.ga
org.hibernate
休眠注释
3.3.1.GA
乔达时间
乔达时间冬眠
1.2
乔达时间
乔达时间
1.6.1
JPA应该为java.util.Date类型的属性返回java.util.Date,@Temporal(TIMESTAMP)注释应该只影响日期的存储方式。您不应该返回java.sql.Timestamp
您使用的是什么JPA提供商?您是否在JPA参考实现EclipseLink中尝试过这一点?TBH,我不确定这一点的确切状态,但Hibernate(您的JPA提供商,对吧?)处理
时间戳
列的方式确实可能存在问题
为了将SQLTIMESTAMP
映射到java.util.Date
,Hibernate使用TimestampType
,它将实际为java.util.Date
属性分配一个时间戳。虽然这是“合法的”,但问题是Timestamp.equals(Object)
是不对称的(到底为什么?!),这打破了Date.equals(Object)
的语义
因此,如果myDate
映射到SQLTIMESTAMP
,则不能“盲目”使用myDate.equals(someRealJavaUtilDate)
,这当然是不可接受的
但是,尽管Hibernate论坛对此进行了广泛讨论,例如在和(阅读所有页面),Hibernate用户和开发人员似乎从未就这个问题达成一致意见(参见类似问题),我只是不明白为什么
也许只是我,也许我只是错过了一些简单的东西,但这个问题对我来说是显而易见的,虽然我认为这个愚蠢的罪魁祸首,我仍然认为休眠应该屏蔽用户从这个问题。我不明白加文为什么不同意这一点
我的建议是创建一个测试用例来演示这个问题(应该非常简单),并(再次)报告这个问题,看看您是否从当前团队获得了更多的积极反馈
同时,您可以使用自定义类型自己“修复”问题,使用类似以下内容(取自论坛并按原样粘贴):
该问题对于DAO测试至关重要:
Employer employer1 = new Employer();
employer1.setName("namenamenamenamenamename");
employer1.setRegistered(new Date(1111111111)); // <- Date field
entityManager.persist(employer1);
assertNotNull(employer1.getId());
entityManager.flush();
entityManager.clear();
Employer employer2 = entityManager.find(Employer.class, employer1.getId());
assertNotNull(employer2);
assertEquals(employer1, employer2); // <- works
assertEquals(employer2, employer1); // <- fails !!!
我还没有决定-我是将这种方法用于测试和生产,还是仅用于测试。来自持久性的日期被另一个无法修改的框架使用(太多的工作)。因为我无法修改比较过程,我需要向主系统提供java.util.Date。我想知道如何有效地转换日期?为什么需要?java.util.Date和java.sql.Timestamp是可以相互比较的。看看我的测试,方法的结果取决于调用的顺序,我没有触及这个顺序。我只需要一个真正的java.util.Date.using
compareTo(..)
应该可以工作。除此之外,您始终可以比较它们的getTime()
,而不是对象。我希望我可以不使用,但映射是由JPA提供的…因为您使用的是hibernate,我想您可以尝试我上面提供的解决方案。hibernate是我的JPA提供程序;)这太疯狂了。正如您所建议的,我将再次尝试使用Hibernate。当我在应用程序中遇到同样的问题时,我创建了一个新问题:我刚刚有一个非常糟糕的pr版本
public class TimeMillisType extends org.hibernate.type.TimestampType {
public Date get(ResultSet rs, String name) throws SQLException {
Timestamp timestamp = rs.getTimestamp(name);
if (timestamp == null) return null;
return
new Date(timestamp.getTime()+timestamp.getNanos()/1000000);
}
}
Employer employer1 = new Employer();
employer1.setName("namenamenamenamenamename");
employer1.setRegistered(new Date(1111111111)); // <- Date field
entityManager.persist(employer1);
assertNotNull(employer1.getId());
entityManager.flush();
entityManager.clear();
Employer employer2 = entityManager.find(Employer.class, employer1.getId());
assertNotNull(employer2);
assertEquals(employer1, employer2); // <- works
assertEquals(employer2, employer1); // <- fails !!!
package xxx;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.type.AdaptedImmutableType;
import xxx.DateTimestampType;
import java.util.Date;
public class CustomHSQLDialect extends HSQLDialect {
public CustomHSQLDialect() {
addTypeOverride(DateTimestampType.INSTANCE);
addTypeOverride(new AdaptedImmutableType<Date>(DateTimestampType.INSTANCE));
}
}