【问题标题】:Generate unique, random alphanumeric primary key of length 10生成长度为 10 的唯一、随机字母数字主键
【发布时间】:2017-11-09 01:37:44
【问题描述】:

我想使用 Hibernate 为随机实体生成唯一主键 - 用于支付确认号码、酒店房间预订号码、礼品卡代码等目的 - 任何时候客户面临唯一需要值来识别实体。

它们不应该被最终用户预测,但不需要是加密安全的。长度必须是 10 个字母数字字符,全部大写。

有很多类似的问题,但似乎都没有真正解决问题。我不可能是第一个遇到这个问题的人。

我想避免只生成随机字母数字字符串并检查它是否存在于数据库中的蛮力方法。

【问题讨论】:

  • 如果它是随机的,那么根据定义它不能保证是唯一的。你有这样一个特定的格式要求,因此不能使用 GUID 有什么原因吗?
  • 例如,因为用户可能需要使用它来输入字段,并且长度会令人望而却步。我不一定需要随机,只是不可预测(或者至少,不容易预测。换句话说,我不希望我们数据库中的顺序整数用于订单号)

标签: java sql hibernate jpa random


【解决方案1】:

您可以生成 10 位 base-36 数字。为了使它们不可预测,您可以使用与 36^10 相对质数的任何值跳过它们,而不是一个接一个。

例如:

public class Unique {
    private static final long SKIP = 1656158440062971L;
    private static final long MOD  = 3656158440062976L;
    private static final long BASE = 36L;

    public static class ExhaustedException extends RuntimeException {
        public ExhaustedException() { super("No more codes"); }
    }

    private long currentValue = 0L;

    public static void main(String[] args) {
        int max = Integer.parseInt(args[0]);
        Unique generator = new Unique();
        for (int i = 0; i < max; i++) {
            System.out.println(generator.nextCode());
        }
    }

    public synchronized String nextCode() {
        currentValue = (currentValue + SKIP) % MOD;
        if (currentValue == 0L) {
            throw new ExhaustedException();
        }
        return codeFromLong(currentValue);
    }

    private String codeFromLong(long value) {
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            int digit = (int) (value % BASE);
            code.insert(0, charFromDigit(digit));
            value /= BASE;
        }
        return code.toString();
    }

    private char charFromDigit(int digit) {
        if (digit < 10) return (char) ('0' + digit);
        return (char) ('A' + (digit - 10));
    }
}

这将以明显随机的顺序生成 10 个字符的字母数字代码。它将在 3,656,158,440,062,976(3.6 万亿)个代码之后重复。为了防止重复,在它生成一个 0 然后重复之前它会抛出一个ExhaustedException。保证每个代码只访问一次,因为 1,656,158,440,062,971 是总循环长度的质数。

我通过将第一个数字更改为 1 并调整最后一个数字来生成此值。这个数到底是什么并不重要,它不需要是素数,但它不能与 36^10 共享任何公因数(必须是互质数),它应该很大但不接近 36 ^10 以便每次转动曲柄时大部分或所有数字都会改变。

这是一个独立的程序,但您可能希望将当前值放入数据库中,甚至可能将此代码放在存储过程后面,在这种情况下,您可能希望它返回一些不同的标记值以指示序列已用尽。我已经制作了生成下一个代码synchronized的方法;您需要以一种或另一种方式确保没有两个调用者收到相同的代码。

注意:这意味着这可能会成为您应用的瓶颈。

我还会在它周围加上一个包装器,并检查咒语的停止列表。您不想给客户一个包含四个字母或其他冒犯性词的代码。如果代码包含作为子字符串的坏词,只需将其丢弃并生成下一个代码。

【讨论】:

  • 我知道已经晚了,但这是一个很好的答案。
  • @Bassinator 迟到总比不到好!谢谢!
猜你喜欢
  • 2011-02-17
  • 2017-06-17
  • 2011-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-21
  • 1970-01-01
  • 2012-03-02
相关资源
最近更新 更多