如何在 JPA 2 实体中映射 postgresql“带有时区的时间戳”

Posted

技术标签:

【中文标题】如何在 JPA 2 实体中映射 postgresql“带有时区的时间戳”【英文标题】:How to map postgresql "timestamp with time zone" in a JPA 2 entity 【发布时间】:2012-11-01 16:33:12 【问题描述】:

我有一个使用 Postgresql(带有 9.0-801.jdbc3 JDBC 驱动程序)的 JPA 2 应用程序(使用 Hibernate 3.6 作为 JPA 实现)。

我在将“带时区的时间戳”字段映射到我的 JPA 实体时遇到问题。

这是一个例子:

CREATE TABLE theme
(
  id serial NOT NULL,
  # Fields that are not material to the question have been edited out
  run_from timestamp with time zone NOT NULL,
  run_to timestamp with time zone NOT NULL,
  CONSTRAINT theme_pkey PRIMARY KEY (id ),
  CONSTRAINT theme_name_key UNIQUE (name )
)

我尝试如下映射:

@Entity
@Table(schema = "content", name = "theme")
public class Theme extends AbstractBaseEntity 
    private static final long serialVersionUID = 1L;

    @Column(name = "run_from")
    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    private Date runFrom;

    @Column(name = "run_to")
    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    private Date runTo;

    /* The rest of the entity has been edited out */

我不断收到异常,根本原因如下:Caused by: org.hibernate.HibernateException: Wrong column type in public.backend_themetopic for column created. Found: timestamptz, expected: date

我的尝试

java.util.Date 替换java.util.Calendar - 没有区别 使用java.sql.Timestamp - 抱怨我无法将@Temporal 注释应用于Timestamp 使用 org.joda.time.DateTime 和自定义 @Type 注释 (@Type(type="org.joda.time.contrib.hibernate.PersistentDateTimeTZ")) 也不起作用

约束

此应用程序与“旧系统”交互 - 因此,更改日期字段的类型不是一个好的选择

我的问题是:我应该如何将这些时区感知时间戳映射到我的 JPA 实体中?

【问题讨论】:

【参考方案1】:

我最终通过关闭模式验证以一种骇人听闻的方式完成了这项“工作”。

以前,我的 persistence.xml 中有 <property name="hibernate.hbm2ddl.auto" value="validate"/>"hibernate.hbm2ddl.auto"。当我注释掉这个属性时,我的应用服务器启动并且模型“工作”了。

我的实体的最终形式是:

@Entity
@Table(schema = "content", name = "theme")
public class Theme extends AbstractBaseEntity 
    private static final long serialVersionUID = 1L;

    @Column(name = "run_from", columnDefinition = "timestamp with time zone not null")
    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    private Date runFrom;

    @Column(name = "run_to", columnDefinition = "timestampt with time zone not null")
    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    private Date runTo;

    /* Getters, setters, .hashCode(), .equals() etc omitted */

在阅读了相当多的内容后,我的印象是没有简单的方法将 Postgresql 时间戳与时区列映射。

一些 JPA 实现 + 数据库组合本身支持这一点(EclipseLink + Oracle 就是一个例子)。对于休眠,使用 jodatime 扩展,可以使用普通时间戳 + 时区的 varchar 字段来存储时区感知时间戳(我不能这样做,因为我无法更改数据库模式)。 Jadira user types 或完全自定义的用户类型也可以用来解决这个问题。

我需要注意,我对这个实体的用例是“只读的”,所以我可以用一个看似幼稚的“解决方案”逃脱。

【讨论】:

【参考方案2】:

添加@Column(columnDefinition= "TIMESTAMP WITH TIME ZONE")

@Column(name = "run_from", columnDefinition= "TIMESTAMP WITH TIME ZONE")
@NotNull
@Temporal(TemporalType.TIMESTAMP)
private Date runFrom;

【讨论】:

这实际上仅对 DDL 自动还是在运行时有用吗? columnDefinition 值仅在 DDL 中使用。但是,如果您的表列是使用该 DDL 创建的,它也会间接影响运行时行为的细节。

以上是关于如何在 JPA 2 实体中映射 postgresql“带有时区的时间戳”的主要内容,如果未能解决你的问题,请参考以下文章

jpa 实体bean中如何定义非映射字段

如果相同的表但不同的模式,如何避免重复 jpa 映射(实体、服务、存储库..)?

如何在运行时检索 JPA 中实体的映射表名称?

如何在 JPA 中映射名称为保留字的实体字段

JPA,如何使用同一个类(实体)来映射不同的表?

如何使用Spring Data JPA在实体中映射Oracle Object类型?