Java中自动生成保存在数据库的编号

本文介绍了一个自动生成编号的逻辑实现,通过查询数据库中已有的最大编号,并在此基础上递增生成新的唯一编号。如果数据库中尚无记录,则使用预设的初始编号。

自动生成编号

if(entry.getKey().equalsIgnoreCase("BH")){
    String sql="select max(BH) from "+defineTable.getTableName();
    String max= oaPersonDefineTableDao.getMax(sql);
    if(StringUtils.isNotEmpty(max)){
        String sub=  max.substring(1,max.length());
        int orderValue=Integer.parseInt(sub);
        orderValue++;
        int n = orderValue;
       NumberFormat formatter = NumberFormat.getNumberInstance();
       formatter.setMinimumIntegerDigits(6);   //设置编号的位数,例如 99999
       formatter.setGroupingUsed(false);   // 设置不使用分组,例如 9,999
       String s= formatter.format(n);
        String now="A"+s;
        updateValue.append(now);
    }else{
        String  ret="A000001";
        updateValue.append(ret);
    }
}else{
    updateValue.append(entry.getValue());
}
<think>我们之前讨论过生成文件唯一标识的几种方法,现在用户明确要求将标识保存数据库。结合引用[1]中提到的分布式ID生成方案(号段模式),我们可以设计一个完整的方案。 方案选择:考虑到用户可能需要分布式环境下的ID生成,并且需要存储到数据库,这里采用两种方式结合: 1. 使用数据库号段模式生成唯一ID(用于数据库主键) 2. 同时存储文件内容哈希(用于文件内容去重) 步骤: 1. 生成唯一ID(作为数据库主键,使用号段模式) 2. 计算文件内容哈希(例如SHA-256) 3. 将文件信息(包括唯一ID、文件哈希、文件名、大小等)存储到数据库 4. 保存文件到存储系统(本地或云存储),文件名可以使用唯一ID或哈希值 引用[1]中提供了号段模式的实现思路,我们可以在此基础上构建。 具体实现: 一、创建数据库表(用于ID生成服务) 参考引用[1],创建表`id_generator`: ```sql CREATE TABLE id_generator ( id int NOT NULL AUTO_INCREMENT COMMENT '主键', biz_tag varchar(63) NOT NULL COMMENT '业务标识', max_id bigint NOT NULL COMMENT '当前最大ID', step int NOT NULL COMMENT '号段长度', version int NOT NULL COMMENT '版本号(乐观锁)', PRIMARY KEY (id), UNIQUE KEY uniq_biz_tag (biz_tag) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` 二、实现号段模式ID生成器 1. 定义实体类 ```java @Data @TableName("id_generator") public class IdGeneratorEntity { private Long id; private String bizTag; private Long maxId; private Integer step; private Integer version; } ``` 2. 定义DAO(MyBatis) ```java @Mapper public interface IdGeneratorMapper { @Select("SELECT * FROM id_generator WHERE biz_tag = #{bizTag}") IdGeneratorEntity selectByBizTag(String bizTag); @Update("UPDATE id_generator SET max_id = max_id + step, version = version + 1 WHERE biz_tag = #{bizTag} AND version = #{version}") int updateMaxId(IdGeneratorEntity entity); } ``` 3. 实现ID生成服务 ```java @Service public class SegmentIdService { @Autowired private IdGeneratorMapper idGeneratorMapper; // 当前号段 [currentId, maxId] private AtomicLong currentId = new AtomicLong(0); private volatile long maxId = 0; private String bizTag = "file_id"; // 业务标识 @PostConstruct public void init() { getNextSegment(); } private void getNextSegment() { IdGeneratorEntity entity = idGeneratorMapper.selectByBizTag(bizTag); if (entity == null) { throw new RuntimeException("未初始化业务标识: " + bizTag); } long newCurrent = entity.getMaxId(); long newMaxId = newCurrent + entity.getStep(); // 更新数据库 int count = idGeneratorMapper.updateMaxId(entity); if (count > 0) { currentId.set(newCurrent); maxId = newMaxId; } else { // 更新失败,重试 getNextSegment(); } } public synchronized long nextId() { long id = currentId.incrementAndGet(); if (id > maxId) { getNextSegment(); id = currentId.incrementAndGet(); } return id; } } ``` 三、文件上传与存储 1. 创建文件信息表 ```sql CREATE TABLE file_info ( id BIGINT PRIMARY KEY COMMENT '文件唯一ID(号段模式生成)', original_name VARCHAR(255) COMMENT '原始文件名', file_hash CHAR(64) COMMENT '文件SHA-256哈希值', file_size BIGINT COMMENT '文件大小(字节)', storage_path VARCHAR(500) COMMENT '存储路径', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); ``` 2. 文件信息实体类 ```java @Data @TableName("file_info") public class FileInfo { private Long id; // 使用号段模式生成的ID private String originalName; private String fileHash; private Long fileSize; private String storagePath; private Date createTime; } ``` 3. 文件上传服务 ```java @Service public class FileStorageService { @Autowired private SegmentIdService segmentIdService; @Autowired private FileInfoMapper fileInfoMapper; @Value("${file.upload-dir}") private String uploadDir; public FileInfo saveFile(MultipartFile file) throws IOException { // 生成文件ID(主键) long fileId = segmentIdService.nextId(); // 计算文件哈希 String fileHash; try (InputStream is = file.getInputStream()) { fileHash = DigestUtils.sha256DigestAsHex(is); } // 存储文件:使用ID作为文件名(避免哈希冲突时覆盖) String fileExtension = FilenameUtils.getExtension(file.getOriginalFilename()); String storageFileName = fileId + (StringUtils.hasText(fileExtension) ? "." + fileExtension : ""); Path storagePath = Paths.get(uploadDir, storageFileName); Files.copy(file.getInputStream(), storagePath, StandardCopyOption.REPLACE_EXISTING); // 保存文件信息到数据库 FileInfo fileInfo = new FileInfo(); fileInfo.setId(fileId); fileInfo.setOriginalName(file.getOriginalFilename()); fileInfo.setFileHash(fileHash); fileInfo.setFileSize(file.getSize()); fileInfo.setStoragePath(storagePath.toString()); fileInfoMapper.insert(fileInfo); return fileInfo; } } ``` 四、文件上传控制器 ```java @RestController @RequestMapping("/file") public class FileUploadController { @Autowired private FileStorageService fileStorageService; @PostMapping("/upload") public ResponseEntity<Map<String, Object>> uploadFile(@RequestParam("file") MultipartFile file) { try { FileInfo fileInfo = fileStorageService.saveFile(file); Map<String, Object> result = new HashMap<>(); result.put("fileId", fileInfo.getId()); result.put("fileHash", fileInfo.getFileHash()); return ResponseEntity.ok(result); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Collections.singletonMap("error", "文件上传失败")); } } } ``` 五、配置上传目录(application.properties) ```properties file.upload-dir=/data/files ``` 六、初始化号段表 在`id_generator`表中插入初始记录: ```sql INSERT INTO id_generator(biz_tag, max_id, step, version) VALUES('file_id', 100000, 1000, 0); ``` 这样,每次获取ID时,服务会从数据库申请1000个ID(步长),在内存中分配,用完后再次申请。 优势: 1. 数据库主键使用号段模式生成,性能高(减少数据库交互) 2. 文件哈希可用于去重,相同文件只存储一份(可根据哈希查询是否已存在) 3. 存储文件名使用ID,避免文件名冲突 优化点: 1. 文件去重:在保存文件前,先根据哈希查询数据库,如果存在则直接返回已有文件信息,避免重复存储。 2. 大文件处理:对于大文件,可以使用分块计算哈希,避免内存溢出。 文件去重改进: ```java public FileInfo saveFile(MultipartFile file) throws IOException { // 先计算哈希 String fileHash; try (InputStream is = file.getInputStream()) { fileHash = DigestUtils.sha256DigestAsHex(is); } // 检查是否已存在相同文件 FileInfo existFile = fileInfoMapper.selectByHash(fileHash); if (existFile != null) { return existFile; // 直接返回已有文件 } // 不存在则存储 long fileId = segmentIdService.nextId(); // ...(后续存储操作) } ``` 注意:如果文件内容相同但文件名不同,这里会视为相同文件(因为哈希相同)。如果业务需要保留不同文件名,可以在文件信息表中增加记录(同一哈希对应多个文件名),但存储文件只存一份。 总结:该方案结合了分布式ID生成和文件内容哈希,既保证了ID的高效生成,又实现了文件去重。 §§相关问题§§ 1. 号段模式ID生成器在集群环境下如何保证ID不重复? 2. 如何优化大文件的哈希计算速度? 3. 文件存储时如何避免目录下文件过多导致的性能问题? 4. 如何实现文件秒传(基于哈希检查)? 5. 号段模式与Snowflake算法在文件ID生成中的优劣对比?</think>在 Spring Boot 应用中生成文件唯一标识并存储到数据库,我推荐结合**数据库号段模式**(引用[1])和**文件内容哈希**的方案,实现高效可靠的分布式文件标识管理。以下是完整实现步骤: --- ### 一、数据库表设计(核心表) #### 1. ID 生成表(号段模式) ```sql CREATE TABLE id_generator ( id INT AUTO_INCREMENT PRIMARY KEY, biz_tag VARCHAR(63) NOT NULL COMMENT '业务标识(如file_id)', max_id BIGINT NOT NULL COMMENT '当前最大ID', step INT NOT NULL COMMENT '号段长度', version INT NOT NULL COMMENT '乐观锁版本', UNIQUE KEY uniq_biz_tag (biz_tag) ); ``` #### 2. 文件信息表 ```sql CREATE TABLE file_info ( id BIGINT PRIMARY KEY COMMENT '唯一标识(号段生成)', file_name VARCHAR(255) NOT NULL, file_hash CHAR(64) NOT NULL COMMENT 'SHA-256哈希值', file_size BIGINT NOT NULL, storage_path VARCHAR(500) NOT NULL COMMENT '实际存储路径', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); ``` --- ### 二、实现文件唯一标识服务 #### 1. 号段模式ID生成器(引用[1]) ```java @Service public class SegmentIdGenerator { @Autowired private IdGeneratorMapper idGeneratorMapper; // MyBatis Mapper // 获取下一个ID段 public synchronized long nextId(String bizTag) { IdGeneratorEntity entity = idGeneratorMapper.selectByBizTag(bizTag); long newMaxId = entity.getMaxId() + entity.getStep(); // 乐观锁更新 int count = idGeneratorMapper.updateMaxId( entity.getId(), newMaxId, entity.getVersion() ); if (count == 0) throw new RuntimeException("并发更新冲突"); return newMaxId - entity.getStep() + 1; // 返回段内起始ID } } ``` #### 2. 文件服务(生成标识+存储) ```java @Service public class FileService { @Autowired private SegmentIdGenerator idGenerator; @Value("${file.storage.path}") private String storagePath; public FileInfo saveFile(MultipartFile file) throws IOException { // 1. 生成文件哈希(引用[3]流式处理) String fileHash; try (InputStream is = file.getInputStream()) { fileHash = DigestUtils.sha256DigestAsHex(is); } // 2. 生成唯一ID(号段模式) long fileId = idGenerator.nextId("file_id"); // 3. 存储文件(使用ID作为文件名) String ext = FilenameUtils.getExtension(file.getOriginalFilename()); String saveName = fileId + (StringUtils.hasText(ext) ? "." + ext : ""); Path savePath = Paths.get(storagePath, saveName); Files.copy(file.getInputStream(), savePath, StandardCopyOption.REPLACE_EXISTING); // 4. 构建文件信息对象 FileInfo fileInfo = new FileInfo(); fileInfo.setId(fileId); fileInfo.setFileName(file.getOriginalFilename()); fileInfo.setFileHash(fileHash); fileInfo.setFileSize(file.getSize()); fileInfo.setStoragePath(savePath.toString()); return fileInfoMapper.insert(fileInfo); // 存入数据库 } } ``` --- ### 三、文件上传控制器 ```java @RestController @RequestMapping("/files") public class FileController { @PostMapping("/upload") public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) { try { FileInfo savedFile = fileService.saveFile(file); return ResponseEntity.ok(Map.of( "fileId", savedFile.getId(), "fileHash", savedFile.getFileHash() )); } catch (IOException e) { return ResponseEntity.status(500).body("文件处理失败"); } } } ``` --- ### 四、关键配置 #### 1. application.properties ```properties # 文件存储路径 file.storage.path=/data/uploads # MyBatis配置 mybatis.mapper-locations=classpath:mapper/*.xml ``` #### 2. 初始化号段数据 ```sql INSERT INTO id_generator(biz_tag, max_id, step, version) VALUES ('file_id', 100000, 1000, 0); ``` --- ### 方案优势 1. **分布式友好**:号段模式减少数据库压力,每次获取一批ID[^1] 2. **去重能力**:通过文件哈希实现内容级去重(相同文件生成相同哈希) 3. **高效存储**:使用数字ID作为文件名(比UUID更短) 4. **数据一致性**:文件信息与存储路径统一管理在数据库 --- ### 优化建议 1. **大文件处理**:添加分块哈希计算避免内存溢出 ```java public String calcLargeFileHash(Path path) throws IOException { DigestInputStream dis = new DigestInputStream( Files.newInputStream(path), MessageDigest.getInstance("SHA-256") ); byte[] buffer = new byte[8192]; while (dis.read(buffer) != -1) { /* 流式读取 */ } return Hex.encodeHexString(dis.getMessageDigest().digest()); } ``` 2. **去重逻辑**:插入前先查询 `file_hash` 避免重复存储 ```java FileInfo existFile = fileInfoMapper.selectByHash(fileHash); if (existFile != null) return existFile; // 返回已有文件 ``` 3. **号段监控**:添加低水位预警(如剩余ID<10%时自动扩容) --- ### 存储效果示例 | id | file_name | file_hash | file_size | storage_path | |----------|-------------|------------------------------------|-----------|----------------------| | 100001 | report.pdf | 9f86d081...ad015a3bf4f1b2b0b822cd15 | 2.5 MB | /data/uploads/100001.pdf | | 100002 | image.png | 5e884898...154e0305332fa6a8c8cfd6f5 | 800 KB | /data/uploads/100002.png | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值