因为工作需要,先是需要将一个小型的MySQL数据库中的数据拷贝到Oracle中;近期又有需要将一个中型的Oracle数据库拷贝到MySQL中。曾经找过网上各种转换工具,大多收费的,自己写个吧,还一切可控。
转换的前提是两种数据库中已经存在相同的数据结构,可以自己利用SQL语句在目标数据库生成数据结构;或者是使用工具仅生成数据结构(如:DBMover,它是收费的,但可以免费转换数据结构,好像不包括外键,网址:http://dbmover.com/cn/)。
第一个程序,从MySQL拷贝到Oracle
很久以前写的,将一个小型的MySQL数据库中的表拷贝到Oracle数据库中(十万数据量级别,没有优化,大数据量可能很慢):
package com.clzhang.sample.jdbc; import java.sql.*; /** * 从MySQL数据库拷贝表数据到Oracle数据库中的程序。 * 前提:两个数据库中都具有相关的表,且表结构相同,目标数据库中还不能存在冲突的数据。 * @author acer * */ public class CopyMySQL2Oracle { // 源数据库,目标数据库的连接配置 String mysql_jdbc_url = "jdbc:mysql://localhost/xuejia?user=root&password=password1&useUnicode=true&characterEncoding=gb2312"; String jdbc_url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl"; String jdbc_user = "mytest"; String jdbc_password = "test001"; public void startProcess(String tableName) throws Exception { // 创建到两个数据库的连接 Class.forName("org.gjt.mm.mysql.Driver"); Connection connSource = DriverManager.getConnection(mysql_jdbc_url); Class.forName("oracle.jdbc.driver.OracleDriver"); Connection connDest = DriverManager.getConnection(jdbc_url, jdbc_user, jdbc_password); // 打开源数据库中相关表 StringBuilder sb = new StringBuilder(); sb.append("insert into " + tableName + "("); Statement stmt= connSource.createStatement(); ResultSet rs = stmt.executeQuery("select * from " + tableName); // 显示共计有多少条记录 rs.last(); System.out.println(tableName + "表共计有:"+ rs.getRow() + "条记录,正在处理......"); rs.beforeFirst(); // 先计算目标数据库的PreparedStatement的SQL语句 ResultSetMetaData rsmd = rs.getMetaData(); int numberOfColumns = rsmd.getColumnCount(); for(int i=1; i<=numberOfColumns;i++) { sb.append(rsmd.getColumnName(i) + ","); } sb.deleteCharAt(sb.length()-1); sb.append(")values("); for(int i=1; i<=numberOfColumns;i++) { sb.append("?,"); } sb.deleteCharAt(sb.length()-1); sb.append(")"); System.out.println(sb.toString()); // 给PreparedStatement赋值,然后更新;如果是大数量的情况,可以考虑Batch处理。因为这里的数据量小,直接单条更新了。 PreparedStatement pstmt = connDest.prepareStatement(sb.toString()); while(rs.next()) { for(int i=1; i<=numberOfColumns;i++) { pstmt.setObject(i, rs.getObject(i)); } System.out.print("-"); pstmt.executeUpdate(); } // 关闭各资源 rs.close(); stmt.close(); pstmt.close(); connSource.close(); connDest.close(); } public static void main(String[] args) throws Exception { CopyMySQL2Oracle ins = new CopyMySQL2Oracle(); ins.startProcess("PRODUCT"); // ...需要拷贝的表名在下面加入即可,注意顺序,比如有外键的表应该排列在主表之后。 } }
第二个程序,从Oracle拷贝到MySQL
最近写的,一个中小型数据库(百万级别,做了适当优化,仍旧挺慢的)的拷贝程序,供参考吧。
建议在拷贝之前,禁用、或者是删除目标数据库中的相关索引以提高速度;拷贝完成之后,重新建立索引。
package com.clzhang.sample.jdbc; import java.sql.*; /** * 这是一个将Oracle数据库中的数据拷贝到MySQL数据库中的简单程序。 * 仅考虑NUMBER/CHAR/VARCHAR/CLOB/DATE/TIMESTAMP等字段类型。 * BLOB没有考虑(因为我的数据库中没有BLOB字段,无法测试)。 * * 前提:两个数据库中都具有相关的表,且表结构相同,目标数据库中还不能存在冲突的数据。 * @author acer * */ public class CopyOracle2MySQL { // 源数据库,目标数据库的连接配置 private final String DEST_MYSQL_JDBC_URL = "jdbc:mysql://localhost/mybbs?user=root&password=password1&useUnicode=true&characterEncoding=utf-8"; private final String SOURCE_JDBC_URL = "jdbc:oracle:thin:@127.0.0.1:1521:orcl"; private final String SOURCE_JDBC_USER = "mybbs"; private final String SOURCE_JDBC_PASSWORD = "bbs001"; public void startImport() throws Exception { // 创建到两个数据库的连接 Class.forName("org.gjt.mm.mysql.Driver"); Class.forName("oracle.jdbc.driver.OracleDriver"); Connection connDest = DriverManager.getConnection(DEST_MYSQL_JDBC_URL); Connection connSource = DriverManager.getConnection(SOURCE_JDBC_URL, SOURCE_JDBC_USER, SOURCE_JDBC_PASSWORD); // 查询出当前用户下面的所有表,依次处理(如果有外键,建议人工输入各表,以保证顺序;如果没有外键,直接运行下面程序即可。) try { /** // 方式一:自动运行 Statement stmt = connSource.createStatement(); ResultSet rs = stmt.executeQuery("select TABLE_NAME from USER_TABLES"); while(rs.next()) { try { importTable(connSource, connDest, rs.getString("TABLE_NAME")); } catch (Exception e) { e.printStackTrace(); } } rs.close(); stmt.close(); */ // 方式二:人工输入各表名(需要保证顺序,以确保有外键的表在主表之后插入数据) importTable(connSource, connDest, "BBSDETAIL"); importTable(connSource, connDest, "BBSCOMMENT"); } finally { // 自动关闭数据库资源? connDest.close(); connSource.close(); } } private void importTable(Connection connSource, Connection connDest, String tablename) throws Exception { Statement stmt = null; PreparedStatement pstmt = null; try { // 给PreparedStatement赋值,然后更新;如果是大数量的情况,可以考虑Batch处理。因为这里的数据量小,直接单条更新了。 // 打开源数据库中相关表 StringBuilder insertSQL = new StringBuilder(); insertSQL.append("insert into " + tablename + "("); stmt= connSource.createStatement(); ResultSet rs = stmt.executeQuery("select * from " + tablename); // 先计算目标数据库的PreparedStatement的SQL语句 ResultSetMetaData rsmd = rs.getMetaData(); int numberOfColumns = rsmd.getColumnCount(); for(int i=1; i<=numberOfColumns;i++) { insertSQL.append(rsmd.getColumnName(i) + ","); } insertSQL.deleteCharAt(insertSQL.length()-1); insertSQL.append(")values("); for(int i=1; i<=numberOfColumns;i++) { insertSQL.append("?,"); } insertSQL.deleteCharAt(insertSQL.length()-1); insertSQL.append(")"); System.out.println(insertSQL.toString()); // 计数 int count = 0; // 每多少条记录提交一次,以提高效率 int batchCount = 1000; pstmt = connDest.prepareStatement(insertSQL.toString()); while(rs.next()) { pstmt.clearParameters(); for(int i=1; i<=numberOfColumns;i++) { if(rsmd.getColumnType(i) == java.sql.Types.NUMERIC) { // 2 pstmt.setInt(i, rs.getInt(i)); }else if(rsmd.getColumnType(i) == java.sql.Types.DOUBLE) { // 8 pstmt.setDouble(i, rs.getDouble(i)); }else if(rsmd.getColumnType(i) == java.sql.Types.CHAR || rsmd.getColumnType(i) == java.sql.Types.VARCHAR || rsmd.getColumnType(i) == java.sql.Types.CLOB) { // 1 // 12 // 2005 pstmt.setString(i, rs.getString(i)); }else if(rsmd.getColumnType(i) == java.sql.Types.DATE) { // 91 pstmt.setDate(i, rs.getDate(i)); }else if(rsmd.getColumnType(i) == java.sql.Types.TIMESTAMP) { // 93 pstmt.setTimestamp(i, rs.getTimestamp(i)); }else { pstmt.setObject(i, rs.getObject(i)); } } pstmt.addBatch(); // 输出统计信息 count++; if(count % batchCount == 0) { // 若干条提交一次 System.out.println(); System.out.print("正在更新数据库..."); pstmt.executeBatch(); System.out.println(count); } } if(count % batchCount != 0) { // 最后提交一次 System.out.println(); System.out.print("正在更新数据库..."); pstmt.executeBatch(); System.out.println(count); } rs.close(); } finally { if(stmt != null) stmt.close(); if(pstmt != null) pstmt.close(); } } public static void main(String[] args) throws Exception { CopyOracle2MySQL ins = new CopyOracle2MySQL(); ins.startImport(); } }