现在的应用系统越来越多的都是业务系统与数据库系统是分离的,这里就会涉及到数据库备份的问题。如果业务系统与数据库是在同一服务器,可以很简单的用mysql自带的命令:

mysqldump --databases dbName > dbNameBak.sql

现在是数据库服务器与应用系统不在同一台服务器,我们可以通过JDBC方式进行备份数据库操作,

代码如下:
备份数据库主要方法:backDB

/**
 * 备份数据
 */
public void backupDB() {
    Connection conn = null;
    String dbName = null;
    try {
        conn = jdbcDao.getDataSource().getConnection();
        dbName = conn.getCatalog();
    } catch (SQLException e) {
        throw new RuntimeException("无法获取数据库连接!");
    }    
    String tableName = null,
            procName = null;
    BufferedWriter writer = null;
    Statement stmtInfo = null, stmtData = null;
    ResultSet rsInfo = null, rsData = null;
    try {
        //存放文件目录
        String folderBackup = CommonUtils.absoluteClassPathUsrPath(Constants.FOLDER_BACKUP);
        //文件名
        String sqlFilename = String.format("%s-%s.sql", dbName, Dates.dateToString(new Date(), Constants.DATETIME_FORMAT_FILENAME));
        writer = new BufferedWriter(new FileWriter(folderBackup + Constants.PATH_SEPARATOR + sqlFilename));
        
        /*
         * 头内容
         */
        writer.write("/*!40101 SET NAMES utf8 */;");
        writer.newLine();
        writer.write("/*!40101 SET SQL_MODE=''*/;");
        writer.newLine();
        writer.write("/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;");
        writer.newLine();
        writer.write("/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;");
        writer.newLine();
        writer.write("/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;");
        writer.newLine();
        writer.write("/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;");
        writer.flush();
        
        /*
         * 导出表数据
         */
        stmtInfo = conn.createStatement();
        rsInfo = stmtInfo.executeQuery(String.format("SHOW FULL TABLES FROM `%s` WHERE TABLE_TYPE = 'BASE TABLE'", dbName));
        // 遍历所有表
        while(rsInfo.next()) {
            tableName = rsInfo.getString(1);
            this.dbBackExportTable(conn, tableName, writer, true);
        }    //end for tables
        if(null != rsInfo) {
            rsInfo.close();
        }
        if(null != stmtInfo) {
            stmtInfo.close();
        }

        /*
         * 导出存储过程信息
         */
        stmtInfo = conn.createStatement();
        rsInfo = stmtInfo.executeQuery(String.format("SELECT `SPECIFIC_NAME` from `INFORMATION_SCHEMA`.`ROUTINES` WHERE `ROUTINE_SCHEMA` = '%s' AND ROUTINE_TYPE = 'PROCEDURE'; ", dbName));
        while (rsInfo.next()) {
            procName = rsInfo.getString(1);
            
            /* 存储过程结构 */
            stmtData = conn.createStatement();
            rsData = stmtData.executeQuery(String.format("SHOW CREATE PROCEDURE `%s`", procName));
            if(!rsData.next()) {
                continue ;
            }
            writer.newLine();
            writer.newLine();
            writer.write(String.format("/* Procedure structure for procedure `%s` */", procName));
            writer.newLine();
            writer.write(String.format("/*!50003 DROP PROCEDURE IF EXISTS  `%s` */;", procName));
            writer.newLine();
            writer.write("DELIMITER $$");
            writer.newLine();
            writer.append("/*!50003 ").append(rsData.getString(3)).append(" */$$");
            writer.newLine();
            writer.write("DELIMITER ;");
            if(null != rsData) {
                rsData.close();
            }
            if(null != stmtData) {
                stmtData.close();
            }
        }
        
        /*
         * 尾内容
         */
        writer.newLine();
        writer.newLine();
        writer.write("/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;");
        writer.newLine();
        writer.write("/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;");
        writer.newLine();
        writer.write("/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;");
        writer.newLine();
        writer.write("/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;");
        writer.newLine();
        
        writer.close();
        if(null != stmtData) {
            stmtData.close();
        }
        if(null != rsData) {
            rsData.close();
        }
        if(null != stmtInfo) {
            stmtInfo.close();
        }
        if(null != rsInfo) {
            rsInfo.close();
        }
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
    }
}

备份具体表数据

/**
 * 导出表数据
 * @param conn
 * @param tableName
 * @param writer
 * @param bulkFlag   是否将数据放在一起
 * @throws SQLException 
 * @throws IOException 
 */
private void dbBackExportTable(Connection conn, String tableName, BufferedWriter writer, boolean bulkFlag) throws SQLException, IOException {
    Statement stmt = null;
    ResultSet rs = null;
    /* 表结构 */
    stmt = conn.createStatement();
    rs = stmt.executeQuery(String.format("SHOW CREATE TABLE `%s`", tableName));
    if(!rs.next()) {
        return ;
    }
    writer.newLine();
    writer.newLine();
    writer.write(String.format("/*Table structure for table `%s` */", tableName));
    writer.newLine();
    writer.write(String.format("DROP TABLE IF EXISTS `%s`;", tableName));
    writer.newLine();                
    writer.write(rs.getString(2) + ";");
    writer.newLine();
    if(null != rs) {
        rs.close();
    }
    if(null != stmt) {
        stmt.close();
    }
    
    /* 导出表数据 */
    // 先获取记录数
    stmt = conn.createStatement();
    rs = stmt.executeQuery(String.format("SELECT COUNT(1) FROM `%s`", tableName));
    int rowCount = rs.next() ? rs.getInt(1) : 0;
    if(0 >= rowCount) {
        writer.flush();
        return ;
    }
    writer.write(String.format("/*Data for the table `%s` */", tableName));
    writer.newLine();
    
    stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
    stmt.setFetchSize(Integer.MIN_VALUE);
    stmt.setFetchDirection(ResultSet.FETCH_REVERSE);
    rs = stmt.executeQuery(String.format("SELECT * FROM `%s`", tableName));
    int colCount = 0;
    Object colValue = null;
    // 所有数据用","连接
    if(!bulkFlag) {
        while(rs.next()) {
            colCount = rs.getMetaData().getColumnCount();
            
            writer.write(String.format("INSERT INTO `%s` VALUES (", tableName));
            // 获取表每一列数据
            for(int j = 0; j < colCount; j ++) {
                if(j > 0) {
                    writer.write(',');    
                }
                colValue = rs.getObject(j + 1);
                if(null != colValue) {
                    writer.write(String.format("'%s'", CommonUtils.escapeString(colValue.toString())));
                } else {
                    writer.write("NULL");
                }
            }    //end for one record columns
            writer.write(");");
            writer.newLine();
            writer.flush();
        }    //end for table records
    }
    // 每行数据独立分开
    else {
        ResultSetMetaData rsMetaData = null;
        int counter = 0;
        while(rs.next()) {
            ++ counter;
            rsMetaData = rs.getMetaData();
            colCount = rsMetaData.getColumnCount();

            // 第一条记录,则列出列名
            if(1 == counter) {
                writer.write(String.format("INSERT INTO `%s` (", tableName));
                for(int i = 0; i < colCount; i ++) {
                    if(i > 0) {
                        writer.write(",");
                    }
                    writer.append('`').append(rsMetaData.getColumnName(i + 1)).append('`');
                }
                writer.append(") VALUES ");
            }
            // 获取表每一列数据
            for(int j = 0; j < colCount; j ++) {
                writer.write((0 >= j) ? '(' : ',');                        
                colValue = rs.getObject(j + 1);
                if(null != colValue) {
                    writer.write(String.format("'%s'", CommonUtils.escapeString(colValue.toString())));
                } else {
                    writer.write("NULL");
                }
            }    //end for one record columns
            // 是否是最后记录
            if(rowCount > counter) {
                writer.write("),");
            } else {
                writer.write(");");
            }
            writer.flush();
        }    //end for table records
    }
    
    if(null != rs) {
        rs.close();
    }
    if(null != stmt) {
        stmt.close();
    }
}

说明:上面用到的处理数据方法CommonUtils.escapeString(String x))),请参看文章:MySQL字符转义涉及的问题及解决
需要注意的是,从数据库获取的原数据如果涉及单、双引号,虽然不会影响导出的sql内容,但是导出的内容就可能无法正常再导入到原数据库,原因就是数据内容包含有单双引号(PS:此处排查浪费了近2天时间)

待完善内容:

  • 上面所有的导出sql相关内容都放在一个StringBuilder对象中, 对于数据量不大并且内存足够的情况下没有问题,但是如果数据库数据比较大或者分配给java内存有限,可能导致内存不够而报错java.lang.OutOfMemoryError: Java heap space

参考文章:

标签: mysql, java

已有 2 条评论

  1. jdbc 写的不错哦

  2. yangshare yangshare

    非常不错,非常感谢分享

添加新评论