java - 生成长度为 10 的唯一、随机字母数字主键

标签 java sql hibernate jpa random

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

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

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

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

最佳答案

您可以生成 10 位 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;您需要以某种方式确保没有两个调用者收到相同的代码。

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

我还会在它周围放置一个包装器并检查咒骂词的非索引字表。您不想为客户提供包含四个字母或其他攻击性单词的代码。如果代码包含坏词作为子字符串,只需将其丢弃并生成下一个代码。

关于java - 生成长度为 10 的唯一、随机字母数字主键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47192507/

相关文章:

java - 发生错误时无限循环

php - 通过 jquery 显示 html 选择(来自 PHP)?

mysql - 使用 SQL RAND() 生成受约束的 DISTINCT 元素

mysql - SQL Server 2008 R2 Express 行号

java - 如何从 spring mvc 中的 Controller 类返回两个列表

hibernate - EntityManager.createQuery() 抛出 NullPointerException

java - 将 ArrayList 的最后 3 个元素复制到 List<String> 中

java - 是否可以简化这个 try catch ?

java - 按类别获取页面上的所有 tr

java - JPA 在多对多关系中按列排序