如何用Java案例实现数据归档?

wen java案例 2

本文目录导读:

如何用Java案例实现数据归档?

  1. 基础文件归档案例(使用ZIP压缩)
  2. 数据库数据归档案例
  3. 日志文件轮转归档案例
  4. 综合归档管理类
  5. 使用建议

我来为您提供几个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();
        }
    }
}

使用建议

  1. 性能优化

    • 使用批量处理而不是逐条处理
    • 归档大文件时使用缓冲流
    • 考虑使用多线程并行归档
  2. 错误处理

    • 实现重试机制
    • 记录详细的归档日志
    • 实现事务回滚
  3. 监控与告警

    • 记录归档进度和状态
    • 归档失败时发送告警
    • 监控归档磁盘空间
  4. 安全性

    • 归档文件加密
    • 权限控制
    • 数据完整性验证

这些案例覆盖了文件、数据库和日志三种常见的数据归档场景,您可以根据实际需求进行调整和扩展。

抱歉,评论功能暂时关闭!