【问题标题】:Hibernate 5.4 - unable to persist Java InstantHibernate 5.4 - 无法持久化 Java Instant
【发布时间】:2021-03-18 08:24:17
【问题描述】:

我有一个基于 Spring Boot 2.3.4 构建的 Kotlin 应用程序。此版本的 Spring Boot 包括 Hibernate 5.4.21。我还导入了 mysql-connector-java 8.0.21。

每当我尝试将 Instant 对象持久保存到我的数据库中时,都会出现以下异常:

com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Incorrect datetime value: '\xAC\xED\x00\x05sr\x00\x0Djava.time.Ser\x95]\x84\xBA\x1B"H\xB2\x0C\x00\x00xpw\x0D\x02\x00\x00\x00\x00_\xCDvA)\xE6Jxx' for column 'myDateTimeColumn' at row 1

这是我试图在 Kotlin 中坚持的对象;它只有两个字段:

@Entity
class RoomEntity{
    @Id
    lateinit var roomId: String
    lateinit var lastAccessedAt: Instant
}

我试图将其保存到的表有两列:roomId 是 CHAR(7),lastAccessedAt 是 TIMESTAMP。

保存的代码只有两行:

val sql = "INSERT INTO mydb.rooms (roomId, lastAccessedAt) VALUES (?, ?);"
db.update(sql, "1234567", Instant.now())

我看过other SO posts 类似于我的问题,但答案都说要更新到我正在使用的Hibernate 5.2+ 版本。什么可能导致此异常?

【问题讨论】:

  • 您的实体和架构是什么样的?
  • 升级Hibernate后,是升级了schema还是设置了持久化单元自动更新schema? Hibernate 5.2+ 原生支持 Java 8 类型,所以问题可能是由于模式和实体类型不匹配造成的?如果您可以共享相关实体注释和数据库架构,将会很有帮助。
  • @alex 我用一些额外的数据更新了这个问题。

标签: hibernate kotlin jpa mysql-connector


【解决方案1】:

MySQL 的时间戳默认精度与 SQL 标准所期望的不同。另见此处:https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html

fsp 值(如果给定)必须在 0 到 6 的范围内。值 0 表示没有小数部分。如果省略,则默认精度为 0。(这与标准 SQL 默认值 6 不同,是为了与以前的 MySQL 版本兼容。)

您可以通过在方言中注册自定义类型来解决此问题,例如

public class SaneMySQLDialect extends MySQL5InnoDBDialect {

    public SaneMySQLDialect() {
        registerColumnType( Types.TIME, "time(6)" );
        registerColumnType( Types.TIMESTAMP, "datetime(6)" );
    }

    @Override
    public String getTableTypeString() {
        return " ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_bin";
    }
}

【讨论】:

  • 但是 Hibernate 5.2+ 原生支持 Java 8 时间,所以你不再需要这个库了。
  • 我会试试这个并回复你;谢谢!
【解决方案2】:

我从来没有弄清楚如何在数据库中存储 Instant。在我的数据访问层中简单地使用java.sql.Timestamp 会容易得多。

所以我的实体看起来像这样:

@Entity
class RoomEntity{
    @Id
    lateinit var roomId: String
    lateinit var lastAccessedAt: Timestamp

    fun toRoom(): Room {
        return Room(roomId, lastAccessedAt.toInstant());
    }
}

我使用此代码保留一个新的 Room 行:

val sql = "INSERT INTO mydb.rooms (roomId, lastAccessedAt) VALUES (?, ?);"
db.update(sql, room.roomId, Timestamp.from(room.lastAccessedAt))

我的房间类如下所示:

data class Room(var roomId: String, var lastAccessedAt: Instant)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-12
    • 2013-01-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多