web-dev-qa-db-ja.com

Java.sql.TimestampをJPAで実際のJava.util.Dateとして使用する方法

ミリ秒単位の日付の管理に問題があります。ミリ秒を格納するためにTIMESTAMPを使用する必要があることを理解しています。

@Temporal(TIMESTAMP)
@Column(name="DATE_COLUMN", nullable = false)
@Override public Java.util.Date getDate() { return this.date; }

ただし、this.dateインスタンスはJava.sql.Timestampであるため、equals()呼び出しの順序に注意を払わない限り、この日付をJava.util.Dateの別のインスタンスと比較できない場合。 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());
}

動作していますが、大量のデータでは効率的ではありません。

他のオプションがあります:

  • @PostLoadを使用して永続クラスの設計を変更し、取得後に指定された日付からJava.util.Dateを作成することができます。

  • ClassTransformerを使用して結果を取得できないのではないかと思いますか?

この問題に直面したことがありますか?私が正しくないことは何ですか?この問題を処理するための最良の方法は何ですか?

14
chepseskaf

_Java.sql.Timestamp_はcompareTo(Date)メソッドをオーバーライドするため、compareTo(..)を使用しても問題はありません。

つまり、_Java.util.Date_と_Java.sql.Timestamp_は相互に比較可能です。

さらに、オブジェクト自体ではなく、いつでもdate.getTime()を比較できます。

さらに、longフィールドを使用して日付を保存できます。またはDateTime(joda-timeから)

7
Bozho

TBH、これの正確なステータスはわかりませんが、Hibernate(JPAプロバイダーですよね?)がTIMESTAMP列を処理する方法に実際に問題がある可能性があります。

SQL TIMESTAMPJava.util.Dateにマップするために、HibernateはTimestampTypeを使用します。これにより、実際には Java.sql.TimestampJava.util.Date属性に割り当てられます。これは「合法」ですが、問題はTimestamp.equals(Object)対称ではない(なぜ地球上で?!)であり、これが壊れることです。 Date.equals(Object)のセマンティクス。

結果として、myDateがSQLTIMESTAMPにマップされている場合、myDate.equals(someRealJavaUtilDate)を「盲目的に」使用することはできません。これはもちろん実際には受け入れられません。

しかし、これはHibernateフォーラムで広く議論されていますが、 このスレッド および このスレッド (すべてのページを読む)では、Hibernateユーザーと開発者が問題に同意したことがないようです( HB-681)などの問題を参照 )そして私は理由がわかりません。

多分それは私だけかもしれません、多分私は他の人にとって単純な何かを見逃しているだけかもしれません、しかし問題は私には明白に見えます、そして私はこの愚かな Java.sql.Timestamp が原因であると考えていますが、それでもHibernateはこれからユーザーを保護するべきだと思います問題。ギャビンがこれに同意しなかった理由がわかりません。

私の提案は、問題を実証するテストケースを作成し(非常に単純なはずです)、問題を(再度)報告して、現在のチームからより肯定的なフィードバックが得られるかどうかを確認することです。

一方、カスタムタイプを使用して、次のようなものを使用して、問題を自分で「修正」することができます(フォーラムから取得してそのまま貼り付けます)。

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);
   }

}
12
Pascal Thivent

私の経験では、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からNiceDateTimeオブジェクトを取得します。日付のみが必要な場合は、次のようにLocalDateを使用できます。

@Type(type = "org.joda.time.contrib.hibernate.PersistentLocalDate")
public LocalDate getCloudExpireDate() {
    return cloudExpireDate;
}

Mavenを使用する場合は、次の依存関係でこれを設定する必要があります(休止状態のバージョンを更新する必要がある場合があります)

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate</artifactId>
        <version>3.2.6.ga</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-annotations</artifactId>
        <version>3.3.1.GA</version>
    </dependency>

    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time-hibernate</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
            <version>1.6.1</version>
    </dependency>
4
Knubo

この問題は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 !!!
_

そのため、結果は本当に驚くべきものであり、テストの作成は難しいものになりました。

ただし、実際のビジネスロジックでは、エンティティは巨大で変更可能であるため、セット/マップキーとして使用することはありません。また、equal比較によって時間値を比較することは決してありません。また、エンティティ全体を比較することも避けてください。

通常のシナリオでは、マップ/セットキーに不変のエンティティIDを使用し、時間値をcompareTo()メソッドと比較するか、getTime()値を使用します。

しかし、テストを行うのは面倒なので、独自のタイプハンドラーを実装しました

http://Pastebin.com/7TgtEd3x

http://Pastebin.com/DMrxzUEV

そして、私が使用する方言を上書きしました:

_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));
    }
}
_

私はまだ決めていません-このアプローチをテストと本番の両方に使用するのか、それともテストのみに使用するのか。

2
Nick Mazurkin

JPAは、タイプJava.util.Dateの属性に対してJava.util.Dateを返す必要があります。@ Temporal(TIMESTAMP)アノテーションは、日付の格納方法にのみ影響する必要があります。 Java.sql.Timestampを取り戻すべきではありません。

どのJPAプロバイダーを使用していますか? JPAリファレンス実装であるEclipseLinkでこれを試しましたか?

1
James