我们有一个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
...
我的问题是:
答案 0 :(得分:2)
正如@thatotherguy在评论中指出的那样,AbstractUUIDGenerator
和UUIDHexGenerator
的Hibernate实现远非符合RFC-4122。在我仔细研究之前,我从未真正意识到实施的糟糕程度。
除此之外,根据其实施情况,您的问题的根本原因归结为UUIDHexGenerator
s使用InetAddress.getLocalHost()
(通过AbstractUUIDGenerator
)来提出& #34;独特"值。如果您的主机名的名称查找结果为127.0.0.1(例如,它在您的/etc/hosts
文件中),或者主机名是" local",则它将使用它。
您有几个选择:
您可以更新/etc/hosts
以包含主机名的LAN IP(如果这是一个选项)。不过,你不会使用正确的UUID(与下一点的最后一部分相同)。
如果Hibernate的算法不足,您可以定义自定义IdentifierGenerator
并提供更适合您任务的更好的UUID生成算法。我将基于Java的内置UUID
,这是合规的。但是,您可能可能" hack"通过扩展UUIDHexGenerator
并覆盖protected int getIP()
来返回准确的IP地址。这是一个黑客,因为AbstractUUIDGenerator
s implementation(您的getIP()
将不再返回其IP
实例字段的值),因为它仍然不是一个合适的UUID。这可能就足够了,但我不推荐它。
不使用生成器,而是指定手动ID分配,并自行生成UUID。同样,Java UUID
可以在这里为你工作。
有一个较新的UUID生成器策略" uuid2",它使用UUIDGenerator
。这是3.6中的新功能,3.3.2中没有。它的来源is available。我之前没有使用过这个策略而且不能说话;然而,正如Andrew Stein在下面的评论中观察到的那样,对来源的检查显示它provides a strategy built around Java's UUID
,使其成为一个好赌注,并且肯定比旧AbstractUUIDGenerator
派生的变体更好。< / p>
选项1是最简单的快速修复,如果它适用于您,但可能有维护/部署问题,并且,再次,并没有真正生成格式良好的UUID。从长远来看,使用UUID
(或选项4以及相应策略)的选项2可能是最正确的。
有一个article describing various UUID assignment strategies for Hibernate,其中可能包含一些更有用的见解和示例。