杰瑞科技汇

Java与Oracle日期类型如何转换处理?

Of course! The relationship between Java and Oracle Dates is a classic and important topic in enterprise development. Here’s a comprehensive breakdown covering the key concepts, best practices, and common pitfalls.

Java与Oracle日期类型如何转换处理?-图1
(图片来源网络,侵删)

The Core Problem: Why Two Different Date/Time APIs?

This is the root of all confusion.

  • Oracle Database: Has its own built-in date and time types, primarily DATE and the more modern TIMESTAMP.
  • Java: Historically had the notoriously problematic java.util.Date and java.util.Calendar. Since Java 8, it has a modern, powerful, and immutable API in java.time.

The goal is to correctly move data between these two worlds. This involves two main operations:

  1. Reading from Oracle: Getting a date/time value from the database into a Java object.
  2. Writing to Oracle: Sending a Java date/time object to the database to be stored.

Oracle's Date/Time Types

First, let's understand what's on the Oracle side.

Type Precision Description Example Literal
DATE Day to Second Stores date and time (year, month, day, hour, minute, second). The fractional second is not stored. It defaults to 0. DATE '2025-10-27 14:30:00'
TIMESTAMP Day to Fractional Second Stores the same as DATE but with fractional seconds (up to 9 digits of precision, configurable). TIMESTAMP '2025-10-27 14:30:00.123456'
TIMESTAMP WITH TIME ZONE Day to Fractional Second + Time Zone Like TIMESTAMP but includes an explicit time zone offset (e.g., +05:30). TIMESTAMP '2025-10-27 14:30:00.123456 +05:30'

Key Takeaway: When you use a PreparedStatement to set a date, you must choose the Java type that best matches the Oracle type you intend to store. If you store a java.sql.Timestamp with nanoseconds into a DATE column, the fractional seconds will be truncated.

Java与Oracle日期类型如何转换处理?-图2
(图片来源网络,侵删)

Java's Date/Time APIs (Old vs. New)

The Old Way (Pre-Java 8): java.sql Package

These classes were designed specifically for JDBC and have a direct, but sometimes confusing, mapping to SQL types.

Java Class Purpose Notes
java.sql.Date Represents a date only (year, month, day). A thin wrapper around java.util.Date. Ignores time.
java.sql.Time Represents a time only (hour, minute, second). A thin wrapper around java.util.Date. Ignores date.
java.sql.Timestamp Represents a timestamp (date + time + fractional seconds). Extends java.util.Date. This is the most important one for TIMESTAMP columns. It has extra methods for nanoseconds.

The java.util.Date Trap: Avoid using java.util.Date directly for database operations. It's confusing because:

  • It represents both a date and a time.
  • Its toString() method uses the local time zone, which is not what you want for a database.
  • It has poor API design (e.g., months are 0-indexed).

The Modern Way (Java 8+): java.time Package

This is the recommended, modern approach. The classes are immutable, thread-safe, and much easier to use.

Java Class Purpose JDBC Mapping (via java.sql Wrapper)
java.time.LocalDate Represents a date only (year, month, day). Mapped to java.sql.Date via java.sql.Date.valueOf(localDate)
java.time.LocalTime Represents a time only (hour, minute, second, nanosecond). Mapped to java.sql.Time via java.sql.Time.valueOf(localTime)
java.time.LocalDateTime Represents a date and time without a time zone. The best choice for DATE or TIMESTAMP columns. Mapped to java.sql.Timestamp via Timestamp.valueOf(localDateTime)
java.time.Instant Represents a point on the timeline in UTC. Good for absolute timestamps. Mapped to java.sql.Timestamp via Timestamp.from(instant)
java.time.ZonedDateTime Represents a date and time with a time zone. The best choice for TIMESTAMP WITH TIME ZONE. Mapped to java.sql.Timestamp (time zone info is lost!) or a custom Object.

Practical Code Examples

Let's assume you have a table:

CREATE TABLE events (
    id          NUMBER PRIMARY KEY,
    event_name  VARCHAR2(100),
    event_time  DATE,
    created_ts  TIMESTAMP(6)
);

Example 1: Reading from Oracle into Java (Using Modern java.time)

This is the recommended approach.

import java.sql.*;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class OracleDateReader {
    public static void main(String[] args) {
        String url = "jdbc:oracle:thin:@localhost:1521:ORCLCDB";
        String user = "your_user";
        String password = "your_password";
        String sql = "SELECT event_time, created_ts FROM events WHERE id = 1";
        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {
            if (rs.next()) {
                // --- Reading a DATE column (event_time) ---
                // 1. Get the value as a java.sql.Timestamp (standard JDBC)
                Timestamp timestampFromDb = rs.getTimestamp("event_time");
                // 2. Convert to the modern java.time.LocalDateTime
                LocalDateTime localDateTime = timestampFromDb.toLocalDateTime();
                System.out.println("Oracle DATE as LocalDateTime: " + localDateTime);
                // --- Reading a TIMESTAMP(6) column (created_ts) ---
                // 1. Get the value as a java.sql.Timestamp
                Timestamp preciseTimestampFromDb = rs.getTimestamp("created_ts");
                // 2. Convert to java.time.LocalDateTime. It will preserve fractional seconds.
                LocalDateTime preciseLocalDateTime = preciseTimestampFromDb.toLocalDateTime();
                System.out.println("Oracle TIMESTAMP as LocalDateTime: " + preciseLocalDateTime);
                // If you need the raw nanoseconds (e.g., for logging)
                int nanos = preciseTimestampFromDb.getNanos();
                System.out.println("Nanoseconds from TIMESTAMP: " + nanos);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Example 2: Writing to Oracle from Java (Using Modern java.time)

import java.sql.*;
import java.time.LocalDateTime;
public class OracleDateWriter {
    public static void main(String[] args) {
        String url = "jdbc:oracle:thin:@localhost:1521:ORCLCDB";
        String user = "your_user";
        String password = "your_password";
        // Create a new event time for now
        LocalDateTime now = LocalDateTime.now();
        String sql = "INSERT INTO events (id, event_name, event_time, created_ts) VALUES (?, ?, ?, ?)";
        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, 101);
            pstmt.setString(2, "Java 8 Demo");
            // --- Writing to a DATE column ---
            // 1. Convert java.time.LocalDateTime to java.sql.Timestamp
            //    This is the standard way. The Timestamp will have 0 for nanoseconds.
            Timestamp tsForDateColumn = Timestamp.valueOf(now);
            pstmt.setTimestamp(3, tsForDateColumn);
            // --- Writing to a TIMESTAMP(6) column ---
            // 1. Convert java.time.LocalDateTime to java.sql.Timestamp
            //    This preserves fractional seconds.
            Timestamp tsForTimestampColumn = Timestamp.valueOf(now);
            pstmt.setTimestamp(4, tsForTimestampColumn);
            int rowsInserted = pstmt.executeUpdate();
            System.out.println(rowsInserted + " row inserted.");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Key Best Practices and Pitfalls

  1. Use Modern java.time (Java 8+): Always prefer java.time classes (LocalDate, LocalDateTime, etc.) for your application logic. They are far superior to the old java.util.Date/java.sql classes.

  2. Use PreparedStatement: Never concatenate SQL strings with date values. Use pstmt.setDate(), pstmt.setTimestamp(), etc. This prevents SQL injection and handles formatting automatically.

  3. Choose the Right Java Type for the Job:

    • For an Oracle DATE column or a TIMESTAMP column without time zone, use java.time.LocalDateTime in your Java code.
    • Convert LocalDateTime to java.sql.Timestamp using Timestamp.valueOf() before setting it on a PreparedStatement.
    • When reading, get the value as a java.sql.Timestamp and convert it to LocalDateTime using toLocalDateTime().
  4. Beware of Time Zones:

    • LocalDateTime has no time zone. It represents "wall-clock" time (e.g., "2:30 PM in New York"). This is often what you want for things like an appointment time or a creation timestamp that's stored in the DB's time zone.
    • If your application needs to deal with absolute moments in time across different time zones, use java.time.Instant (UTC) or java.time.ZonedDateTime.
    • Crucially: java.sql.Timestamp has limited support for time zones. When you store a ZonedDateTime into a TIMESTAMP WITH TIME ZONE column, you typically need to convert it to a String in a specific format (like ISO-8601) and use pstmt.setString(), or use a third-party library like Joda-Time or Vibur's java-time-jdbc which provides better mappings.
  5. Handling java.sql.Date and java.sql.Time: These are largely legacy. You should only use them if you have a specific DATE or TIME column in Oracle and you are certain you only need the date or time part. For most use cases, LocalDateTime is the simpler, more robust choice.

Summary Table: Java to Oracle Mapping

If your Java object is... And your Oracle column is... The correct JDBC method is...
java.time.LocalDate DATE pstmt.setDate(..., Date.valueOf(localDate))
java.time.LocalDateTime DATE or TIMESTAMP pstmt.setTimestamp(..., Timestamp.valueOf(localDateTime))
java.time.LocalDateTime TIMESTAMP WITH TIME ZONE Complex. Convert to String or use a library. pstmt.setString() is a common workaround.
java.time.Instant TIMESTAMP pstmt.setTimestamp(..., Timestamp.from(instant))
java.sql.Timestamp TIMESTAMP pstmt.setTimestamp(..., timestampObject)
分享:
扫描分享到社交APP
上一篇
下一篇