当前位置:   article > 正文

MySql表结构迁移到PostgreSql(pgsql) 方案_mysql迁移到postgre

mysql迁移到postgre

由于工作需要,要将原有的mysql的表结构和数据都迁移到pgsql里面,因为pgsql支持更大数据存储,还能做AI大模型的向量数据库。
网上找了很多种办法,这种跨数据库的迁移方案都是收费的。只有navicate 工具可以免费迁移,怎耐我们公司禁止使用navicate 。

没有办法,我只能首先java代码实现数据库表结构迁移工作。

下面就是代码,本打算从mysql读取所有的数据库结构直接插入到pgsql里面,但是实际却发现有很多不合理的地方,与改成了将所有的mysql结构生成pgsql 的结构打印出来。使用者可以拷贝打印出来的内容形成一个脚本再使用。

里面有类型转换我中用到我们实际使用到的结果,还有不少没列出来,使用时要注意一下。

  1. import java.sql.*;
  2. import java.util.*;
  3. public class MysqlToPgsqlMigration {
  4. private static final String MYSQL_URL = "jdbc:mysql://127.0.0.1:3306/test";
  5. private static final String MYSQL_USER = "root";
  6. private static final String MYSQL_PASSWORD = "root";
  7. private static final String POSTGRESQL_URL = "jdbc:postgresql://127.0.0.1:5432/jeecg-boot";
  8. private static final String POSTGRESQL_USER = "root";
  9. private static final String POSTGRESQL_PASSWORD = "root1";
  10. public static void main(String[] args) throws SQLException, ClassNotFoundException {
  11. Class.forName("com.mysql.cj.jdbc.Driver");
  12. Class.forName("org.postgresql.Driver");
  13. try (Connection mysqlConn = DriverManager.getConnection(MYSQL_URL, MYSQL_USER, MYSQL_PASSWORD);
  14. Connection pgsqlConn = DriverManager.getConnection(POSTGRESQL_URL, POSTGRESQL_USER, POSTGRESQL_PASSWORD)) {
  15. DatabaseMetaData mysqlMeta = mysqlConn.getMetaData();
  16. Statement pgsqlStmt = pgsqlConn.createStatement();
  17. ResultSet tablesRs = mysqlMeta.getTables(null, null, "%", new String[]{"TABLE"});
  18. while (tablesRs.next()) {
  19. String tableName = tablesRs.getString("TABLE_NAME");
  20. // 重建表结构
  21. StringBuilder createTableSql = new StringBuilder("CREATE TABLE ");
  22. createTableSql.append(tableName).append(" (\n");
  23. ResultSet columnsRs = mysqlMeta.getColumns(null, null, tableName, null);
  24. List<String> primaryKeyColumns = new ArrayList<>();
  25. List<String> indexKeyColumns = new ArrayList<>();
  26. List<String> columnComments = new ArrayList<>();
  27. List<String> uniqueindexKeyColumns = new ArrayList<>();
  28. while (columnsRs.next()) {
  29. String columnName = columnsRs.getString("COLUMN_NAME");
  30. int dataType = columnsRs.getInt("DATA_TYPE");
  31. String typeName = mapColumnType(dataType);
  32. int size = columnsRs.getInt("COLUMN_SIZE");
  33. boolean isNullable = columnsRs.getBoolean("NULLABLE");
  34. String columnDef = columnsRs.getString("COLUMN_DEF");
  35. String columnComment = columnsRs.getString("REMARKS");
  36. createTableSql.append("\t").append(columnName).append(' ').append(typeName);
  37. if (!"INTEGER".equalsIgnoreCase(typeName)
  38. &&!"int4".equalsIgnoreCase(typeName)
  39. &&!"int8".equalsIgnoreCase(typeName)
  40. &&!"smallint".equalsIgnoreCase(typeName)
  41. &&!"bigint".equalsIgnoreCase(typeName)
  42. &&!"text".equalsIgnoreCase(typeName)){
  43. if (size > 0) {
  44. createTableSql.append('(').append(size).append(')');
  45. }
  46. }
  47. if (!isNullable) {
  48. createTableSql.append(" NOT NULL");
  49. }
  50. if (columnDef != null) {
  51. if ("INTEGER".equalsIgnoreCase(typeName)
  52. ||"int4".equalsIgnoreCase(typeName)
  53. ||"int8".equalsIgnoreCase(typeName)
  54. ||"smallint".equalsIgnoreCase(typeName)
  55. ||"bigint".equalsIgnoreCase(typeName)
  56. ||"NUMERIC".equalsIgnoreCase(typeName)){
  57. createTableSql.append(" DEFAULT ").append(columnDef);
  58. }else if("TIMESTAMP".equalsIgnoreCase(typeName)){
  59. createTableSql.append(" DEFAULT ").append("TO_TIMESTAMP('2024-02-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')");
  60. }else{
  61. createTableSql.append(" DEFAULT ").append("'" + escapeSingleQuotes(columnDef) + "'");
  62. }
  63. }
  64. columnComments.add(addColumnComment(tableName, columnName, columnComment, pgsqlStmt));
  65. createTableSql.append(",\n");
  66. // 检查是否为主键
  67. ResultSet pkRs = mysqlMeta.getPrimaryKeys(null, null, tableName);
  68. while (pkRs.next() && pkRs.getString("COLUMN_NAME").equals(columnName)) {
  69. primaryKeyColumns.add(columnName);
  70. }
  71. pkRs.close();
  72. }
  73. // 移除最后一个逗号和换行符
  74. createTableSql.setLength(createTableSql.length() - 2);
  75. // 添加主键约束
  76. if (!primaryKeyColumns.isEmpty()) {
  77. createTableSql.append(", \n PRIMARY KEY (")
  78. .append(String.join(",", primaryKeyColumns))
  79. .append(")");
  80. }
  81. createTableSql.append("\n);");
  82. System.out.println(createTableSql);
  83. //pgsqlStmt.executeUpdate(createTableSql.toString());
  84. // 处理索引及联合索引
  85. Map<String,List<String>> unIndexMap = new HashMap<>();
  86. ResultSet uniqueindexSet = mysqlMeta.getIndexInfo(null, null, tableName, true, false);
  87. while (uniqueindexSet.next()) {
  88. String indexName = uniqueindexSet.getString("INDEX_NAME"); // 索引名称
  89. String columnName = uniqueindexSet.getString("COLUMN_NAME"); // 索引列名
  90. /*boolean isNonUnique = uniqueindexSet.getBoolean("NON_UNIQUE"); // 是否非唯一索引(0表示唯一索引,1表示非唯一索引)
  91. System.out.println("indexName="+indexName+",columnName="+columnName+",isNonUnique="+isNonUnique);*/
  92. if ("PRIMARY".equalsIgnoreCase(indexName)){
  93. continue;
  94. }
  95. List<String> indexCols = unIndexMap.get(indexName);
  96. if (indexCols == null){
  97. indexCols = new ArrayList<>();
  98. }
  99. if (!indexCols.contains(columnName)){
  100. indexCols.add(columnName);
  101. }
  102. unIndexMap.put(indexName,indexCols);
  103. uniqueindexKeyColumns.add(columnName);
  104. }
  105. for (Map.Entry<String, List<String>> entry : unIndexMap.entrySet()) {
  106. String indexName = entry.getKey();
  107. List<String> list = entry.getValue();
  108. if (list.size()>1){ // 联合索引
  109. System.out.println("CREATE UNIQUE INDEX "+indexName+" ON "+tableName+"("+String.join(",",list)+");");
  110. }else{// 普通索引
  111. if (indexName.startsWith("uni")||indexName.startsWith("uk_")){
  112. System.out.println("CREATE UNIQUE INDEX "+indexName+" ON "+tableName+"("+list.get(0)+");");
  113. }else{
  114. System.out.println("CREATE INDEX "+indexName+" ON "+tableName+"("+list.get(0)+");");
  115. }
  116. }
  117. }
  118. uniqueindexSet.close();
  119. // 处理外键索引
  120. /*ResultSet foreignKeysRS = mysqlMeta.getImportedKeys(null, null, tableName);
  121. while (foreignKeysRS.next()) {
  122. String fkName = foreignKeysRS.getString("FK_NAME"); // 外键约束名称
  123. String fkColumnName = foreignKeysRS.getString("FKCOLUMN_NAME"); // 本表中对应的外键列名
  124. String pkTableName = foreignKeysRS.getString("PKTABLE_NAME"); // 主键所在的表名
  125. String pkColumnName = foreignKeysRS.getString("PKCOLUMN_NAME"); // 主键列名
  126. System.out.println("Foreign Key: " + fkName);
  127. System.out.println("From Column: " + fkColumnName + " in Table " + tableName);
  128. System.out.println("References Column: " + pkColumnName + " in Table " + pkTableName);
  129. }
  130. foreignKeysRS.close();*/
  131. //字段注释
  132. if (!columnComments.isEmpty()){
  133. for (String comment : columnComments) {
  134. System.out.println(comment);
  135. }
  136. }
  137. // 获取表注释并添加
  138. String tableComment = getTableComment(tableName, mysqlMeta);
  139. System.out.println(addTableComment(tableName, tableComment, pgsqlStmt));
  140. System.out.println("\n");
  141. // 插入数据
  142. //copyTableData(tableName, mysqlConn, pgsqlConn);
  143. }
  144. tablesRs.close();
  145. }
  146. }
  147. private static String addTableComment(String tableName, String comment, Statement stmt) throws SQLException {
  148. /*if (comment != null && !comment.isEmpty()) {
  149. stmt.executeUpdate("COMMENT ON TABLE " + tableName + " IS '" + escapeSingleQuotes(comment) + "'");
  150. }*/
  151. return "COMMENT ON TABLE " + tableName + " IS '" + escapeSingleQuotes(comment) + "';";
  152. }
  153. private static String addColumnComment(String tableName, String columnName, String comment, Statement stmt) throws SQLException {
  154. return "COMMENT ON COLUMN " + tableName + "." + columnName + " IS '" + escapeSingleQuotes(comment) + "';";
  155. /*if (comment != null && !comment.isEmpty()) {
  156. stmt.executeUpdate("COMMENT ON COLUMN " + tableName + "." + columnName + " IS '" + escapeSingleQuotes(comment) + "'");
  157. }*/
  158. }
  159. private static String getTableComment(String tableName, DatabaseMetaData meta) throws SQLException {
  160. ResultSet rs = meta.getTables(null, null, tableName, null);
  161. if (rs.next()) {
  162. return rs.getString("REMARKS");
  163. }
  164. return "";
  165. }
  166. private static String mapColumnType(int mysqlDataType) {
  167. // 这里仅提供一种简单的映射方式,实际情况可能更复杂
  168. switch (mysqlDataType) {
  169. // case Types.BIT:
  170. // return "BOOLEAN";
  171. case Types.TINYINT:
  172. return "SMALLINT";
  173. case Types.BIGINT:
  174. return "BIGINT";
  175. case Types.INTEGER:
  176. return "INTEGER";
  177. case Types.DATE:
  178. return "timestamp";
  179. case Types.DOUBLE:
  180. return "NUMERIC";
  181. case Types.NUMERIC:
  182. return "NUMERIC";
  183. case Types.DECIMAL:
  184. return "NUMERIC";
  185. case Types.LONGVARCHAR:
  186. return "text";
  187. case Types.TIMESTAMP:
  188. return "TIMESTAMP";
  189. case Types.BLOB:
  190. return "BYTEA";
  191. // 其他类型...
  192. default:
  193. return "varchar";
  194. }
  195. }
  196. private static void copyTableData(String tableName, Connection mysqlConn, Connection pgsqlConn) throws SQLException {
  197. String selectAllDataSql = "SELECT * FROM " + tableName;
  198. PreparedStatement mysqlPs = mysqlConn.prepareStatement(selectAllDataSql);
  199. ResultSet dataRs = mysqlPs.executeQuery();
  200. ResultSetMetaData metaData = dataRs.getMetaData();
  201. StringBuilder insertSql = new StringBuilder("INSERT INTO ").append(tableName).append(" VALUES (");
  202. for (int i = 1; i <= metaData.getColumnCount(); i++) {
  203. insertSql.append("?");
  204. if (i < metaData.getColumnCount()) {
  205. insertSql.append(",");
  206. }
  207. }
  208. insertSql.append(")");
  209. PreparedStatement pgsqlPs = pgsqlConn.prepareStatement(insertSql.toString());
  210. while (dataRs.next()) {
  211. for (int i = 1; i <= metaData.getColumnCount(); i++) {
  212. pgsqlPs.setObject(i, dataRs.getObject(i));
  213. }
  214. pgsqlPs.addBatch();
  215. }
  216. pgsqlPs.executeBatch();
  217. }
  218. private static String escapeSingleQuotes(String str) {
  219. return str.replace("'", "''");
  220. }
  221. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/401332
推荐阅读
相关标签
  

闽ICP备14008679号