2.

java.util.Date cannot be cast to java.sql.Date 対処

編集
この記事の要点
  • java.lang.ClassCastException: java.util.Date cannot be cast to ...Date 型と Timestamp 型の混同
  • JDBC で getDate()java.sql.DategetTimestamp()java.sql.Timestamp を返す
  • JPA は @Temporal(DATE/TIMESTAMP) で型指定
  • 対処: 適切な型へキャスト or 直接 LocalDate / LocalDateTime 使用(Java 8+)
  • Hibernate 5+ では java.time 型を直接マッピング可能

 

エラーの状況

java.lang.ClassCastException:
java.util.Date cannot be cast to java.sql.Timestamp
    at com.example.UserService.process(UserService.java:42)

# または逆方向
java.lang.ClassCastException:
java.sql.Timestamp cannot be cast to java.util.Date

Date 型の関係を理解

パッケージ用途
java.util.Datejava.util日時の汎用型(古い)
java.sql.Datejava.sqlSQL DATE 型(時刻なし)
java.sql.Timejava.sqlSQL TIME 型(時刻のみ)
java.sql.Timestampjava.sqlSQL TIMESTAMP 型(日時 + ナノ秒)
java.time.LocalDatejava.time日付(Java 8+ 推奨)
java.time.LocalDateTimejava.time日時(Java 8+ 推奨)
java.time.ZonedDateTimejava.timeタイムゾーン付き日時

継承関係:

java.util.Date
├── java.sql.Date         (extends Date)
├── java.sql.Time          (extends Date)
└── java.sql.Timestamp     (extends Date)

# つまり java.sql.* は全部 java.util.Date のサブクラス
# だが ClassCastException は逆方向 (親 → 子) のキャストで発生

原因と対処

原因 1: getObject() / 戻り値から強引にキャスト

// JDBC で取得
ResultSet rs = ps.executeQuery();
while (rs.next()) {
    Object val = rs.getObject("created_at");  // 型が決まらない
    Timestamp ts = (Timestamp) val;
    // → val が Date だった場合 ClassCastException
}

// 修正: 専用メソッド使用
Timestamp ts = rs.getTimestamp("created_at");
Date date = rs.getDate("birthday");

// または LocalDateTime 直接 (JDBC 4.2+)
LocalDateTime dt = rs.getObject("created_at", LocalDateTime.class);
LocalDate d = rs.getObject("birthday", LocalDate.class);

原因 2: JPA エンティティの型不一致

// ❌ ダメな例
@Entity
public class Event {
    @Id @GeneratedValue
    private Long id;

    @Column(name = "scheduled_at")
    @Temporal(TemporalType.TIMESTAMP)
    private Date scheduledAt;  // java.util.Date
}

// Native Query で TIMESTAMP を取得すると
List events = em.createNativeQuery(
    "SELECT scheduled_at FROM events"
).getResultList();
Timestamp ts = (Timestamp) events.get(0);  // ← util.Date を sql.Timestamp にキャストでエラー

// 修正: LocalDateTime に変更 (推奨)
@Column(name = "scheduled_at")
private LocalDateTime scheduledAt;

原因 3: Object[] からのキャスト

// JPQL の SELECT で複数値
@Query("SELECT u.id, u.createdAt FROM User u")
List findUserDates();

// 利用側
for (Object[] row : userRepository.findUserDates()) {
    Long id = (Long) row[0];
    Timestamp ts = (Timestamp) row[1];  // 型が Date or LocalDateTime の可能性
    // → ClassCastException
}

// 修正: DTO 投影
@Query("SELECT new com.example.UserDateDto(u.id, u.createdAt) FROM User u")
List findUserDates();

public class UserDateDto {
    private Long id;
    private LocalDateTime createdAt;
    public UserDateDto(Long id, LocalDateTime createdAt) { ... }
}

各型の変換方法

// 旧型同士
Date utilDate = new Date();
Timestamp ts = new Timestamp(utilDate.getTime());
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());

// 旧型 → java.time
Date utilDate = new Date();
LocalDateTime ldt = utilDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDate ld = utilDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

Timestamp ts = new Timestamp(System.currentTimeMillis());
LocalDateTime ldt = ts.toLocalDateTime();

java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());
LocalDate ld = sqlDate.toLocalDate();

// java.time → 旧型 (DB 連携のため)
LocalDateTime ldt = LocalDateTime.now();
Timestamp ts = Timestamp.valueOf(ldt);
Date utilDate = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

LocalDate ld = LocalDate.now();
java.sql.Date sqlDate = java.sql.Date.valueOf(ld);

Hibernate 5+ で java.time 直接使用

@Entity
public class Event {
    @Id @GeneratedValue
    private Long id;

    // LocalDate → DATE
    private LocalDate eventDate;

    // LocalDateTime → TIMESTAMP
    private LocalDateTime createdAt;

    // ZonedDateTime → TIMESTAMP WITH TIME ZONE (PostgreSQL)
    private ZonedDateTime publishedAt;

    // OffsetDateTime → TIMESTAMP WITH TIME ZONE
    private OffsetDateTime updatedAt;

    // Instant → TIMESTAMP (UTC)
    private Instant timestamp;
}

// @Temporal は不要 (Java 8+ では非推奨)

Spring Data JPA でのソート / フィルタ

// Date 型で問題が起きるパターン
public List findRecent() {
    return eventRepository.findByCreatedAtAfter(
        Date.from(Instant.now().minus(7, ChronoUnit.DAYS))
    );
}

// 推奨: LocalDateTime で一貫
public List findRecent() {
    return eventRepository.findByCreatedAtAfter(
        LocalDateTime.now().minusDays(7)
    );
}

// Repository
public interface EventRepository extends JpaRepository {
    List findByCreatedAtAfter(LocalDateTime threshold);
    List findByEventDateBetween(LocalDate from, LocalDate to);
}

JSON シリアライズ (Jackson)

// Date 型の出力
new ObjectMapper().writeValueAsString(new Date());
// → "1684123456789" (タイムスタンプ数値) ← 読みにくい

// 設定
ObjectMapper om = new ObjectMapper();
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// → "2026-05-15 12:00:00"

// java.time 対応
om.registerModule(new JavaTimeModule());
om.writeValueAsString(LocalDateTime.now());
// → "2026-05-15T12:00:00"

// Spring Boot 自動設定 (application.properties)
spring.jackson.serialization.write-dates-as-timestamps=false
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=Asia/Tokyo

推奨される対処

  1. 新規コードは java.time のみ使う: LocalDate / LocalDateTime / Instant / ZonedDateTime
  2. 既存コード: Date を見つけたら徐々に LocalDate / LocalDateTime に置き換え
  3. JPA エンティティの型統一: 全エンティティで java.time を使用
  4. DB 側: TIMESTAMP / DATE / TIME を適切に使い分け
  5. タイムゾーン: アプリ全体で UTC または明示的 ZoneId 統一

関連エラー

  • ParseException: Unparseable date: 日付文字列パース失敗 (SimpleDateFormat)
  • DateTimeParseException: 同様 (DateTimeFormatter)
  • java.time.DateTimeException: 不正な日時値
  • ZoneRulesException: 不正なタイムゾーン

関連記事

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. java.lang.NoSuchMethodError
  2. java.lang.ClassCastException: java.util.Date cannot be cast to java.sql.Date
  3. java.lang.UnsupportedClassVersionError
  4. version less than X.X is not supported.
  5. パッケージ~は存在しません
  6. org.apache.jasper.JasperException: ...The jsp:param action must not be...
  7. java.io.FileNotFoundException: ファイル名 (許可がありません)
  8. java.sql.SQLException: Cannot convert value 'YYYY-MM-DD ...' from column n(YYYY-MM-DD ...) to TIMESTAMP.
  9. 警告: この文字は、エンコーディング[文字コード]にマップできません
  10. java.text.ParseException: Unparseable date
  11. Unsupported major.minor version 52.0
  12. エンティティ" ... "への参照は';'デリミタで終了する必要があります。
  13. java.math.BigDecimal cannot be cast to java.lang.String