如何将 TIMESTAMP 列映射到 ZonedDateTime JPA 实体属性?
Posted
技术标签:
【中文标题】如何将 TIMESTAMP 列映射到 ZonedDateTime JPA 实体属性?【英文标题】:How to map a TIMESTAMP column to a ZonedDateTime JPA entity property? 【发布时间】:2019-11-01 00:43:34 【问题描述】:我正在使用 Spring data jpa 和 mariadb 最新版本,以及 MariaDB 10.3.16
+--- org.springframework.boot:spring-boot-starter-data-jpa -> 2.1.5.RELEASE
...
| +--- org.springframework.boot:spring-boot-starter-jdbc:2.1.5.RELEASE
...
| +--- org.hibernate:hibernate-core:5.3.10.Final
这是我的实体:
@Entity
@Data
@Table
@NoArgsConstructor
@AllArgsConstructor
public class Note
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
@Column
private String gsn;
@Column
@Enumerated(EnumType.STRING)
private NoteType type;
@Column
private String text;
@Column
private ZonedDateTime scheduleDt;
@Column
@CreationTimestamp
private Instant createDt;
@Column
@UpdateTimestamp
private ZonedDateTime updateDt;
当我持久化我的实体时,Hibernate 会尝试将 ZonedDateTime
成员保存为 DATETIME 列。但我想使用 TIMESTAMP 列而不是 DATETIME 列。
这是创建 DDL,我从日志中看到的。
create table `note` (`id` integer not null, `create_dt` datetime,
`gsn` varchar(255), `schedule_dt` datetime, `text` varchar(255),
`type` varchar(255), `update_dt` datetime, primary key (`id`))
engine=MyISAM
这里create_dt
、schedule_dt
、update_dt
被创建为datetime
列类型,这不是我想要的。 (我也不喜欢 MyISAM)。
我该如何解决?
由于注释无法表达ddl而添加。
当我使用 columnDefinition 属性时,生成的 ddl 是 ...
create table `note` (`id` integer not null, `create_dt` datetime,
`gsn` varchar(255), `schedule_dt` datetime, `text` varchar(255),
`type` varchar(255), `update_dt` `TIMESTAMP`, primary key (`id`))
engine=MyISAM
TIMESTAMP 周围有不需要的“`”。
【问题讨论】:
不是很相关,但为什么要使用 ZonedDateTime 而不是 Instant 或 LocalDateTime?时间戳不会在任何地方存储存储在 ZonedDateTime 中的时区,因此您将无法使用原始时区取回 ZonedDateTime。 @JB 是的,你是对的。时区不可恢复。无论如何 Instant,LocalDateTime 也不起作用。生成的 DDL 使用 DATETIME 列类型。 @galex:您的内联代码格式更改很好,但软件名称和版本并没有通过将它们加粗来提高可读性。一般来说这是没有必要的。 好的,谢谢你的建议,我是***的新手 与问题无关,但永远不要将 lombok 的 Data 或 EqualsAndHashcode 用于实体。您可以检查反编译的文件:lombok 不理解实体。实体不是价值对象。 Lombok 对 equals 和 hascode 的实现既不正确,又是潜在的性能杀手。检查例如@vlad-mihalcea blog vladmihalcea.com/… 以获得正确的实施。 【参考方案1】:JPA 2.2 支持映射 Java 8 日期/时间 API,但仅适用于以下类型:
java.time.LocalDate
java.time.LocalTime
java.time.LocalDateTime
java.time.OffsetTime
java.time.OffsetDateTime
不过,Hibernate 也支持ZonedDateTime
,就像这样。
保存ZonedDateTime
时,以下Timestamp
将被发送到PreparedStatement
:
Timestamp.from( zonedDateTime.toInstant() )
并且,当从数据库中读取它时,ResultSet
将包含一个Timestamp
,它将被转换为一个ZonedDateTime
,如下所示:
ts.toInstant().atZone( ZoneId.systemDefault() )
注意ZoneId
没有存储在数据库中,所以基本上,如果Timestamp
转换不适合您,您最好使用LocalDateTime
。
所以,假设我们有以下实体:
@Entity(name = "UserAccount")
@Table(name = "user_account")
public class UserAccount
@Id
private Long id;
@Column(name = "first_name", length = 50)
private String firstName;
@Column(name = "last_name", length = 50)
private String lastName;
@Column(name = "subscribed_on")
private ZonedDateTime subscribedOn;
//Getters and setters omitted for brevity
注意subscribedOn
属性是一个ZonedDateTime
Java 对象。
当持久化UserAccount
:
UserAccount user = new UserAccount()
.setId(1L)
.setFirstName("Vlad")
.setLastName("Mihalcea")
.setSubscribedOn(
LocalDateTime.of(
2020, 5, 1,
12, 30, 0
).atZone(ZoneId.systemDefault())
);
entityManager.persist(user);
Hibernate 生成正确的 SQL INSERT 语句:
INSERT INTO user_account (
first_name,
last_name,
subscribed_on,
id
)
VALUES (
'Vlad',
'Mihalcea',
'2020-05-01 12:30:00.0',
1
)
在获取UserAccount
实体时,我们可以看到ZonedDateTime
已从数据库中正确获取:
UserAccount userAccount = entityManager.find(
UserAccount.class, 1L
);
assertEquals(
LocalDateTime.of(
2020, 5, 1,
12, 30, 0
).atZone(ZoneId.systemDefault()),
userAccount.getSubscribedOn()
);
【讨论】:
JDBC 没有定义对ZonedDateTime
的支持,这是一个非标准扩展,JPA 2.2 也没有定义对它的支持(检查您自己的博文和您从该博文链接的 JPA 2.2 更改日志) .
我更新了答案。基本上,Hibernate 支持ZonedDateTime
,我解释了它是如何做到的,即使 JDBC 不提供对这种类型的本机支持。【参考方案2】:
您可以使用@Column 注释定义列类型:
@Column(columnDefinition="TIMESTAMP")
@UpdateTimestamp
private ZonedDateTime updateDt;
【讨论】:
这会产生错误。 DDL 是... 创建表note
(id
integer not null auto_increment, create_dt
datetime(6), gsn
varchar(255), schedule_dt
TIMESTAMP
, text
varchar(255), type
varchar(255), update_dt
datetime(6), 主键 (id
)) engine=InnoDB
引擎很可能不同:您的引擎是 InnoDB,但是使用了有问题的定义 MyISAM。
@SimonMartinelli 我在上面添加了 ddl,因为注释删除了 '`'以上是关于如何将 TIMESTAMP 列映射到 ZonedDateTime JPA 实体属性?的主要内容,如果未能解决你的问题,请参考以下文章