Skip to content
Advertisement

Getting a TIMESTAMP from postgre as a LocalDatetime with HibernateORM

I have this Hibernate model

package net.nebulacraft.nebulous.Data.Models;

import org.bukkit.command.CommandSender;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.UUID;

@Entity
@Table(name = "warning")
public class Warning {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "warningid")
    int WarningId;
    @Column(name = "userid")
    UUID Player;
    @Column(name = "rulenumber")
    int RuleNumber;
    @Column(name = "expiration")
    LocalDateTime Expiration;
    @Column(name = "datecreated")
    LocalDateTime DateCreated;
    @Column(name = "warnedby")
    UUID WarnedBy;
    @Column(name = "message")
    String Message;
    public UUID getPlayer() {
        return Player;
    }
    public void setPlayer(UUID player) {
        Player = player;
    }
    public int getRuleNumber() {
        return RuleNumber;
    }
    public void setRuleNumber(int ruleNumber) {
        RuleNumber = ruleNumber;
    }
    public LocalDateTime getExpiration() {
        return Expiration;
    }
    public void setExpiration(LocalDateTime expiration) {
        Expiration = expiration;
    }
    public LocalDateTime getDateCreated() {
        return DateCreated;
    }
    public void setDateCreated(LocalDateTime dateCreated) {
        DateCreated = dateCreated;
    }
    public UUID getWarnedBy() {
        return WarnedBy;
    }
    public void setWarnedBy(UUID warnedBy) {
        WarnedBy = warnedBy;
    }
    public String getMessage() {
        return Message;
    }
    public void setMessage(String message) {
        Message = message;
    }
    public int getWarningId() {
        return WarningId;
    }
    public void setWarningId(int warningId) {
        WarningId = warningId;
    }
}

I can insert a new Warning into the database with session.save(warning); session.getTransaction().commit();, warning being an instance of Warning and session being a hibernate session. However, trying to get an instance of Warning from the database like this

    public static List<Warning> GetWarnings(UUID player) {
        var session = DbFactory.getSessionFactory().openSession();
        var builder = session.getCriteriaBuilder();
        var query = builder.createQuery(Warning.class);
        var root = query.from(Warning.class);

        query.select(root);
        query.where(
                builder.and(
                        builder.equal(root.get("Player"), player),
                        builder.greaterThan(root.get("Expiration"), new Date())
                )
        );
        query.orderBy(builder.desc(root.get("DateCreated")));
        return session.createQuery(query).getResultList();
    }

results in the following error: java.lang.ClassCastException: class java.util.Date cannot be cast to class java.time.LocalDateTime (java.util.Date and java.time.LocalDateTime are in module java.base of loader 'bootstrap')

Hibernate seems to have no problem inserting a LocalDatetime into the database, why can’t I get one back from the database?

Both the Expiration and DateCreated columns have data type “timestamp without time zone” in postgre

Advertisement

Answer

You said:

Both the Expiration and DateCreated columns have data type “timestamp without time zone” in postgre

Wrong type in database

You are using the wrong data type for your database columns.

The TIMESTAMP WITHOUT TIME ZONE Postgres type purposely lacks any concept of a time zone or offset-from-UTC. So this type cannot be used to represent a moment, cannot be a specific point on the timeline.

When tracking a moment, a specific point on the timeline, always use a column of type TIMESTAMP WITH TIME ZONE.

Wrong type in Java

Likewise, you are using the wrong Java class.

The LocalDateTime class, purposely lacks any concept of a time zone or offset-from-UTC. So this type cannot be used to represent a moment, cannot be a specific point on the timeline.

Instead use OffsetDateTime class to retrieve values from a column of TIMESTAMP WITH TIME ZONE.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

Avoid legacy date-time classes

Both Date classes are now legacy, years ago supplanted by the modern java.time classes defined in JSR 310.

There is no need to use the legacy classes. Their functionality is entirely replaced by the java.time classes.

  • java.util.Date is replaced by java.time.Instant.
  • java.sql.Date is replaced by java.time.LocalDate.

For database work, use:

  • java.time.OffsetDateTime for database columns of a type akin to the SQL-standard type TIMESTAMP WITH TIME ZONE.
  • java.time.LocalDateTime for database columns of a type akin to the SQL-standard type TIMESTAMP WITHOUT TIME ZONE.

JDBC 4.2+, Jakarta Persistence (JPA), and Hibernate have all been updated to support java.time.

Advertisement