您的位置:首页 > 编程语言 > Java开发

Java使用springboot+mybatis+Navicat实现MySQL转SqlServer(超详细)

2020-07-11 23:12 197 查看

文章目录

因某些原因最近需要把mysql转换为SqlServer数据库,以下为进行转换的过程及遇到的坑

1.使用Navicat进行表结构同步

1.Navicat连接SqlServer

1.报错如下

解决方案:进入Navicat安装目录找到如下文件,进行无脑安装,一路下一步即可

2.连接SqlServer数据库

第二次报错:

08001][Mircrosoft][SQL Server Native Client 10.0]Named Pipes Provider:Could not open a connection to SQL Server[53]. (53)

[HYT00][Mircrosoft][SQL Server Native Client 10.0]Login timeout expried(0)

[08001][Mircrosoft][SQL Server Native Client 10.0]A network-related or instance-specific error has occurred while establishing aconnection to SQL Server is not found or not accessible. Check if instance name iscorrect and if SQL Server is configured to allow remote connetcions. For more information see SQL Server Books Online. (53)

解决方案:ip和port使用逗号连接如下即可

127.0.0.1,3433 ===》 ip,port

2.进行表结构同步

工具==》数据传输

按需进行同步配置

下一步,选择需要同步的表

生成的SQL语句

/*
Navicat Premium Data Transfer

Source Server         : localhost
Source Server Type    : MySQL
Source Server Version : 50622
Source Host           : localhost:3306
Source Schema         : mybatis

Target Server Type    : SQL Server
Target Server Version : 80000
File Encoding         : 65001

Date: 11/07/2020 22:01:02
*/

-- ----------------------------
-- Table structure for tb_item
-- ----------------------------
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[mybatis].[tb_item]') AND type IN ('U'))
DROP TABLE [mybatis].[tb_item]
GO

CREATE TABLE [mybatis].[tb_item] (
[id] int NOT NULL PRIMARY KEY IDENTITY,
[item_name] nvarchar(32) NOT NULL,
[item_price] real NOT NULL,
[item_detail] ntext NULL,
[update_time] datetime NOT NULL
)
GO

EXEC sp_addextendedproperty
'MS_Description', N'商品名称',
'USER', N'mybatis',
'TABLE', N'tb_item',
'COLUMN', N'item_name'
GO

EXEC sp_addextendedproperty
'MS_Description', N'商品价格',
'USER', N'mybatis',
'TABLE', N'tb_item',
'COLUMN', N'item_price'
GO

EXEC sp_addextendedproperty
'MS_Description', N'商品描述',
'USER', N'mybatis',
'TABLE', N'tb_item',
'COLUMN', N'item_detail'
GO

EXEC sp_addextendedproperty
'MS_Description', N'更新时间',
'USER', N'mybatis',
'TABLE', N'tb_item',
'COLUMN', N'update_time'
GO

-- ----------------------------
-- Primary Key structure for table tb_item
-- ----------------------------
ALTER TABLE [mybatis].[tb_item] ADD PRIMARY KEY CLUSTERED ([id])
GO

原mysql表,注意这张表的id是自增的,并且update_time 字段会根据当前系统时间自动更新

/*
Navicat Premium Data Transfer

Source Server         : localhost
Source Server Type    : MySQL
Source Server Version : 50622
Source Host           : localhost:3306
Source Schema         : mybatis

Target Server Type    : MySQL
Target Server Version : 50622
File Encoding         : 65001

Date: 11/07/2020 22:26:46
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_item
-- ----------------------------
DROP TABLE IF EXISTS `tb_item`;
CREATE TABLE `tb_item`  (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '商品名称',
`item_price` decimal(6, 1) NOT NULL DEFAULT 0.0 COMMENT '商品价格',
`item_detail` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品描述',
`update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

2.使用工具类优化SqlServer建表语句

注意,经过对比发现以上使用Navicat生成的SqlServer建表语句(以下简称建表语句)没有id自增长,所有字段都没有默认值,update_time 字段也不会自动获取当前系统时间(属实有些蛋疼),小弟没找到好的方案,于是自己写了几个工具类。

1.id自增长

首先使用Navicat导出MySQL的建表语句

/*
Navicat Premium Data Transfer

Source Server         : localhost
Source Server Type    : MySQL
Source Server Version : 50622
Source Host           : localhost:3306
Source Schema         : mybatis

Target Server Type    : MySQL
Target Server Version : 50622
File Encoding         : 65001

Date: 11/07/2020 22:26:46
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_item
-- ----------------------------
DROP TABLE IF EXISTS `tb_item`;
CREATE TABLE `tb_item`  (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '商品名称',
`item_price` decimal(6, 1) NOT NULL DEFAULT 0.0 COMMENT '商品价格',
`item_detail` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品描述',
`update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

工具类

package cn.xiaozhuang.common.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.HashMap;

public class SqlServerAutoIncrement {
private static final String SQL_SCRIPT = " PRIMARY KEY IDENTITY,";

public static void main(String[] args) {
//        使用Navicat生成的MySQL建表语句
String mysqlScriptPath = "F:\\learn\\demo\\tb_item.sql";
//        使用Navicat生成的sqlServer建表语句
String sqlServerScriptPath = "F:\\learn\\demo\\demo.sql";
//        已经添加id自增的sqlServer建表语句输出路径
String sqlServerScriptOutPath = "F:\\learn\\demo\\tb_item_sqlServer.sql";

//      获取MySQL脚本中需要id自增的表
HashMap<String, String> map = getTableMap(mysqlScriptPath);
//        遍历SqlServerDDL,当语句中包含map的表名时,拼接 id字段
createSqlScript(sqlServerScriptPath, sqlServerScriptOutPath, map);
}

/**
* @param sqlServerScriptPath    使用Navicat生成的sqlServer建表语句
* @param sqlServerScriptOutPath 已经添加id自增的sqlServer建表语句输出路径
* @param map                    需要id自增的表
*/
private static void createSqlScript(String sqlServerScriptPath, String sqlServerScriptOutPath, HashMap<String, String> map) {
try (BufferedReader sqlServerReader = new BufferedReader(new FileReader(sqlServerScriptPath));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(sqlServerScriptOutPath))
) {
String tableName;
int count = 0;
while (sqlServerReader.ready()) {
String line = sqlServerReader.readLine();
bufferedWriter.write(line);
bufferedWriter.newLine();
if (line.contains("CREATE TABLE")) {
//                   获取表名
tableName = line.substring(line.lastIndexOf("[") + 1, line.lastIndexOf("]"));
if (map.containsKey(tableName)) {
//                    获取表名下一行
String lineNext = sqlServerReader.readLine();
if (lineNext.contains("[id]")) {
count++;
String concat = lineNext.replace(",", "").concat(SQL_SCRIPT);
bufferedWriter.write(concat);
bufferedWriter.newLine();
}
}
}
}
System.out.println("已处理的SqlServer的表数量 " + count);
} catch (Exception e) {
e.printStackTrace();
}
}

private static HashMap<String, String> getTableMap(String mysqlScriptPath) {
HashMap<String, String> map = new HashMap<>();
try (BufferedReader mysqlReader = new BufferedReader(new FileReader(mysqlScriptPath))
) {
String tableName;
while (mysqlReader.ready()) {
String line = mysqlReader.readLine();
if (line.contains("CREATE TABLE")) {
tableName = line.substring(line.indexOf("`") + 1, line.lastIndexOf("`"));
line = mysqlReader.readLine();
if (line.contains("AUTO_INCREMENT")) {
map.put(tableName, "");
}
}
}
System.out.println("需要id自增的表数量 : " + map.size());
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
}

使用工具类生成带id自增长的建表语句

2.生成默认值

package cn.xiaozhuang.common.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;

public class SqlServerDefaultValueUtils {

public static void main(String[] args) {
String mysqlDDLPath = "F:\\learn\\demo\\tb_item.sql";
String sqlServerAlterFilePath = "F:\\learn\\demo\\tb_item_default_value.sql";
createDefaultValueAlterScript(mysqlDDLPath, sqlServerAlterFilePath);
countFieldLine(mysqlDDLPath);
}

/**
*
* 根据mysqlDDL 生成SQLserver alter 默认值语句
*/
private static void createDefaultValueAlterScript(String mysqlDDLPath, String sqlServerAlterFilePath) {
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(mysqlDDLPath));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(sqlServerAlterFilePath))
) {
StringBuilder builder = new StringBuilder("ALTER TABLE ");
while (bufferedReader.ready()) {
String mysqlLine = bufferedReader.readLine();

int beginIndex = mysqlLine.indexOf("`");
if (mysqlLine.contains("CREATE TABLE")) {
builder.append(mysqlLine.substring(beginIndex, mysqlLine.lastIndexOf("`")));
}

if (mysqlLine.contains("DEFAULT ''")) {
builder.append(" ADD ").append(" DEFAULT '' FOR ").append(mysqlLine.substring(beginIndex, mysqlLine.indexOf("`", beginIndex + 1))).append(" ;");
bufferedWriter.write(builder.toString().replace("`", "") + System.lineSeparator());
bufferedWriter.flush();
builder.delete(builder.toString().indexOf(" ADD"), builder.toString().length());
} else if (mysqlLine.contains("DEFAULT CURRENT_TIMESTAMP")) {
builder.append(" ADD ").append(" DEFAULT (getdate()) FOR ").append(mysqlLine.substring(beginIndex, mysqlLine.indexOf("`", beginIndex + 1))).append(" ;");
bufferedWriter.write(builder.toString().replace("`", "") + System.lineSeparator());
bufferedWriter.flush();
builder.delete(builder.toString().indexOf(" ADD"), builder.toString().length());
} else if (mysqlLine.contains("DEFAULT") && !mysqlLine.contains("InnoDB")) {
//                    获取原默认值
String defalueValue = "";
if (mysqlLine.contains("COMMENT")) {
defalueValue = mysqlLine.substring(mysqlLine.indexOf("DEFAULT")+7, mysqlLine.indexOf("COMMENT",mysqlLine.indexOf("DEFAULT")));
} else {
try {
defalueValue = mysqlLine.substring(mysqlLine.indexOf("DEFAULT")+7, mysqlLine.lastIndexOf(","));
} catch (Exception e) {
System.out.println("出错 :"+mysqlLine);
}
}
builder.append(" ADD ")
.append(" DEFAULT ")
.append(defalueValue)
.append(" FOR ")
.append(mysqlLine.substring(beginIndex, mysqlLine.indexOf("`", beginIndex + 1)))
.append(";");
bufferedWriter.write(builder.toString().replace("`", "") + System.lineSeparator());
bufferedWriter.flush();
builder.delete(builder.toString().indexOf(" ADD"), builder.toString().length());
}else {
System.out.println("忽略的行  "+mysqlLine);
}
if (mysqlLine.contains("ENGINE=InnoDB")) {
builder.delete(12, builder.toString().length());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 计算共有多少字段
* @param mysqlDDLPath
*/
private static void countFieldLine(String mysqlDDLPath) {
int count = 0;
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(mysqlDDLPath))) {
while (bufferedReader.ready()) {
String mysqlLine = bufferedReader.readLine();
if (mysqlLine.contains("CREATE TABLE")) {
while (true) {
String s = bufferedReader.readLine();
if (s.contains("PRIMARY KEY")) {
break;
}
count++;
}
}
}
System.out.println("fieldCount = " + count);
} catch (Exception e) {
e.printStackTrace();
}
}
}

生成的SqlServer alter语句

ALTER TABLE tb_item ADD  DEFAULT '' FOR item_name ;
ALTER TABLE tb_item ADD  DEFAULT  0.0  FOR item_price;
// getdate() 插入时自动获取系统时间
ALTER TABLE tb_item ADD  DEFAULT (getdate()) FOR update_time ;

3.update_time根据修改时的时间戳更新

SqlServer不能够向mysql一样建表语句中直接设置,而是需要使用触发器,以下为工具类

package cn.xiaozhuang.common.utils;

import java.io.*;

public class SqlServerUpdateTimeTriggerUtils {
public static void main(String[] args) {
String mysqlDDLPath = "F:\\learn\\demo\\tb_item.sql";
String sqlServerAlterFilePath = "F:\\learn\\demo\\tb_item_trigger.sql";
createUpdateTimeTrigger(mysqlDDLPath, sqlServerAlterFilePath, "update_time",false);
createUpdateTimeTrigger(mysqlDDLPath, sqlServerAlterFilePath, "gmt_modified",true);
}
/**
*  创建SQLserver 字段根据当前时间更新(触发器)
* @param mysqlDDLPath MySQL建表语句
* @param sqlServerAlterFilePath 生成的触发器路径
* @param field 要自动更新的字段
* @param append 是否向 sqlServerAlterFilePath 中追加
*/
private static void createUpdateTimeTrigger(String mysqlDDLPath, String sqlServerAlterFilePath, String field,boolean append) {
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(mysqlDDLPath));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(sqlServerAlterFilePath,append))
) {
String tableName = "tableName";
while (bufferedReader.ready()) {
String line = bufferedReader.readLine();
if (line.contains("CREATE TABLE")) {
int beginIndex = line.indexOf("`");
tableName = line.substring(beginIndex + 1, line.lastIndexOf("`"));
}
if (line.contains(field)) {
createTrigger(bufferedWriter, tableName, field);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

private static void createTrigger(BufferedWriter bufferedWriter, String tableName, String field) throws IOException {
//      hera为SqlServer库名
bufferedWriter.write("USE [hera]");
bufferedWriter.newLine();
bufferedWriter.write("GO");
bufferedWriter.newLine();
bufferedWriter.write("SET ANSI_NULLS ON");
bufferedWriter.newLine();
bufferedWriter.write("GO");
bufferedWriter.newLine();
bufferedWriter.write("SET QUOTED_IDENTIFIER ON");
bufferedWriter.newLine();
bufferedWriter.write("GO");
bufferedWriter.newLine();
//      dbo为模式名
bufferedWriter.write("CREATE TRIGGER [dbo]." + tableName + "_" + field + "");
bufferedWriter.newLine();
bufferedWriter.write("ON [dbo].[" + tableName + "]");
bufferedWriter.newLine();
bufferedWriter.write("AFTER UPDATE AS ");
bufferedWriter.newLine();
bufferedWriter.write("BEGIN");
bufferedWriter.newLine();
bufferedWriter.write("SET NOCOUNT ON;");
bufferedWriter.newLine();
bufferedWriter.write("UPDATE [dbo].[" + tableName + "]");
bufferedWriter.newLine();
bufferedWriter.write("SET " + field + "=SYSDATETIME()");
bufferedWriter.newLine();
bufferedWriter.write("WHERE ID IN (SELECT DISTINCT ID FROM inserted)");
bufferedWriter.newLine();
bufferedWriter.write("END");
bufferedWriter.newLine();
bufferedWriter.write("GO");
bufferedWriter.newLine();
bufferedWriter.write("ALTER TABLE [dbo].[" + tableName + "] ENABLE TRIGGER " + tableName + "_" + field + ";");
bufferedWriter.newLine();
bufferedWriter.newLine();
}
}

生成的触发器

USE [hera]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].tb_item_update_time
ON [dbo].[tb_item]
AFTER UPDATE AS
BEGIN
SET NOCOUNT ON;
UPDATE [dbo].[tb_item]
SET update_time=SYSDATETIME()
WHERE ID IN (SELECT DISTINCT ID FROM inserted)
END
GO
ALTER TABLE [dbo].[tb_item] ENABLE TRIGGER tb_item_update_time;

上述脚本按照以上顺序执行即可,ok,这时数据库已准备妥当

这是使用insert语句如果给了 id字段值或报错如下:

[Microsoft][SQL Server Native Client 10.0][SQL Server]Cannot insert explicit value for identity column in table 'tb_item' when IDENTITY_INSERT is set to OFF.

错误很明显,SqlServer开启主键自增时,insert语句不能给id值

语句改为如下即可

set IDENTITY_INSERT [dbo].[tb_item]  on;
-- 注意 SqlServer的字段名不能加 `
INSERT INTO tb_item (id, item_name, item_price, item_detail, update_time) VALUES (1, N'iPhone 6', 5288.0, N'苹果公司新发布的手机产品。', '2020-07-11 21:59:27');

set IDENTITY_INSERT [dbo].[tb_item]  off;

3.使用java程序操作SqlServer

所需java框架: springboot+mybatis-plus

其他依赖和连接MySQL没有不同,另外连接SqlServer需要引入 maven依赖

<!--SQLServer-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>4.0</version>
</dependency>

连接配置

#SqlServer版
spring.datasource.url=jdbc:sqlserver://127.0.0.1:3433;databasename=hera
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver

1.如果项目操作数据库报错 Invalid Object Name 但是表又真实存在,把数据表建在 dbo 模式下,否则需要在sql语句表前加模式前缀

SELECT * FROM 模式.表名

2.使用 mp 批量插入saveBatch() 方法报错

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: 必须执行该语句才能获得结果。
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:190)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.getGeneratedKeys(SQLServerStatement.java:2040)
at com.alibaba.druid.filter.FilterChainImpl.statement_getGeneratedKeys(FilterChainImpl.java:3091)
at com.alibaba.druid.filter.FilterAdapter.statement_getGeneratedKeys(FilterAdapter.java:2546)
at com.alibaba.druid.filter.FilterEventAdapter.statement_getGeneratedKeys(FilterEventAdapter.java:415)
at com.alibaba.druid.filter.FilterChainImpl.statement_getGeneratedKeys(FilterChainImpl.java:3088)
at com.alibaba.druid.proxy.jdbc.StatementProxyImpl.getGeneratedKeys(StatementProxyImpl.java:309)
at com.alibaba.druid.pool.DruidPooledStatement.getGeneratedKeys(DruidPooledStatement.java:821)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:78)
at com.sun.proxy.$Proxy141.getGeneratedKeys(Unknown Source)
at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.processBatch(Jdbc3KeyGenerator.java:63)
... 160 common frames omitted

待解决

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐