本文记录一个实例:
1、用户邀请码表,里面存邀请码和关联的用户编号,每次有用户注册,就领取(关联)一个邀请码。邀请码都是系统提前生成空挡在表里待分配的(不要问为什么,目前业务就是这样设计的)。
2、系统初始化的时候,或者空闲的邀请码快不够用的时候,会需要追加生成一批邀请码入库。一般系统上线的时候直接入库几十万或者几百万妥妥了。
代码实现步骤:
1、在 Controller 请求,调用Service。
2、Service 中,使用Java程序生成不重复的值,然后按1000一批的方式,分割Controller提交的总数,多次执行。
3、MyBatis XML文件中,3个方法(将每一批邀请码存入创建的临时表》将临时表数据排除掉已经存在的数据转移到正式邀请码表中》显示删除临时表)
主要代码如下:
/**
* 生成一批邀请码(说预期是因为程序生成后,可能数据库已经存在一部分,这里不做严格数量处理,实际入库多少就是多少)
*
* @return
*/
@PostMapping("/generate")
public ResultVO batchGenerate() {
if (request.getRequestURL().toString().contains("localhost")
|| request.getRequestURL().toString().contains("127.0.0.1")) {
long _start = System.currentTimeMillis();
int n = userInviteCodeService.batchGenerateInviteCode(20000);
String msg = "成功增加" + n + "个邀请码。耗时:" + (System.currentTimeMillis() - _start) + "ms";
logger.info(msg);
return new ResultVO(msg);
} else {
logger.warn("请在本机使用localhost或127.0.0.1方式请求接口");
return ResultVO.FORBIDDEN;
}
}
/**
* 生成一批推荐码
*
* @param count 预生成数量,这里不做严格处理,假设预生成1万个,因为数据库已经存在100个,那么实际生成则是9900个,没有关系。
*/
public int batchGenerateInviteCode(int count) {
// 批量生成验证码
Map<String, String> inviteCodeMap = new HashMap<>();
List<UserInviteCode> codeList = new ArrayList<>();
int i = 0, j = 0;
UserInviteCode record;
int successCount = 0;
while (i < count) {
String inviteCode = RandomStringUtils.randomAlphanumeric(6).toUpperCase();
if (inviteCodeMap.put(inviteCode, "1") == null) {
record = new UserInviteCode();
record.setInviteCode(inviteCode);
record.setCreateTime(new Date());
record.setDelFlag(1);
record.setVersion(1);
codeList.add(record);
i++;
}
if (i % 1000 == 0) {
successCount += saveGenerateInviteCode(codeList);
codeList = new ArrayList<>();
}
j++;
}
// 1000倍数后的余数
successCount += saveGenerateInviteCode(codeList);
logger.info("预生成{}个推荐码,程序产生{}个验证码,程序碰撞重复{}个,{}个邀请码实际入库{}个,与库中重复碰撞{}个", count, j, j - i, count, successCount,
count - successCount);
return successCount;
}
/**
* 持久化code到数据库,list集合大小限制不超过1000
*
* @param codeList
* @return
*/
private int saveGenerateInviteCode(List<UserInviteCode> codeList) {
int n = 0;
// 限制单次操作数据的数量为1000,这是经过实测出来的结果请勿随意改动此值!!!
if (codeList.size() > 0 && codeList.size() <= 1000) {
try {
userInviteCodeMapper.saveNewCodeToTempTable(codeList);
n = userInviteCodeMapper.insertByTempTable();
} finally {
// 显式调用,实际上数据库会自动回收
userInviteCodeMapper.dropTempTable();
}
}
return n;
}
/**
* 用户邀请码
*
* @author 单红宇
* @date 2019年6月28日
*
*/
public interface UserInviteCodeMapper extends Mapper<UserInviteCode> {
int saveNewCodeToTempTable(List<UserInviteCode> inviteCodeList);
int insertByTempTable();
void dropTempTable();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shanhy.example.module.user.mapper.UserInviteCodeMapper">
<!-- 1.创建临时表并插入数据(注意:该方法其实就拼接一个很长的SQL,因为数据库对SQL长度有限制,所以调用该方法传入的集合要注意大小限制)为了节约长度,减少换行和变量名的长度等等 -->
<!-- 对于本例SQL,传入的是6个字符长度的随机值,实测List大小在1300左右可以正常入库,1500就超长了。所以本例实际业务中,限制传入的List大小为1000 -->
<update id="saveNewCodeToTempTable">
CREATE TEMPORARY TABLE IF NOT EXISTS user_invite_code_temp1
<foreach collection="list" item="item" separator="UNION">SELECT #{item.inviteCode} code</foreach>
</update>
<!-- 2.将临时表数据插入到标准表 -->
<update id="insertByTempTable">
insert into user_invite_code(invite_code, create_time, version, del_flag)
select code,now(),1,1 from user_invite_code_temp1 t1
where not exists(select * from user_invite_code t2 where t2.invite_code = t1.code)
</update>
<!-- 3.删除临时表 -->
<update id="dropTempTable">
DROP TEMPORARY TABLE IF EXISTS user_invite_code_temp1
</update>
</mapper>
(结束)