我想使用Hibernate为随机的实体生成唯一的主键 - 用于付款确认号,酒店房间预订号,礼品卡代码等目的 - 任何时候客户都面临独特的需要价值来识别实体。
最终用户不应该预测它们,但不需要加密安全。长度必须是10个字母的字母数字,使用全部大写字母。
有很多类似的问题,但它们似乎都没有真正解决问题。我不能成为第一个遇到这个问题的人。
我想避免使用蛮力方法生成随机字母数字字符串并检查它是否存在于数据库中。
答案 0 :(得分:1)
您可以生成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这样每次转动曲柄时大部分或全部数字都会改变。
这是一个自包含程序,但您可能希望将当前值放入数据库中,甚至可能将此代码放在存储过程后面,在这种情况下,您可能希望让它返回一些不同的sentinel值以指示序列已经用尽了。我已经制作了生成下一个代码synchronized
的方法;你需要以这种或那种方式确保没有两个呼叫者收到相同的代码。
注意:这意味着这可能会成为您应用的瓶颈。
我还会在它周围放一个包装,并检查一下咒骂的停止列表。您不希望向客户提供包含四个字母或其他令人反感的单词的代码。如果代码包含一个坏字作为子字符串,则只需将其丢弃并生成下一个代码。