《数据库系统》课程之实验七 通过ODBC/JDBC转移异构数据库中数据
1 实验目的
学会配置ODBC/JDBC数据源,熟悉使用ODBC/JDBC来进行数据库应用程序的设计,熟悉通过ODBC/JDBC接口访问异构数据库并对异构数据库进行操作。
2 实验平台和实验工具
实验平台:2个异构数据库(如MySQL数据库、SQL Sever数据库等ODBC/JDBC常支持的数据库)。
通过JAVA语言等编写访问数据库的应用程序。编程工具自选。
3 实验内容和要求
配置两个不同的数据源,使用ODBC/JDBC编写程序连接两个不同关系数据库管理系统的数据源,对异构数据库中的数据进行互相转移。如,将MySQL数据库的某个表中的数据转移到SQL Server数据库的表,将SQL Server中的数据转移至MySQL数据库。
认真填写实验报告,并且提交源程序,保证可正确编译和运行。
3.1 知识预备
提前自行了解ODBC/JDBC的概念和使用流程。
3.2 实验要求
给出配置两个不同的数据源的过程。提交应用程序源代码,并标识必要的注释,尽可能清楚明白地说明程序的功能,实现的方法,关键数据结构、变量、函数的定义。
4 实验过程
配置数据源
MySQL
在实验一中已经对MySQL进行了配置
SQL Server

- 后改用SQL Server身份认证,使用登录名和密码连接。

关键代码及注释
展示编写的类
连接MySQL数据库
基于jdbc驱动进行连接
类:MysqlConnection
static初始化
时从properties文件中获取属性,包括驱动名
、URL
、用户名
、密码
,然后通过Class.forName
加载驱动。
- public void
connect()
调用DriverManager.getConnection
连接数据库。
- public Connection
getConnection()
返回当前连接。
- public void
close()
关闭数据库连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| public class MysqlConnection { private static String driverName; private static String url; private static String username; private static String password; private static Properties p = new Properties(); public Connection conn;
static { try { InputStream inputStream = new FileInputStream("src/main/resources/config.properties"); p.load(inputStream); driverName = p.getProperty("Mysql_driverName"); url = p.getProperty("Mysql_URL"); username = p.getProperty("Mysql_username"); password = p.getProperty("Mysql_password"); Class.forName(driverName); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
public void connect() { try { System.out.println("正在连接MySQL数据库"); this.conn = DriverManager.getConnection(url, username, password); System.out.println("连接MySQL数据库成功"); } catch (Exception e) { System.out.println("连接MySQL数据库失败"); e.printStackTrace(); } }
public Connection getConnection() { return this.conn; }
public void close() { try { if (this.conn != null) this.conn.close(); } catch (Exception e) { e.printStackTrace(); } } }
|
连接SQL Server数据库
类:SqlServerConnection
与连接MySQL基本一致,基于jdbc驱动使用用户名和密码进行连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| public class SqlServerConnection { private static Properties p = new Properties(); private Connection conn = null; private static String driverName; private static String url; private static String username; private static String password;
static { try { InputStream inputStream = new FileInputStream("src/main/resources/config.properties"); p.load(inputStream); driverName = p.getProperty("SqlServer_driverName"); url = p.getProperty("SqlServer_URL"); username=p.getProperty("SqlServer_username"); password=p.getProperty(("SqlServer_password")); Class.forName(driverName); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
public void connect() { System.out.println("正在连接SQLServer数据库"); try { this.conn = DriverManager.getConnection(url,username,password); System.out.println("连接SQLServer数据库成功"); } catch (Exception e) { System.out.println("连接SQLServer数据库失败"); e.printStackTrace(); } }
public Connection getConnection() { return this.conn; }
public void close() { try { if (this.conn != null) this.conn.close(); } catch (Exception e) { e.printStackTrace(); } } }
|
由 SQL Server 转移数据至 MySQL
类:SqlServerToMysql
- public static void
setConnSQLserver(Connection connSQLserver)
:传参分别设置SQL Server的连接。
- public static void
setConnMysql(Connection connMysql)
:传参分别设置MySQL的连接。
public static List< String > getTable()
:对连接调用getMetaData()
获取数据库的元数据md
,对于md
调用getTables(null, "dbo", null, null)
获得数据库中的表信息
,若该表在使用者指定要转移的表中,则将表的信息存入list返回。
public static boolean insertTable(String table,int size)
:使用Preparedstatement
定义预处理对象,执行sql语句,完成向Mysql数据库单个表插入元组
的操作。
- public static void
convert()
:调用getTable()和insertTable(String table,int size),也同样执行sql语句,遍历原表中所有字段来加入建表语句,以完成在Mysql数据库中建表并转移数据
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| public class SqlServerToMysql { static Connection connSQLserver = null; static Connection connMysql = null;
public static void setConnSQLserver(Connection connSQLserver) { SqlServerToMysql.connSQLserver = connSQLserver; }
public static void setConnMysql(Connection connMysql) { SqlServerToMysql.connMysql = connMysql; }
static PreparedStatement pstatSqlServer; static PreparedStatement pstatMySql;
public static List<String> getTable(List<String> TableList) { System.out.println("开始获取SqlServer数据库的表"); DatabaseMetaData md = null; List<String> list = new ArrayList<>(); try { md = connSQLserver.getMetaData(); ResultSet rs = md.getTables(null, "dbo", null, null); if (rs != null) { list = new ArrayList<String>(); } while (rs.next()) { if(TableList.contains(rs.getString("TABLE_NAME"))||TableList.isEmpty()) list.add(rs.getString("TABLE_NAME")); } } catch (Exception e) { e.printStackTrace(); } System.out.println("成功获取SqlServer数据库的表"); return list; }
public static boolean insertTable(String table,int size){ try { pstatSqlServer=connSQLserver.prepareStatement("select * from "+table); ResultSet rs=pstatSqlServer.executeQuery(); String separator=""; String insertSql="insert into "+table+" values("; for (int i = 0; i < size; i++) { insertSql+=(separator+"?"); separator=","; } insertSql+=")"; connMysql.setAutoCommit(false); pstatMySql=connMysql.prepareStatement(insertSql);
while (rs.next()){ for (int i = 0; i < size; i++) { pstatMySql.setObject(i+1,rs.getObject(i+1)); } pstatMySql.addBatch(); } pstatMySql.executeBatch(); connMysql.commit(); connMysql.setAutoCommit(true); } catch (Exception e) { e.printStackTrace(); } return true; }
public static void convert(List<String> TableList){ ResultSetMetaData rsmd = null; List<String> list=getTable(TableList); System.out.println(list.size()); for (int i = 0; i < list.size(); i++) { System.out.println("开始转移第" + (i + 1 )+ "数据表..."); String sql = "select * from " + list.get(i); int size = 0; try { pstatSqlServer = connSQLserver.prepareStatement(sql); rsmd = pstatSqlServer.getMetaData(); String createSql = "create table " + list.get(i) + "("; String separator = ""; size = rsmd.getColumnCount(); for (int j = 0; j < size; j++) { createSql += separator + rsmd.getColumnName(j + 1) + " " + rsmd.getColumnTypeName(j + 1); if (rsmd.getPrecision(j + 1) != 0) { createSql += "(" + rsmd.getPrecision(j + 1) + ")"; } separator = ","; } createSql += ");"; pstatMySql=connMysql.prepareStatement(createSql); pstatMySql.execute(); } catch (SQLException e) { e.printStackTrace(); } System.out.println("开始向(" + list.get(i) + ")数据表添加数据..."); if (insertTable( list.get(i), size)){ System.out.println("第" + (i + 1 ) + "个数据表数据转移成功"); } else { System.out.println("第" + (i + 1 ) + "个数据表数据转移失败"); } } } }
|
由 MySQL 转移数据至 SQL Server
类:MysqlToSqlServer
与SQL Server 转移数据至 MySQL 大同小异。不同之处在于:
- 查询获得表的语句为:
ResultSet rs = md.getTables(null, null, null, new String[]{"TABLE"})
,这要求在创建jdbc连接的URL
后面接上nullCatalogMeansCurrent=true
,否则会返回所有数据库表的信息,而不是指定的数据库的表。
- 判断字段类型是否规定长度,注意MySQL中默认INT类型长度为10,而SQL Server中为0,故对于INT型字段要添加过滤条件,往SQL Server数据库插入INT类型的数据时不得指定长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| public class MysqlToSqlServer { static Connection connSQLserver = null; static Connection connMysql = null;
public static void setConnSQLserver(Connection connSQLserver) { MysqlToSqlServer.connSQLserver = connSQLserver; }
public static void setConnMysql(Connection connMysql) { MysqlToSqlServer.connMysql = connMysql; }
static PreparedStatement pstatSqlServer; static PreparedStatement pstatMySql;
public static List<String> getTable(List<String> TableList) { System.out.println("开始获取MySQL数据库的表"); DatabaseMetaData md = null; List<String> list = new ArrayList<>(); try { md = connMysql.getMetaData(); ResultSet rs = md.getTables(null, null, null, new String[]{"TABLE"}); if (rs != null) { list = new ArrayList<String>(); } while (rs.next()) { if(TableList.contains(rs.getString("TABLE_NAME"))||TableList.isEmpty()) list.add(rs.getString("TABLE_NAME")); } } catch (Exception e) { e.printStackTrace(); } System.out.println("成功获取MySQL数据库的表"); return list; }
public static boolean insertTable(String table,int size){ try { pstatMySql=connMysql.prepareStatement("select * from "+table); ResultSet rs=pstatMySql.executeQuery(); String separator=""; String insertSql="insert into "+table+" values("; for (int i = 0; i < size; i++) { insertSql+=(separator+"?"); separator=","; } insertSql+=")"; connSQLserver.setAutoCommit(false); pstatSqlServer=connSQLserver.prepareStatement(insertSql);
while (rs.next()){ for (int i = 0; i < size; i++) { pstatSqlServer.setObject(i+1,rs.getObject(i+1)); } pstatSqlServer.addBatch(); } pstatSqlServer.executeBatch(); connSQLserver.commit(); connSQLserver.setAutoCommit(true); } catch (Exception e) { e.printStackTrace(); } return true; }
public static void convert(List<String> TableList){ ResultSetMetaData rsmd = null; List<String> list=getTable(TableList); System.out.println(list.size()); for (int i = 0; i < list.size(); i++) { System.out.println("开始转移第" + (i + 1 )+ "数据表..."); String sql = "select * from " + list.get(i); int size = 0; try { pstatMySql = connMysql.prepareStatement(sql); rsmd = pstatMySql.getMetaData(); String createSql = "create table " + list.get(i) + "("; String separator = ""; size = rsmd.getColumnCount(); for (int j = 0; j < size; j++) { createSql += separator + rsmd.getColumnName(j + 1) + " " + rsmd.getColumnTypeName(j + 1); if (rsmd.getPrecision(j + 1) != 0&&rsmd.getColumnTypeName(j + 1)!="INT") { createSql += "(" + rsmd.getPrecision(j + 1) + ")"; } separator = ","; } createSql += ");"; pstatSqlServer=connSQLserver.prepareStatement(createSql); pstatSqlServer.execute(); } catch (SQLException e) { e.printStackTrace(); } System.out.println("开始向(" + list.get(i) + ")数据表添加数据..."); if (insertTable( list.get(i), size)){ System.out.println("第" + (i + 1 ) + "个数据表数据转移成功"); } else { System.out.println("第" + (i + 1 ) + "个数据表数据转移失败"); } } } }
|
主函数类
可与使用者交互,由使用者选择功能
,并指定源数据库
和目的数据库
,动态修改连接数据库的配置文件config.properties
成功与指定的数据库连接。然后由使用者可以指定需要转移的表,或者选择转移所有表,程序将执行convert()
函数转移数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| public class main {
public static void modifyConfig(String SqlServer_URL, String Mysql_URL) { try { String filePath = "src/main/resources/config.properties"; Properties properties = new Properties(); properties.load(new FileInputStream(filePath)); properties.setProperty("SqlServer_URL", SqlServer_URL); properties.setProperty("Mysql_URL", Mysql_URL); properties.store(new FileOutputStream(filePath), ""); } catch (Exception e) { e.printStackTrace(); } }
public static void main(String[] args) { try { System.out.println(System.getProperty("java.library.path")); Scanner in = new Scanner(System.in); System.out.println("请选择功能:"); System.out.println("1.从SqlServer转移数据至MySQL"); System.out.println("2.从MySQL转移数据至SqlServer"); System.out.println("其它: 退出程序"); int op = in.nextInt(); if(op!=1&&op!=2) return; System.out.println("请输入源数据库名:"); String src = in.next(); System.out.println("请输入目的数据库名:"); String des = in.next(); List<String> TableList = new ArrayList<>(); System.out.println("请要转移的表名:(以回车分隔,输入#结束; 若只输入#,则转移所有表)"); String TableName; do { TableName= in.next(); if(!TableName.equals("#")) { TableList.add(TableName); } else { break; } }while(true); if (op == 1) { String SqlServer_URL = "jdbc:sqlserver://127.0.0.1:1433;DatabaseName=" + src; String Mysql_URL = "jdbc:mysql://localhost:3306/" + des; modifyConfig(SqlServer_URL, Mysql_URL); SqlServerConnection SqlServerConn = new SqlServerConnection(); MysqlConnection MysqlConn = new MysqlConnection(); SqlServerConn.connect(); MysqlConn.connect(); SqlServerToMysql.setConnSQLserver(SqlServerConn.getConnection()); SqlServerToMysql.setConnMysql(MysqlConn.getConnection()); System.out.println("开始转移数据"); long start = System.currentTimeMillis(); SqlServerToMysql.convert(TableList); System.out.println("迁移完毕,耗时:"+(System.currentTimeMillis()-start)+"ms"); } else if (op==2){ String SqlServer_URL = "jdbc:sqlserver://127.0.0.1:1433;DatabaseName=" + des; String Mysql_URL = "jdbc:mysql://localhost:3306/" + src+"?nullCatalogMeansCurrent=true"; modifyConfig(SqlServer_URL, Mysql_URL); SqlServerConnection SqlServerConn = new SqlServerConnection(); MysqlConnection MysqlConn = new MysqlConnection(); SqlServerConn.connect(); MysqlConn.connect(); MysqlToSqlServer.setConnSQLserver(SqlServerConn.getConnection()); MysqlToSqlServer.setConnMysql(MysqlConn.getConnection()); System.out.println("开始转移数据"); long start = System.currentTimeMillis(); MysqlToSqlServer.convert(TableList); System.out.println("迁移完毕,耗时:"+(System.currentTimeMillis()-start)+"ms"); } } catch (Exception e) { e.printStackTrace(); } } }
|
5 实验效果
由 SQL Server 转移数据至 MySQL
转移指定的表
转移所有表
由 MySQL 转移数据至 SQL Server
转移指定的表
转移所有表
6 实验心得
这次实验让我进一步熟练了如何通过JDBC连接MySQL数据库并进行操作,而是首次接触SQL Server,刚开始使用Windows身份认证
配置连接,结果接连出错,后来改用SQL Server身份认证
,通过登录名和密码连接数据库从而成功在Java中配置数据源。
而在转移数据部分代码的编写中,我学会了如何使用getMetaData()
获取当前连接数据库的元数据,并通过getTable()
获取数据表,这里要格外注意在SQL Server和MySQL获取表信息的区别。既SQL Server查询获得表的语句getTables(null, "dbo", null, null)
为,MySQL的为:md.getTables(null, null, null, new String[]{"TABLE"})
。对于Mysql,需要在连接URL
后面接上nullCatalogMeansCurrent=true
,否则会返回所有数据库表的信息,而不是指定的数据库的表。
此次我对预编译SQL语句的对象prepareStatement
的使用更加熟练,并学会如何对一组SQL语句事务
进行批处理,即对于一个连接通过setAutoCommit(false)
关闭自动提交,对于预编译对象,通过addBatch()
将填充后SQL语句加入批处理队列,使用executeBatch()
批量更新sql语句。最后对于连接,通过commit()
将这组SQL事务提交,一次性执行。这样批量处理sql数据提高了数据库执行SQL语句的效率,解决了数据库处理速度快与sql语句一次次传输需要时间的冲突
在主函数中,我增加了与用户的交互模块,使使用者可以选择需要的转移数据功能,直接修改配置文件指定源数据库和目的数据库,使用列表保存用户需要转移的表,不将转移数据局限于固定的数据库和固定的表中中,即灵活地实现转移异构数据库中数据。