spring 用Java代码还是用数据库函数插入随机帐号?

uajslkp6  于 2023-09-29  发布在  Spring
关注(0)|答案(3)|浏览(140)

我已经开始使用Java、Sping Boot 应用程序和PostgreSQL数据库构建一个银行项目。我想在每次创建新帐户时生成一个随机的8位或10位数的帐号。实现这一目标的最佳方法是什么?
选项1:我可以在创建新帐户时在Java服务层中处理此问题(但如何确保它是唯一的?).
选项2:我可以这样设计数据库:

CREATE TABLE IF NOT EXISTS account (
id SERIAL PRIMARY KEY,
account_number VARCHAR(8) UNIQUE, -- Unique 8-character account number
-- Other columns for account details
);

然后创建一个SQL函数来生成一个随机数,如:

CREATE OR REPLACE FUNCTION generate_random_account_number()
RETURNS VARCHAR(8) AS $$
BEGIN
   RETURN lpad(floor(random() * 100000000)::int::text, 8, '0');
END;
$$ LANGUAGE plpgsql;

然后像这样插入:

INSERT INTO account (account_number, other_columns)
VALUES (generate_random_account_number(), 'other_data');

我不确定哪种方法更好地实现这一点,以及如何确保每次创建帐户时帐户号码都是唯一的。
PS:我不希望遇到独特性的问题,因为这是一个业余爱好项目,我不会创建超过100个帐户。

wlzqhblo

wlzqhblo1#

我会这样做:

选项1:我可以在创建新帐户时在Java服务层中处理此问题

如何确保它是独一无二的?

创建随机帐号,创建后,执行数据库查询,返回带有该帐号的所有数据集。如果返回的结果数量大于0,则创建一个新的随机帐号并重新检查。循环执行此操作,直到数据库查询不返回任何数据集。
这个答案的原因是:

  • 在我看来,让数据库成为一个数据库,让后端代码处理业务逻辑是一种很好的风格。如果需要,可以在数据库表中将字段创建为唯一字段。这将再次检查您的帐号
  • 这段代码很容易写,也很容易读。IMO这是最重要的
  • 不会有性能问题。是的,有可能你的循环会运行不止一次,但你永远不会注意到这一点。9,999,999,999种可能性存在;即使你有一百万个帐户,这个循环也几乎不会运行两次以上。
    旧熨斗的尖头
  • 代码可读性第一。你读代码的次数比写代码的次数多100倍。
  • 最后一次表演。性能优化的代码经常使用Map或它的硬冗长等。编写简单直观的代码,如果遇到性能问题,可以考虑优化。
  • 不要编写“超级花哨的三重超通用代码”,以保存3行。这是不酷了,下次你需要改变的东西。
  • 保持简单。IDE是编写代码的地方。如果你想锻炼,那你就得去俱乐部。
  • 如果你不再理解你自己的代码,你就做错了(在大多数情况下)。
  • 最好的单元测试是你不需要编写的测试。
  • 对其他程序员要友善,不要演变成一个自私的“我是最好的,其他人都是愚蠢的”程序员。即使代码看起来很糟糕,或者你认为它一定是愚蠢的-三思而后行。有人有自己的想法!

现在,成长,成为最好的自己!

s1ag04yj

s1ag04yj2#

你需要一个从连续输入中可靠地生成10位数的函数,而且这个函数看起来是随机的,也就是说,不知道你的函数的人不会知道前一个数字后面是哪些数字。实现它的一种方法是定义一个模式。假设我们有一个大数的10位数倍数,比如9754031400
这是

12345 × 98765 × 8

所以我们可以

1 +(98765 * 1),1 +(98765 * 2),...,1 +(98765 * 98759)

作为我们的第一组数字(98765 + 1的倍数低于9754031400)。当我们在这个区间内用完了数字时,我们可以继续这个系列,将2加到倍数上:

2 +(98765 * 1),2 +(98765 * 2),...,2 +(98765 * 98759)

无可否认,这还不够令人困惑。所以我们可以把数字打乱。如果原始号码的数字最初是
d1 d2 d3 d4 d5 d6 d7 d8 d9 d10**
则可以更改它们的顺序,以使结果变得模糊
d10 d7 d5 d6 d3 d4 d8 d1 d9 d2**
这将是非常困难的逆向工程。但如果这还不够,您还可以添加更多级别的模糊处理。由于这是一个算法示例,而JavaScript是我们可以添加为片段的唯一语言,因此我将通过Javascript来说明这一点,但您可以在Java或SQL中实现此算法,没有任何问题

const magicNumber = 98765;
const magicLimit = 9754031400;
const period = parseInt(magicLimit / magicNumber) - 1;
const digitNumber = 10;
function obfuscate(input) {
    let offset = 1 + parseInt(input / period);
    let mod = input % period;
    let firstLevel = offset + mod * magicNumber;
    let str = firstLevel + "";
    while (str.length < digitNumber) str = "0" + str;
    let rawArray = str.split("");
    let newDigits = [9, 6, 4, 5, 2, 3, 7, 0, 8, 1];
    let newArray = [];
    while (newArray.length < rawArray.length) newArray.push(rawArray[newDigits[newArray.length]]);
    return newArray.join("");
}

function compute(input) {
    let output = obfuscate(parseInt(input));
    document.getElementById("result").innerText = output;
}

compute(1);
<input type="number" id="thevalue" value="1" onchange="compute(this.value)">
<div id="result"></div>
sc4hvdpw

sc4hvdpw3#

我想每次生成一个随机的8位或10位数的帐号。
第一个问题是你为什么要这样做。如果这个数字必须满足特定的标准,请列出它们 * 精确 *。这个数字必须是真正的“随机”还是“任意”?为什么是数字,为什么是8或10?前导零可以吗?很重要?等
为什么额外的替代PK idaccount_number不是自然键吗?为什么varchar(8)只是数字?
想想由不同客户同时创建的多个帐户。数据库可以可靠地避免冲突,而客户端则相反。
除非有特定的条件,否则使用bigintIDENTITY列。一个 * 任意的 *,但不是 * 随机的 * 数字。前导零是无关紧要的噪声:

CREATE TEMP TABLE account (
  account_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, name text NOT NULL   
  -- other columns for account details
, CONSTRAINT id_max_10_digits CHECK (account_id BETWEEN 1 AND 9999999999)
);

请参阅:

  • 自动递增表列

10位数对于普通的integer来说太多了。所以bigint
我加入了一个CHECK约束来强制你的数字范围。
要在一个步骤中插入并取回生成的account_id,请执行以下操作:

INSERT INTO account (name)
VALUES ('Foo')
RETURNING account_id;

如果您需要10位数的字符串格式,则始终可以设置数字的格式以便于显示。像to_char(account_id, '0000000000')lpad(account_id::text, 10, '0')-哪个更好,因为 * 不可变 *。如果需要,您甚至可以将生成的列添加到表中:

CREATE TABLE account (
  account_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, account_nr text GENERATED ALWAYS AS (lpad(account_id::text, 10, '0')) STORED
, name text NOT NULL   
  -- other columns for account details
, CONSTRAINT id_max_10_digits CHECK (account_id BETWEEN 1 AND 9999999999)
);

请参阅:

  • PostgreSQL中的计算/计算/虚拟/派生列

真随机

如果你需要真正的随机数,其固有的缺点是原则上可能会发生冲突。

CREATE OR REPLACE FUNCTION public.generate_random_account_number()
  RETURNS bigint
  LANGUAGE SQL VOLATILE PARALLEL SAFE AS
$func$
SELECT trunc(random() * 10000000000)::bigint;
$func$;

然后又道:

CREATE TABLE account (
  account_id bigint PRIMARY KEY DEFAULT public.generate_random_account_number()
-- , ... rest like above

UUID

要使冲突几乎不可能发生,请使用uuid值而不是10位数。这些可以在客户端或in the database中生成。它们只是有点笨拙,占用16个字节。关于冲突的可能性,请参考章节 “哈希冲突的概率?“ 此处:

相关问题