生成随机数批量插入数据库方案
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 条记录时,我们就会执行一次批处理,并清空批处理缓存。最后,在所有数据插入完成后再进行一次提交。