Hibernate生成重复的UUID

时间:2014-11-24 17:19:16

标签: java linux hibernate

我们有一个6节点的Red Hat 4.4.7 / Linux 2.6.32网络,每个节点运行一个Java应用程序,使用Hibernate 3.3.2.GA在中央Oracle数据库中创建记录。

我们遇到了Hibernate生成重复UUID的问题。

有问题的Java类定义如下:

@Entity
@Table(name = "X_Y")
@GenericGenerator(name = "x-y-uuid", strategy = "uuid")
public class XY implements ... {
    @Id
    @Column(name = "X_Y_ID")
    @GeneratedValue(generator = "x-y-uuid")
    private String id;
    ...
}

使用我们已成功使用一段时间的定义,我们遇到了重复X_Y_ID键的问题。我们禁用了对X_Y_ID的唯一约束并重新启动了该进程。与此同时,我们开始在我们的代码以及Hibernate代码中挖掘可能的错误。阅读Hibernate的UUIDHexGenerator,看来UUID的前8个字符基于机器IP地址,后8个字符基于JVM开始时间。

在完成对X_Y_ID的禁用唯一约束的过程之后,我们对生成的UUID进行了一些分析。我们发现实际上有59个重复的X_Y_ID值。 令我们惊讶的是,查询:

select SUBSTR(X_Y_ID,1,8), COUNT(*)
from X_Y
group by SUBSTR(X_Y_ID,1,8)

表示所有6台机器都有相同的前8个字符。查询:

select SUBSTR(X_Y_ID,9,8), COUNT(*)
from X_Y
group by SUBSTR(X_Y_ID,9,8)

,得到

"49d99de6"  2148309
"49d99e3c"  2044966
"49d99def"  2228095
"49d99df2"  2091068
"49d99dee"  4110661

如您所见,有5行,最后一行的行数约为两倍。这本身就不足为奇了。 (这意味着两台不同机器上的JVM在彼此相差256毫秒内启动)。

进一步调查显示,前八个字符ff808081生成的值对应于本地主机127.0.0.1的IP地址。

在其中一台机器上运行ifconfig给出(例如):

eth0      Link encap:Ethernet  HWaddr 00:50:56:81:2C:20  
          inet addr:10.191.8.50  Bcast:10.191.63.255  Mask:255.255.192.0
          inet6 addr: fe80::250:56ff:fe81:2c20/64 Scope:Link
          ...

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          ...

我的问题是:

  • Hibernate看到的IP地址怎么可能是127.0.0.1而不是说10.191.8.50?
  • 我们可以做些什么来防止在部署的系统上出现这种情况?

1 个答案:

答案 0 :(得分:2)

正如@thatotherguy在评论中指出的那样,AbstractUUIDGeneratorUUIDHexGenerator的Hibernate实现远非符合RFC-4122。在我仔细研究之前,我从未真正意识到实施的糟糕程度。

除此之外,根据其实施情况,您的问题的根本原因归结为UUIDHexGenerators使用InetAddress.getLocalHost()(通过AbstractUUIDGenerator)来提出& #34;独特"值。如果您的主机名的名称查找结果为127.0.0.1(例如,它在您的/etc/hosts文件中),或者主机名是" local",则它将使用它。

您有几个选择:

  1. 您可以更新/etc/hosts以包含主机名的LAN IP(如果这是一个选项)。不过,你不会使用正确的UUID(与下一点的最后一部分相同)。

  2. 如果Hibernate的算法不足,您可以定义自定义IdentifierGenerator并提供更适合您任务的更好的UUID生成算法。我将基于Java的内置UUID,这是合规的。但是,您可能可能" hack"通过扩展UUIDHexGenerator并覆盖protected int getIP()来返回准确的IP地址。这是一个黑客,因为AbstractUUIDGenerators implementation(您的getIP()将不再返回其IP实例字段的值),因为它仍然不是一个合适的UUID。这可能就足够了,但我不推荐它。

  3. 不使用生成器,而是指定手动ID分配,并自行生成UUID。同样,Java UUID可以在这里为你工作。

  4. 有一个较新的UUID生成器策略&#34; uuid2&#34;,它使用UUIDGenerator。这是3.6中的新功能,3.3.2中没有。它的来源is available。我之前没有使用过这个策略而且不能说话;然而,正如Andrew Stein在下面的评论中观察到的那样,对来源的检查显示它provides a strategy built around Java's UUID,使其成为一个好赌注,并且肯定比旧AbstractUUIDGenerator派生的变体更好。< / p>

  5. 选项1是最简单的快速修复,如果它适用于您,但可能有维护/部署问题,并且,再次,并没有真正生成格式良好的UUID。从长远来看,使用UUID(或选项4以及相应策略)的选项2可能是最正确的。

    有一个article describing various UUID assignment strategies for Hibernate,其中可能包含一些更有用的见解和示例。