本文目录导读:

我来为您提供几个Java实现数据归档的实用案例。
基础文件归档案例(使用ZIP压缩)
import java.io.*;
import java.nio.file.*;
import java.util.zip.*;
public class FileArchiveDemo {
/**
* 将文件夹压缩成ZIP归档文件
*/
public static void archiveToZip(String sourceDirPath, String zipFilePath)
throws IOException {
Path sourceDir = Paths.get(sourceDirPath);
Path zipFile = Paths.get(zipFilePath);
try (ZipOutputStream zos = new ZipOutputStream(
new FileOutputStream(zipFile.toFile()))) {
Files.walk(sourceDir)
.filter(path -> !Files.isDirectory(path))
.forEach(path -> {
ZipEntry zipEntry = new ZipEntry(
sourceDir.relativize(path).toString());
try {
zos.putNextEntry(zipEntry);
Files.copy(path, zos);
zos.closeEntry();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
}
/**
* 解压ZIP归档文件
*/
public static void extractFromZip(String zipFilePath, String destDirPath)
throws IOException {
Path destDir = Paths.get(destDirPath);
Files.createDirectories(destDir);
try (ZipInputStream zis = new ZipInputStream(
new FileInputStream(zipFilePath))) {
ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null) {
Path newFilePath = destDir.resolve(zipEntry.getName());
if (zipEntry.isDirectory()) {
Files.createDirectories(newFilePath);
} else {
Files.createDirectories(newFilePath.getParent());
Files.copy(zis, newFilePath,
StandardCopyOption.REPLACE_EXISTING);
}
zis.closeEntry();
}
}
}
public static void main(String[] args) {
try {
// 归档示例
archiveToZip("/data/source", "/backup/data_2024_01_01.zip");
System.out.println("归档成功");
// 解压示例
extractFromZip("/backup/data_2024_01_01.zip", "/data/restore");
System.out.println("解压成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
数据库数据归档案例
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class DatabaseArchiveDemo {
private Connection sourceConn;
private Connection archiveConn;
public DatabaseArchiveDemo(Connection source, Connection archive) {
this.sourceConn = source;
this.archiveConn = archive;
}
/**
* 归档旧数据
*/
public int archiveOldData(String tableName, int daysOld) throws SQLException {
String selectSQL = String.format(
"SELECT * FROM %s WHERE create_date < ?", tableName);
String insertSQL = String.format(
"INSERT INTO %s_archive SELECT * FROM %s WHERE create_date < ?",
tableName, tableName);
String deleteSQL = String.format(
"DELETE FROM %s WHERE create_date < ?", tableName);
LocalDateTime cutoffDate = LocalDateTime.now().minusDays(daysOld);
try {
sourceConn.setAutoCommit(false);
// 复制到归档表
try (PreparedStatement insertStmt =
archiveConn.prepareStatement(insertSQL)) {
insertStmt.setObject(1, cutoffDate);
int archivedCount = insertStmt.executeUpdate();
// 从源表删除
try (PreparedStatement deleteStmt =
sourceConn.prepareStatement(deleteSQL)) {
deleteStmt.setObject(1, cutoffDate);
deleteStmt.executeUpdate();
}
sourceConn.commit();
return archivedCount;
}
} catch (SQLException e) {
sourceConn.rollback();
throw e;
} finally {
sourceConn.setAutoCommit(true);
}
}
/**
* 按批次归档大数据量
*/
public int batchArchive(String tableName, int daysOld, int batchSize)
throws SQLException {
int totalArchived = 0;
boolean hasMore = true;
while (hasMore) {
// 查询需要归档的数据ID
String selectIdsSQL = String.format(
"SELECT id FROM %s WHERE create_date < ? LIMIT ?", tableName);
List<Long> idsToArchive = new ArrayList<>();
try (PreparedStatement selectStmt =
sourceConn.prepareStatement(selectIdsSQL)) {
selectStmt.setObject(1,
LocalDateTime.now().minusDays(daysOld));
selectStmt.setInt(2, batchSize);
ResultSet rs = selectStmt.executeQuery();
while (rs.next()) {
idsToArchive.add(rs.getLong("id"));
}
}
if (idsToArchive.isEmpty()) {
hasMore = false;
break;
}
// 批量归档
archiveBatch(tableName, idsToArchive);
totalArchived += idsToArchive.size();
System.out.printf("已归档 %d 条记录,总计 %d 条%n",
idsToArchive.size(), totalArchived);
}
return totalArchived;
}
private void archiveBatch(String tableName, List<Long> ids)
throws SQLException {
String placeholders = String.join(",",
ids.stream().map(id -> "?").toArray(String[]::new));
String copySQL = String.format(
"INSERT INTO %s_archive SELECT * FROM %s WHERE id IN (%s)",
tableName, tableName, placeholders);
String deleteSQL = String.format(
"DELETE FROM %s WHERE id IN (%s)", tableName, placeholders);
try {
sourceConn.setAutoCommit(false);
// 复制到归档表
try (PreparedStatement copyStmt =
archiveConn.prepareStatement(copySQL)) {
for (int i = 0; i < ids.size(); i++) {
copyStmt.setLong(i + 1, ids.get(i));
}
copyStmt.executeUpdate();
}
// 删除源数据
try (PreparedStatement deleteStmt =
sourceConn.prepareStatement(deleteSQL)) {
for (int i = 0; i < ids.size(); i++) {
deleteStmt.setLong(i + 1, ids.get(i));
}
deleteStmt.executeUpdate();
}
sourceConn.commit();
} catch (SQLException e) {
sourceConn.rollback();
throw e;
} finally {
sourceConn.setAutoCommit(true);
}
}
}
日志文件轮转归档案例
import java.io.*;
import java.nio.file.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.stream.Stream;
public class LogRotateArchiveDemo {
private final Path logDir;
private final DateTimeFormatter dateFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd");
public LogRotateArchiveDemo(String logDirPath) {
this.logDir = Paths.get(logDirPath);
}
/**
* 轮转当前日志文件
*/
public void rotateLogFile(String logFileName, long maxSize)
throws IOException {
Path logFile = logDir.resolve(logFileName);
if (!Files.exists(logFile)) {
return;
}
long fileSize = Files.size(logFile);
if (fileSize < maxSize) {
return;
}
// 生成带日期的归档文件名
String dateStr = LocalDate.now().format(dateFormatter);
String archivedName = logFileName + "." + dateStr;
Path archivedFile = logDir.resolve(archivedName);
// 重命名当前日志文件
Files.move(logFile, archivedFile,
StandardCopyOption.REPLACE_EXISTING);
// 创建新的空日志文件
Files.createFile(logFile);
// 对归档文件进行压缩
compressOldLog(archivedFile);
System.out.printf("日志轮转完成: %s -> %s.gz%n",
logFileName, archivedName);
}
/**
* 压缩旧的日志文件
*/
private void compressOldLog(Path logFile) throws IOException {
Path gzFile = Paths.get(logFile.toString() + ".gz");
try (GZIPOutputStream gzos = new GZIPOutputStream(
new FileOutputStream(gzFile.toFile()));
FileInputStream fis = new FileInputStream(logFile.toFile())) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
gzos.write(buffer, 0, bytesRead);
}
}
// 压缩后删除原文件
Files.delete(logFile);
}
/**
* 清理过期归档文件
*/
public void cleanOldArchives(int maxKeepDays) throws IOException {
LocalDate cutoffDate = LocalDate.now().minusDays(maxKeepDays);
try (DirectoryStream<Path> stream =
Files.newDirectoryStream(logDir, "*.gz")) {
for (Path file : stream) {
String fileName = file.getFileName().toString();
// 从文件名提取日期
String dateStr = extractDateFromFileName(fileName);
if (dateStr != null) {
LocalDate fileDate = LocalDate.parse(dateStr,
dateFormatter);
if (fileDate.isBefore(cutoffDate)) {
Files.delete(file);
System.out.printf("删除过期归档: %s%n", fileName);
}
}
}
}
}
private String extractDateFromFileName(String fileName) {
// 从文件名中提取日期部分
String[] parts = fileName.split("\\.");
if (parts.length >= 3) {
return parts[1]; // 格式: logname.2024-01-01.gz
}
return null;
}
}
综合归档管理类
import java.io.*;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ArchiveManager {
private final FileArchiveDemo fileArchiver = new FileArchiveDemo();
private final DatabaseArchiveDemo dbArchiver;
private final LogRotateArchiveDemo logRotater;
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(2);
public ArchiveManager(Connection source, Connection archive,
String logDirPath) {
this.dbArchiver = new DatabaseArchiveDemo(source, archive);
this.logRotater = new LogRotateArchiveDemo(logDirPath);
}
/**
* 启动定时归档任务
*/
public void startScheduledArchiving() {
// 每天凌晨2点执行数据库归档
scheduler.scheduleAtFixedRate(() -> {
try {
System.out.println("开始数据库归档...");
int count = dbArchiver.archiveOldData("orders", 90);
System.out.printf("数据库归档完成,归档 %d 条记录%n", count);
} catch (Exception e) {
System.err.println("数据库归档失败: " + e.getMessage());
}
}, 0, 24, TimeUnit.HOURS);
// 每6小时检查日志文件大小
scheduler.scheduleAtFixedRate(() -> {
try {
logRotater.rotateLogFile("application.log",
100 * 1024 * 1024); // 100MB
// 每月清理一次过期归档
if (LocalDateTime.now().getDayOfMonth() == 1) {
logRotater.cleanOldArchives(90); // 保留90天
}
} catch (Exception e) {
System.err.println("日志轮转失败: " + e.getMessage());
}
}, 0, 6, TimeUnit.HOURS);
}
/**
* 手动执行完整归档
*/
public void performFullArchive() throws Exception {
System.out.println("开始完整归档流程...");
// 1. 文件归档
fileArchiver.archiveToZip(
"/data/old_files",
"/archive/data_" + LocalDateTime.now().toString() + ".zip"
);
// 2. 数据库归档
dbArchiver.batchArchive("transactions", 365, 1000);
// 3. 日志归档
logRotater.rotateLogFile("application.log", 50 * 1024 * 1024);
System.out.println("完整归档完成");
}
public void shutdown() {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
}
}
}
使用建议
-
性能优化:
- 使用批量处理而不是逐条处理
- 归档大文件时使用缓冲流
- 考虑使用多线程并行归档
-
错误处理:
- 实现重试机制
- 记录详细的归档日志
- 实现事务回滚
-
监控与告警:
- 记录归档进度和状态
- 归档失败时发送告警
- 监控归档磁盘空间
-
安全性:
- 归档文件加密
- 权限控制
- 数据完整性验证
这些案例覆盖了文件、数据库和日志三种常见的数据归档场景,您可以根据实际需求进行调整和扩展。