生成随机数批量插入数据库方案

发布时间 2023-05-06 08:13:52作者: Jimmyhus

Java生成随机数值4位插入数据库,如何保证插入数据库不重复的方案

我们可以通过以下两种方式来保证Java生成的随机数不会重复插入到数据库中:

1. 使用Java代码检测数据库中是否已存在该数值

在生成随机数之前,我们可以查询数据库,确保生成的随机数不存在于数据库中。如果已经存在,则重新生成一个随机数。

import java.sql.*;
import java.util.Random;

public class RandomNumberGenerator {
    public static void main(String[] args) {
        int randomNumber = generateRandomNumber();
        insertRandomNumber(randomNumber);
    }

    private static int generateRandomNumber() {
        int number;
        boolean isDuplicate;
        do {
            number = new Random().nextInt(9000) + 1000; // 生成4位随机数
            isDuplicate = checkIfNumberExistsInDatabase(number);
        } while (isDuplicate);
        return number;
    }

    private static boolean checkIfNumberExistsInDatabase(int number) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "";
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            String sql = "SELECT COUNT(*) FROM numbers WHERE value=?";
            PreparedStatement stmt = conn.prepareStatement(sql);
            stmt.setInt(1, number);
            ResultSet rs = stmt.executeQuery();
            rs.next();
            return rs.getInt(1) > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }

    private static void insertRandomNumber(int number) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "";
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            String sql = "INSERT INTO numbers (id, value) VALUES (NULL, ?)";
            PreparedStatement stmt = conn.prepareStatement(sql);
            stmt.setInt(1, number);
            stmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

该程序中,generateRandomNumber()函数会生成一个随机数,并检查该数是否已经存在于数据库中;checkIfNumberExistsInDatabase()函数会查询数据库,判断给定的数是否已经存在于数据库中;insertRandomNumber()函数会将一个数字插入到数据库中。在主程序中,我们可以通过调用generateRandomNumber()函数和insertRandomNumber()函数来实现将一个唯一的4位随机数插入到数据库中。

2. 使用数据库约束

在设计数据表时,可以为需要保证唯一性的字段添加唯一性约束(unique constraint),这样在插入数据时,数据库会检查要插入的值是否已经存在。如果已经存在,则会抛出异常或返回错误信息。

import java.sql.*;
import java.util.Random;

public class RandomNumberGenerator {
    public static void main(String[] args) {
        int randomNumber = new Random().nextInt(9000) + 1000; // 生成4位随机数
        insertRandomNumber(randomNumber);
    }

    private static void insertRandomNumber(int number) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "";
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            String sql = "INSERT INTO numbers (id, value) VALUES (NULL, ?)";
            PreparedStatement stmt = conn.prepareStatement(sql);
            stmt.setInt(1, number);
            stmt.executeUpdate();
        } catch (SQLIntegrityConstraintViolationException e) {
            // 主键或唯一性约束冲突,处理异常
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

该程序中,在插入数据时如果违反了唯一性约束,则会抛出SQLIntegrityConstraintViolationException异常。我们可以在catch块中进行异常处理。

3.使用 Set 集合去重

可以使用 Java 中的 Set 集合来存储生成的随机数,这样就可以自动去重,然后再将 Set 集合中的数据批量插入到数据库中。这种方案适用于数据量较小的情况,对于大量数据插入的情况效率可能较低。
示例代码:

// 假设数据表名为 numbers,id 字段为自增长主键,value 字段为要插入的随机数
String sql = "INSERT INTO numbers (value) VALUES (?)";
PreparedStatement stmt = conn.prepareStatement(sql);
Set<String> set = new HashSet<>();
while (set.size() < 10000) {
    String number = generateRandomNumber();
    set.add(number);
}
for (String number : set) {
    stmt.setString(1, number);
    stmt.addBatch();
}
stmt.executeBatch();

在以上示例代码中,我们可以将 generateRandomNumber() 函数替换成前面给出的生成 4 位数字+字母随机数的函数即可。

java生成一个4位数字+字母的随机数作为起始值,并递增1万条批量插入数据库,高效插入数据库并且数据不重复:

对于 Java 生成一个 4 位数字+字母的随机数作为起始值,并递增 1 万条批量插入数据库的情况下,我们可以使用以下方案:

  • 首先生成一个 4 位数字+字母的随机数,然后将其作为起始值,递增生成 1 万个不重复的数值。

  • 将这 1 万个数值拆分成若干个批次,每批次插入到数据库中。可以使用 JDBC 的 Batch 处理来实现批量插入。

  • 在生成随机数时,可以考虑使用 SecureRandom 类来生成随机数,以确保随机性和安全性,同时也能够生成符合要求的随机字符串。

以下是生成一个 4 位数字+字母的随机数作为起始值,并递增 1 万条批量插入数据库,高效插入数据库并且数据不重复的 Java 代码:

import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;

public class RandomNumberGenerator {
    public static void main(String[] args) {
        String startNumber = generateRandomNumber();
        Set<String> numbers = generateNumbers(startNumber, 10000);
        insertNumbers(numbers);
    }
	
	//在生成随机数时,可以考虑使用 UUID(通用唯一标识符)算法来生成随机字符串,确保数据的唯一性
	 /**private static String generateRandomNumber() {
        return UUID.randomUUID().toString().substring(0, 4); // 生成4位随机数
    }*/

    private static String generateRandomNumber() {
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(4);
        for (int i = 0; i < 4; i++) {
            int num = random.nextInt(36);
            if (num < 10) {
                sb.append(num); // 数字
            } else {
                char c = (char) ('a' + (num - 10)); // 字母
                sb.append(c);
            }
        }
        return sb.toString();
    }

    private static Set<String> generateNumbers(String startNumber, int count) {
        Set<String> numbers = new HashSet<>();
        for (int i = 0; i < count; i++) {
            numbers.add(startNumber);
            startNumber = getNextNumber(startNumber);
        }
        return numbers;
    }

    private static String getNextNumber(String number) {
        StringBuilder sb = new StringBuilder(number);
        for (int i = 3; i >= 0; i--) {
            char c = sb.charAt(i);
            if (c == '9') {
                sb.setCharAt(i, 'a');
                break;
            } else if (c == 'z') {
                sb.setCharAt(i, '0');
            } else if (c == 'Z') {
                sb.setCharAt(i, 'a');
                break;
            } else if (c < '9' || (c > 'a' && c < 'z') || (c > 'A' && c < 'Z')) {
                sb.setCharAt(i, ++c);
                break;
            }
        }
        return sb.toString();
    }

    private static void insertNumbers(Set<String> numbers) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "";
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            conn.setAutoCommit(false); // 手动提交事务
            String sql = "INSERT INTO numbers (id, value) VALUES (NULL, ?)";
            PreparedStatement stmt = conn.prepareStatement(sql);
            for (String number : numbers) {
                stmt.setString(1, number);
                stmt.addBatch(); // 添加到批处理
            }
            stmt.executeBatch(); // 执行批处理
            conn.commit(); // 提交事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

该程序中,generateRandomNumber()函数会生成一个 4 位数字+字母的随机数作为起始值;getNextNumber()函数会根据起始值递增生成指定数量的不重复数值;insertNumbers()函数会将生成的数值批量插入数据库中。在插入数据时,我们使用 JDBC 的 Batch 处理来实现批量插入,可以显著提高插入数据的效率。同时,我们将事务设为手动提交,并在所有数据插入完成后再进行一次提交,这样可以确保数据的一致性和完整性,并且减少了数据库的负担。

需要注意的是,在生成随机数时,通过使用 SecureRandom 类来生成随机数,以确保随机性和安全性,同时也能够生成符合要求的随机字符串。
在使用 HashSet 去重时,如果需要确保数据的绝对唯一性和高并发情况下的正确性,可以考虑使用分布式锁机制来避免重复插入。

getNextNumber() 函数是一个用于递增生成 4 位数字+字母随机数的工具函数。下面是该函数的逻辑解释:

  • 将传入的字符串 number 转换为 StringBuilder 对象。

  • 遍历字符串中每个字符,从右往左依次进行如下处理:

    • 如果当前字符是数字 '9',则将其转换为小写字母 'a'。

    • 如果当前字符是小写字母 'z',则将其转换为数字 '0'。

    • 如果当前字符是大写字母 'Z',则将其转换为小写字母 'a'。

    • 如果当前字符是数字、小写字母或大写字母,且不在以上三个特殊情况中,则将其递增为下一个字符,并结束循环。

  • 返回递增后的字符串。

该函数的实现逻辑比较繁琐,但基本思路是根据 ASCII 码表中字符的顺序进行递增操作。首先将字符串转换为 StringBuilder 对象,然后从右往左依次遍历每个字符。如果当前字符是数字 '9',则需要将其转换为小写字母 'a',因为在 ASCII 码表中,字符 'a' 的编码比字符 '9' 大。如果当前字符是小写字母 'z',则需要将其转换为数字 '0',以保证递增后仍然是 4 位随机数。如果当前字符是大写字母 'Z',则需要将其转换为小写字母 'a',这是因为在 ASCII 码表中,字符 'a' 的编码比字符 'Z' 大。最后,如果当前字符不是以上三种特殊情况之一,则可以直接将其递增为下一个字符,并结束循环。

这样,通过反复调用 getNextNumber() 函数,就可以逐个生成 1 万个不重复的 4 位数字+字母随机数。

使用分布式锁机制来避免重复插入

在使用 HashSet 去重时,如果需要确保数据的绝对唯一性和高并发情况下的正确性,可以考虑使用分布式锁机制来避免重复插入。在 Java 中,常用的分布式锁实现方案有 Redisson、Zookeeper 等。

以下是一个使用 Redisson 实现分布式锁的示例代码:

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class RandomNumberGenerator {
    private static final String REDIS_LOCK_PREFIX = "random-number-generator-lock:";

    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);

        String startNumber = generateRandomNumber();
        Set<String> numbers = generateNumbers(startNumber, 10000);
        insertNumbers(numbers, redisson);
    }

    private static String generateRandomNumber() {
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(4);
        for (int i = 0; i < 4; i++) {
            int num = random.nextInt(36);
            if (num < 10) {
                sb.append(num); // 数字
            } else {
                char c = (char) ('a' + (num - 10)); // 字母
                sb.append(c);
            }
        }
        return sb.toString();
    }

    private static Set<String> generateNumbers(String startNumber, int count) {
        Set<String> numbers = new HashSet<>();
        for (int i = 0; i < count; i++) {
            numbers.add(startNumber);
            startNumber = getNextNumber(startNumber);
        }
        return numbers;
    }

    private static String getNextNumber(String number) {
        StringBuilder sb = new StringBuilder(number);
        for (int i = 3; i >= 0; i--) {
            char c = sb.charAt(i);
            if (c == '9') {
                sb.setCharAt(i, 'a');
                break;
            } else if (c == 'z') {
                sb.setCharAt(i, '0');
            } else if (c == 'Z') {
                sb.setCharAt(i, 'a');
                break;
            } else if (c < '9' || (c > 'a' && c < 'z') || (c > 'A' && c < 'Z')) {
                sb.setCharAt(i, ++c);
                break;
            }
        }
        return sb.toString();
    }

    private static void insertNumbers(Set<String> numbers, RedissonClient redisson) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "";
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            conn.setAutoCommit(false); // 手动提交事务
            String sql = "INSERT INTO numbers (id, value) VALUES (NULL, ?)";
            PreparedStatement stmt = conn.prepareStatement(sql);

            RLock lock = redisson.getLock(REDIS_LOCK_PREFIX + "insert-numbers");
            if (lock.tryLock(5L, TimeUnit.SECONDS)) { // 尝试获取锁,等待 5 秒
                try {
                    for (String number : numbers) {
                        stmt.setString(1, number);
                        stmt.executeUpdate();
                    }
                    conn.commit(); // 提交事务
                } catch (SQLException e) {
                    conn.rollback(); // 回滚事务
                    e.printStackTrace();
                } finally {
                    lock.unlock(); // 释放锁
                }
            } else {
                System.out.println("Failed to acquire lock.");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

在 insertNumbers() 方法中,我们使用 Redisson 的 RLock 接口来创建一个分布式锁对象,并尝试获取锁。如果在 5 秒内成功获取到锁,则执行插入数据的操作,并在所有数据插入完成后再进行一次提交。如果无法获取锁,则输出提示信息。

通过使用分布式锁机制,我们可以确保多个实例同时运行时不会重复

将这 1 万个数值拆分成若干个批次,每批次插入到数据库中

将这 1 万个数值拆分成若干个批次,每批次插入到数据库中可以有效地减少单次批量插入的数据量,并有助于提高插入速度和降低系统负载。以下是一个示例代码:

import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;

public class RandomNumberGenerator {
    private static final int BATCH_SIZE = 1000; // 每批次插入数据的数量

    public static void main(String[] args) {
        String startNumber = generateRandomNumber();
        Set<String> numbers = generateNumbers(startNumber, 10000);
        insertNumbers(numbers);
    }

    private static String generateRandomNumber() {
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(4);
        for (int i = 0; i < 4; i++) {
            int num = random.nextInt(36);
            if (num < 10) {
                sb.append(num); // 数字
            } else {
                char c = (char) ('a' + (num - 10)); // 字母
                sb.append(c);
            }
        }
        return sb.toString();
    }

    private static Set<String> generateNumbers(String startNumber, int count) {
        Set<String> numbers = new HashSet<>();
        for (int i = 0; i < count; i++) {
            numbers.add(startNumber);
            startNumber = getNextNumber(startNumber);
        }
        return numbers;
    }

    private static String getNextNumber(String number) {
        StringBuilder sb = new StringBuilder(number);
        for (int i = 3; i >= 0; i--) {
            char c = sb.charAt(i);
            if (c == '9') {
                sb.setCharAt(i, 'a');
                break;
            } else if (c == 'z') {
                sb.setCharAt(i, '0');
            } else if (c == 'Z') {
                sb.setCharAt(i, 'a');
                break;
            } else if (c < '9' || (c > 'a' && c < 'z') || (c > 'A' && c < 'Z')) {
                sb.setCharAt(i, ++c);
                break;
            }
        }
        return sb.toString();
    }

    private static void insertNumbers(Set<String> numbers) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "";
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            conn.setAutoCommit(false); // 手动提交事务
            String sql = "INSERT INTO numbers (id, value) VALUES (NULL, ?)";
            PreparedStatement stmt = conn.prepareStatement(sql);

            int count = 0;
            for (String number : numbers) {
                stmt.setString(1, number);
                stmt.addBatch(); // 添加到批处理
                if (++count % BATCH_SIZE == 0) { // 每达到 BATCH_SIZE 条记录就执行一次批处理
                    stmt.executeBatch(); // 执行批处理
                    stmt.clearBatch(); // 清空批处理
                }
            }
            if (count % BATCH_SIZE != 0) { // 如果还有剩余的数据,则再执行一次批处理
                stmt.executeBatch(); // 执行批处理
                stmt.clearBatch(); // 清空批处理
            }
            conn.commit(); // 提交事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

在 insertNumbers() 方法中,我们首先将 Connection 对象的自动提交事务设置为 false,然后按照 BATCH_SIZE 的数量拆分数据,并使用 addBatch() 函数将每个批次的数据添加到批处理中。当累计达到 BATCH_SIZE 条记录时,我们就会执行一次批处理,并清空批处理缓存。最后,在所有数据插入完成后再进行一次提交。

需要注意的是,在拆分数据时,如果还有剩余的不足 BATCH_SIZE 条记录的数据,则需要再执行一次批处理,以确保所有数据都能被插入数据库中。

将这 1千条数据拆分成若干个批次,每批次插入到数据库中

将这 1 千条数据拆分成若干个批次,每批次插入到数据库中可以有效地减少单次批量插入的数据量,并有助于提高插入速度和降低系统负载。以下是一个示例代码:

import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;

public class RandomNumberGenerator {
    private static final int BATCH_SIZE = 100; // 每批次插入数据的数量

    public static void main(String[] args) {
        String startNumber = generateRandomNumber();
        Set<String> numbers = generateNumbers(startNumber, 1000);
        insertNumbers(numbers);
    }

    private static String generateRandomNumber() {
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(4);
        for (int i = 0; i < 4; i++) {
            int num = random.nextInt(36);
            if (num < 10) {
                sb.append(num); // 数字
            } else {
                char c = (char) ('a' + (num - 10)); // 字母
                sb.append(c);
            }
        }
        return sb.toString();
    }

    private static Set<String> generateNumbers(String startNumber, int count) {
        Set<String> numbers = new HashSet<>();
        for (int i = 0; i < count; i++) {
            numbers.add(startNumber);
            startNumber = getNextNumber(startNumber);
        }
        return numbers;
    }

    private static String getNextNumber(String number) {
        StringBuilder sb = new StringBuilder(number);
        for (int i = 3; i >= 0; i--) {
            char c = sb.charAt(i);
            if (c == '9') {
                sb.setCharAt(i, 'a');
                break;
            } else if (c == 'z') {
                sb.setCharAt(i, '0');
            } else if (c == 'Z') {
                sb.setCharAt(i, 'a');
                break;
            } else if (c < '9' || (c > 'a' && c < 'z') || (c > 'A' && c < 'Z')) {
                sb.setCharAt(i, ++c);
                break;
            }
        }
        return sb.toString();
    }

    private static void insertNumbers(Set<String> numbers) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "";
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            conn.setAutoCommit(false); // 手动提交事务
            String sql = "INSERT INTO numbers (id, value) VALUES (NULL, ?)";
            PreparedStatement stmt = conn.prepareStatement(sql);

            int count = 0;
            for (String number : numbers) {
                stmt.setString(1, number);
                stmt.addBatch(); // 添加到批处理
                if (++count % BATCH_SIZE == 0) { // 每达到 BATCH_SIZE 条记录就执行一次批处理
                    stmt.executeBatch(); // 执行批处理
                    stmt.clearBatch(); // 清空批处理
                }
            }
            if (count % BATCH_SIZE != 0) { // 如果还有剩余的数据,则再执行一次批处理
                stmt.executeBatch(); // 执行批处理
                stmt.clearBatch(); // 清空批处理
            }
            conn.commit(); // 提交事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

和之前的示例代码基本相同,只是将 BATCH_SIZE 的数量调整为 100,并将生成数据的数量修改为 1 千。在 insertNumbers() 方法中,我们按照 BATCH_SIZE 的数量拆分数据,并使用 addBatch() 函数将每个批次的数据添加到批处理中。当累计达到 BATCH_SIZE 条记录时,我们就会执行一次批处理,并清空批处理缓存。最后,在所有数据插入完成后再进行一次提交。