【问题标题】:Mapping Java byte[] to MySQL binary(64) in Hibernate在 Hibernate 中将 Java byte[] 映射到 MySQL binary(64)
【发布时间】:2011-08-23 20:06:42
【问题描述】:

我在将字节数组映射到 Hibernate 中的 MySQL 数据库时遇到了一些问题,我想知道我是否遗漏了一些明显的东西。我的班级大致是这样的:

public class Foo {
    private byte[] bar;

    // Getter and setter for 'bar'
}

在 MySQL 5.5 中表是这样定义的:

CREATE TABLE foo (
bar BINARY(64) NOT NULL)

Hibernate 3.6.2 的映射看起来与此类似:

<hibernate-mapping>
    <class name="example.Foo" table="foo">
        <property name="bar" column="bar" type="binary" />
    </class>
</hibernate-mapping>

我只使用 hbm2ddl 进行验证,当我部署应用程序时它给了我这个错误:

Wrong column type in foo for column bar. Found: binary, expected: tinyblob

如果在映射中使用 type="binary" 不会导致 Hibernate 期望列的类型是二进制(而不是 tinyblob),我不知道会发生什么。我花了一些时间谷歌搜索,但找不到确切的错误。类似错误的解决方案是...

  1. 上指定“长度”。这会改变 Hibernate 所期望的类型,但它始终是各种 blob,而不是它找到的“二进制”类型。
  2. 不要在属性元素上声明“类型”,而是嵌套一个列元素并为其赋予一个 sql-type 属性。那是可行的,但这也会使绑定特定于 MySQL,所以如果可能的话,我想避免它。

此设置有什么突出的地方会导致这种不匹配吗?如果我指定 type="binary" 而不是 "blob",为什么 Hibernate 需要一个 blob 而不是二进制文件?

【问题讨论】:

  • 二进制类型让我对 Java/Hibernate 感到无尽的沮丧。通常我解决这个问题的方法是在 Java 端对二进制数据进行 Base64 编码,然后将其作为TEXT 字段存储在数据库中。然后你的实体中的属性可以是String 类型,一切都会更加顺利。
  • 是的,字节数组在 Hibernate 中似乎确实很麻烦。映射明确告诉 Hibernate 期望二进制作为类型,但它正在寻找不同类型的 blob,这不是一个错误吗?确实非常令人沮丧!
  • 这不是明确告诉 Hibernate 使用二进制文件。 property 元素的 type 属性不用于指定 SQL 数据类型。我在下面的回答中对此进行了解释。我无法评论 Hibernate 在设置为使用 BINARY 数据类型(正确)时的行为方式,我没有尝试过。
  • 由于 MySQL 的二进制类型似乎被 Hibnerate “孤立”了,并且变通办法似乎是映射它们的唯一方法,我将其发布到 Hibernate 论坛。 forum.hibernate.org/viewtopic.php?f=1&t=1011076 我有点希望这被认为是一个错误并在将来修复。

标签: mysql hibernate hbm2ddl hbm


【解决方案1】:

我认为问题出在type="binary"

该类型是休眠的泛型类型。它不直接映射到特定于数据库引擎的类型。根据您使用的驱动程序,它们被转换为不同的 SQL 类型。显然,MySQL 驱动程序将休眠类型“二进制”映射到一个 tinyblob。

完整的休眠类型列表可在here获得。

您有 2 个选项。您可以更改 CREATE TABLE 脚本以使用 tinyblob 数据类型存储该列。然后你的休眠验证不会失败,你的应用程序就会工作。这将是建议的解决方案。

仅当您必须在 DB 中使用 BINARY 数据类型时才应使用第二个选项。您可以做的是在休眠映射中指定一个 sql 类型,以便强制休眠使用您想要的类型。映射如下所示:

<property name="bar">
  <column name="bar" sql-type="binary" />
</property>

这样做的主要缺点是您失去了 DB 引擎的独立性,这就是大多数人首先使用休眠的原因。此代码仅适用于具有 BINARY 数据类型的数据库引擎。

【讨论】:

  • 你是对的 - type="binary" 绝对是问题所在。我只是(错误地)假设由于既有 blob Hibernate 类型和二进制 Hibernate 类型,“blob”Hibernate 类型将对应于各种 MySQL blob 类型,而二进制将对应于二进制。不幸的是,似乎没有任何 Hibernate 类型对应于 MySQL 二进制文件,这似乎是一个相当大的疏忽。
【解决方案2】:

我们最终解决类似问题的方法是编写我们自己的自定义 UserType。

UserTypes 相对容易实现。只需创建一个实现 org.hibernate.usertype.UserType 的类并实现 @override 方法。

在您的休眠定义中,使用用户类型非常简单:

<property name="data" type="com.yourpackage.hibernate.CustomBinaryStreamUserType" column="binary_data" />

简单地说,它会执行这个类来读取和写入数据库中的数据。具体来说,使用了 nullSafeGet 和 nullSafeSet 方法。

在我们的例子中,我们使用它在将二进制数据写入数据库之前对其进行 gzip 压缩,并在读取时将其解压缩。这隐藏了使用此数据从应用程序压缩数据这一事实。

【讨论】:

  • A UserType 很容易编写并且肯定会工作,但它似乎并不比问题中的选项 2 更与数据库无关 - 用嵌套的 声明 sql 类型属性。不同之处在于,使用“选项 2”是一种单行解决方案,不会向项目引入更多代码(错误的机会)。虽然我会说,在您的 gzipping 数据示例中,UserType 似乎是最好的地方。我认为这很聪明。
【解决方案3】:

我认为在休眠中映射二进制列有一个简单的解决方案。

“BINARY”列可以很容易地映射到休眠实体类中的“java.util.UUID”。

例如列定义看起来像

`tokenValue` BINARY(16) NOT NULL

Hibernate Entitiy 将有以下代码来支持 BINARY 列

private UUID tokenValue;

@Column(columnDefinition = "BINARY(16)", length = 16)
public UUID getTokenValue() {
    return this.tokenValue;
}

public void setTokenValue(UUID sessionTokenValue) {
    this.tokenValue = tokenValue;
}

【讨论】:

  • 指定列的 SQL 定义是一个已经讨论过的选项(包括在问题中。)
猜你喜欢
  • 2013-07-15
  • 2016-07-20
  • 2011-11-14
  • 2019-09-18
  • 2011-03-11
  • 2016-02-25
  • 2011-05-13
  • 1970-01-01
  • 2012-04-06
相关资源
最近更新 更多