如何在Java中复制PostgreSQL的uuid_generate_v3()?

时间:2016-11-15 17:28:19

标签: java postgresql uuid

的PostgreSQL:

 create extension if not exists "uuid-ossp";
 select uuid_generate_v3(uuid_nil(), 'this is a test');
           uuid_generate_v3           
--------------------------------------
 e1e27115-9f5b-366d-90e8-e07b1b36b99c
(1 row)

爪哇:

java> java.util.UUID.nameUUIDFromBytes("this is a test".getBytes());
java.util.UUID res9 = 54b0c58c-7ce9-32a8-b551-351102ee0938

如何在Java中生成与PostgreSQL相同的UUID?

3 个答案:

答案 0 :(得分:1)

据我所知,没有直接的方式,你必须深入研究PostgreSQL(及其库)的源代码,并自己用Java复制算法,或者调用本地相同的库函数。一个更简单的选择是让PostgreSQL数据库为您生成它们,但我认为由于某种原因这是不可能的。

the documentation可以看出,PostgreSQL首先采用MD5哈希来防止逆向工程。然后,他们使用OSSP UUID库中的UUID生成器。

我试过了:

1> select newid(),newid(1)
2> go

 ------------------------------------ ------------------------------------
 086ae8a4920944dcb45f81b8c983b3f1     3c23dabf-d989-48dc-807b-81b8c983b3f1

但那产生了:

java.util.UUID.nameUUIDFromBytes(Md5Utils.getMd5Digest("this is a test"));

答案 1 :(得分:1)

此处https://tools.ietf.org/html/rfc4122#section-4.3

描述了用于生成版本3 UUID的算法

但是关键步骤是:

  • 分配一个UUID用作所有UUID的“名称空间ID” 从该名称空间中的名称生成。
  • 选择MD5或SHA-1作为哈希算法
  • 将名称转换为八位字节的规范序列
  • 计算与名称连接的名称空间ID的哈希值。
  • 将的某些字节更改为预定义的值(请参见上面的链接)
  • 将生成的UUID转换为本地字节顺序。

postgres函数签名为uuid_generate_v3(namespace uuid, name text),因此它将名称空间UUID和name作为参数。
Java方法nameUUIDFromBytes(byte[] name)仅采用name并用MD5对其进行哈希处理以创建UUID。为了获得与PostgreSQL相同的输出,您必须自己将名称空间字节和name字节连接在一起。

对于命名空间,您使用了uuid_nil()(在Java中为new UUID(0L, 0L))。

将所有内容放在一起看起来像这样:

byte[] bytes = Arrays.concatenate(toByteArray(new UUID(0L, 0L)), "this is a test".getBytes(StandardCharsets.UTF_8));
System.out.println(UUID.nameUUIDFromBytes(bytes)); // prints out e1e27115-9f5b-366d-90e8-e07b1b36b99c

您可以将名称空间UUID转换为字节数组,如下所示:

private static byte[] toByteArray(UUID uuid) {
    ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    return bb.array();
}

答案 2 :(得分:0)

尽管这不是原始问题的答案,但是我不得不以另一种方式进行此转换,即在迁移过程中模仿PostgreSQL中的UUID.nameUUIDFromBytes行为:

CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE EXTENSION IF NOT EXISTS "plpgsql";

CREATE OR REPLACE FUNCTION uuidv3(bytes bytea)
  RETURNS uuid
  RETURNS null on null input
  STABLE
  PARALLEL SAFE
AS $$
DECLARE
  md5bytes  bytea;
BEGIN
  md5bytes := digest(bytes, 'md5');
  --       md5Bytes[6]  &= 0x0f;  /* clear version        */
  --       md5Bytes[6]  |= 0x30;  /* set to version 3     */
  md5bytes := set_byte(md5bytes, 6, (get_byte(md5bytes, 6) & x'0F'::int) | x'30'::int);
  --       md5Bytes[8]  &= 0x3f;  /* clear variant        */
  --       md5Bytes[8]  |= 0x80;  /* set to IETF variant  */
  md5bytes := set_byte(md5bytes, 8, (get_byte(md5bytes, 8) & x'3F'::int)|  x'80'::int);
  RETURN (encode(md5bytes, 'hex'))::uuid;
END
$$
  LANGUAGE plpgsql;

,您应该使用内部textsend将文本列转换为bytea

SELECT uuidv3(textsend('this is a test'));

尽管我必须承认我从未尝试过其他方法。