项目从 Mysql切换 PostgreSQL 改造及踩坑记录

0、前言

原项目框架 SpringBoot + MybatisPlus + Mysql

1、切换流程

1.1、项目引入postgresql驱动包

由于我们要连接新的数据库,理所当然的要引入该数据库的驱动包,这与mysql驱动包类似

<dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
</dependency>

1.2、修改jdbc连接信息

之前用的是mysql协议,现在改成postgresql连接协议

spring:
  datasource:
    # 修改驱动类
    driver-class-name: org.postgresql.Driver
    # 修改连接地址
    url: jdbc:postgresql://数据库地址/数据库名?currentSchema=模式名&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false

postgres相比mysql多了一层模式的概念, 一个数据库下可以有多个模式。 这里的模型名等价于以前的mysql的数据库名。 如果不指定默认是public。

这时切换流程基本就改造完了,无非就是代码修改下连接信息。 但是你以为到这就结束了? 一堆坑还在后面呢,毕竟是两个完全不同数据库在语法层面还有很多差别,接下来就是修改代码里的sql语法踩坑

2、踩坑记录

2.1、TIMESTAMPTZ类型与LocalDateTime不匹配

异常信息:

PSQLException: Cannot convert the column of type TIMESTAMPTZ to requested type java.time.LocalDateTime.

如果postgres表的字段类型是TIMESTAMPTZ ,但是java对象的字段类型是LocalDateTime, 这时会无法转换映射上。 postgres表字段类型应该用timestamp 或者 java字段类型用Date

2.2、参数值不能用双引号

错误例子:

 WHERE name = "jay"   ===>    WHERE name = 'jay'

这里参数值"jay" 应该改成单引号 ‘jay’

2.3、字段不能用``包起来

错误例子

 WHERE `name` = 'jay'  ==>    WHERE name = 'jay'

这里的字段名name不能用``选取

2.4、json字段处理语法不同

-- mysql语法: 
WHERE keywords_json->'$.name' LIKE CONCAT('%', ?, '%')

-- postgreSQL语法:
WHERE keywords_json ->>'name' LIKE CONCAT('%', ?, '%')

获取json字段子属性的值mysql是用 -> '$.xxx'的语法去选取的,
而 postgreSQL 得用 ->>'xx' 语法选择属性

2.5、convert函数不存在

postgreSQL没有convert函数,用CAST函数替换

-- mysql语法: 
select convert(name, DECIMAL(20, 2))

-- postgreSQL语法:
select CAST(name as DECIMAL(20, 2))

2.6、force index 语法不存在

-- mysql语法
select xx FROM user force index(idx_audit_time)

mysql可以使用force index强制走索引, postgres没有,建议去掉

2.7、ifnull 函数不存在

postgreSQL没有ifnull函数,用COALESCE函数替换

异常信息

cause: org.postgresql.util.PSQLException: ERROR: function ifnull(numeric, numeric) does not exist

2.8、date_format 函数不存在

异常信息

Cause: org.postgresql.util.PSQLException: ERROR: function date_format(timestamp without time zone, unknown) does not exist

postgreSQL没有date_format函数,用to_char函数替换

替换例子:

// %Y => YYYY 
// %m  =>   MM
// %d   =>  DD
// %H => HH24
// %i => MI
// %s => SS
to_char(time,'YYYY-MM-DD') => DATE_FORMAT(time,'%Y-%m-%d')
to_char(time,'YYYY-MM') => DATE_FORMAT(time,'%Y-%m')
to_char(time,'YYYYMMDDHH24MISS') => DATE_FORMAT(time,'%Y%m%d%H%i%s')

2.9、group by语法问题

异常信息

Cause: org.postgresql.util.PSQLException: ERROR: column  "r.name" must appear in the GROUP BY clause or be used in an  aggregate function

postgreSQL 的 selectd的字段必须是group by的字段里的 或者使用了聚合函数。 mysql则没有这个要求,非聚合列会随机取值

错误例子

select name, age, count(*)
from user 
group by age, score

这时 select name 是错误的, 应为group by里没有这个字段,要么加上,要么变成select min(name)

2.10、事务异常问题

异常信息

# Cause: org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block

; uncategorized SQLException; SQL state [25P02]; error code [0]; ERROR: current transaction is aborted, commands ignored until end of transaction block; nested exception is org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block

Postgres数据库中,同一事务中如果某次数据库操作中出错的话,那这个事务以后的数据库操作都会出错。正常来说不会有这种情况,但是如果有人去捕获了事务异常后又去执行数据库操作就会导致这个问题。mysql貌似不会有这个问题

下面就是错误的代码例子: 靠异常去走逻辑。 解决办法就是不要靠数据库的异常去控制逻辑, 手动判断。

image.png

2.11 类型转换异常 (大头)

这个可以说是最坑的, 因为mysql是支持自动类型转换的。 在表字段类型和参数值之间如果类型不一样也会自动进行转换。 而postgreSQL是强数据类型,字段类型和参数值类型之间必须一样否则就会抛出异常。

这时候解决办法一般有两种

  1. 手动修改代码里的字段类型和传参类型保证 或者 postgreSQL表字段类型,反正保证双方一一对应
  2. 添加自动隐式转换函数,达到类似mysql的效果

布尔值和int类型类型转换错误

1、select查询时的转换异常信息

Cause: org.postgresql.util.PSQLException: ERROR: operator does not exist: smallint = boolean
SELECT   xx fom xx    WHERE   enable = ture

错误原因: enable字段是smallint类型查询却传了一个布尔值类型

2、update更新时的转换异常信息

Cause: org.postgresql.util.PSQLException: ERROR: column "name" is of type smallint but expression is of type boolean
update from xx set name = false  where  name = true

错误原因: 在update/insert赋值语句的时候,字段类型是smallint,但是传参却是布尔值类型

解决办法:

  • postgres数据库添加boolean <-> smallint 的自动转换逻辑
-- 创建函数1  smallint到boolean到转换函数
CREATE OR REPLACE FUNCTION "smallint_to_boolean"("i" int2)
  RETURNS "pg_catalog"."bool" AS $BODY$
 BEGIN
  RETURN (i::int2)::integer::bool;
 END;
 $BODY$
LANGUAGE plpgsql VOLATILE
-- 创建赋值转换1
create cast (SMALLINT as BOOLEAN) with function smallint_to_boolean as ASSIGNMENT;

-- 创建函数2    boolean到smallint到转换函数
CREATE OR REPLACE FUNCTION "boolean_to_smallint"("b" bool)
  RETURNS "pg_catalog"."int2" AS $BODY$
 BEGIN
  RETURN (b::boolean)::bool::int;
 END;
 $BODY$
LANGUAGE plpgsql VOLATILE
  
-- 创建隐式转换2
create cast (BOOLEAN as SMALLINT) with function boolean_to_smallint as implicit;

如果想重来可以删除掉上面创建的函数和转换逻辑

-- 删除函数
drop function smallint_to_boolean
-- 删除转换
drop  CAST (SMALLINT as BOOLEAN)

主要不要乱添加隐式转换函数,可能导致 Could not choose a best candidate operator 异常# operator is not unique 异常 就是在操作符比较的时候有多个转换逻辑不知道用哪个了,死循环了

3、PostgreSQL辅助脚本

3.1、批量修改timestamptz脚本

批量修改表字段类型 timestamptz 为 timestamp, 因为我们说过前者无法与LocalDateTime对应上

ps:

  • timestamp without time zone 就是 timestamp
  • timestamp with time zone 就是 timestamptz
DO $$
DECLARE
    rec RECORD;
BEGIN
    FOR rec IN SELECT table_name, column_name,data_type
               FROM information_schema.columns
               where table_schema = '要处理的模式名' 
               AND data_type = 'timestamp with time zone'
    LOOP
        EXECUTE 'ALTER TABLE ' || rec.table_name || ' ALTER COLUMN ' || rec.column_name || ' TYPE timestamp';
    END LOOP;
END $$;

3.2、批量设置时间默认值脚本

批量修改模式名下的所有字段类型为timestamp的并且字段名为 create_time 或者 update_time的字段的默认值为 CURRENT_TIMESTAMP

-- 注意 || 号拼接的后面的字符串前面要有一个空格
DO $$
DECLARE
    rec RECORD;
BEGIN
    FOR rec IN SELECT table_name, column_name,data_type
               FROM information_schema.columns
               where table_schema = '要处理的模式名' 
                 AND data_type = 'timestamp without time zone'
                 -- 修改的字段名
       			and column_name in ('create_time','update_time')
    LOOP
         EXECUTE 'ALTER TABLE ' || rec.table_name || ' ALTER COLUMN ' || rec.column_name || ' SET DEFAULT CURRENT_TIMESTAMP;';
    END LOOP;
END $$;

4、注意事项

1、将数据表从mysql迁移postgres 要注意字段类型要对应不要变更(*)

2、原先是 tinyint的就变samllint类型,不要是bool类型,有时代码字段类型可能对应不上

3、如果java字段是LocalDateTime原先mysql时间类型到postgres后不要用TIMESTAMPTZ类型

4、mysql一般用tinyint类型和java的Boolean字段对应并且在查询和更新时支持自动转换,但是postgres是强类型不支持,如果想无缝迁移postgres内部就新增自动转换的隐式函数,但是缺点是每次部署postgres后都要去执行一次脚本。 如果不想这样,只能修改代码的所有表对象的字段类型和传参类型保证与postgres数据库的字段类型对应,但是有些依赖的框架底层自己操作数据库可能就无法修改源码了,只能修改数据库表字段类型了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/533662.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu Desktop:创建桌面启动图标

Ubuntu Desktop&#xff1a;创建桌面启动图标 在Ubuntu Desktop上创建桌面启动图标是一个相对简单的过程&#xff0c;可以帮助用户快速访问他们最常用的应用程序。本文旨在指导你完成创建一个桌面启动图标的步骤&#xff0c;从而使你能够轻松启动你的应用程序。 为什么创建桌…

String类(1)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&a…

Java-接口—知识(基础)

承接上一节&#xff0c;我们讨论了抽象类和抽象方法。 那我们尝试在抽象的道路上走的更远一点&#xff0c;先来总结一下抽象&#xff1b; 抽象类&#xff1a;类中的属性规范了子类必须有什么样的特征&#xff0c;有什么状态。类中的方法规范了子类必须有什么行为&#xff0c;…

【Linux】进程间通信——匿名管道|命名管道

目录 什么是进程间通信 管道 匿名管道 什么是进程间通信 进程间通信&#xff0c;顾名思义就是两个进程互相通信。 可是进程是独立的&#xff0c;该如何通信呢&#xff1f; 类比你和你的朋友在网上聊天&#xff0c;你们两个人也是独立的&#xff0c;是如何通信的呢&#xf…

C#学习笔记10:winform上位机与西门子PLC网口通信_中篇_winform的窗口操作设计、日志的添加使用

今日继续我的C#winform上位机学习之路 这系列笔记的目标是尝试编写一个能够与西门子PLC进行以太网口通信的上位机软件。 文章提供完整代码解释、设计点解释、测试效果图、完整工程下载 本章主要学习&#xff1a;Winform多个窗体的一些操作 、无边框窗体的创建、Combox组件插…

Linux:文本编辑器 - vim

Linux&#xff1a;文本编辑器 - vim vim基本操作普通模式模式切换移动光标复制粘贴删除替换撤销 底行模式行号查找 vim基本操作 Vim(Vi Improved)是一款功能强大的文本编辑器&#xff0c;是Unix/Linux系统中广泛使用的编辑器之一。它源于上世纪70年代开发的Vi编辑器&#xff0…

Understanding Diffusion Models: A Unified Perspective翻译和公式补充解读

“Understanding Diffusion Models: A Unified Perspective”是一篇写的非常好的扩散模型DDPM数学原理解读文章&#xff0c;这里翻译了一遍&#xff0c;对于一些细节补充记录一下&#xff0c;方便对照原文更好的理解。 这篇文章作者是Calvin Luo&#xff0c;来自Google Resear…

激活黑科技

下载/如何使用它&#xff1f; 方法 1 - PowerShell&#xff08;推荐&#xff09;Method 1 - PowerShell &#xff08;Recommended&#xff09; 右键单击 Windows 开始菜单&#xff0c;然后选择“PowerShell”或“终端&#xff08;非 CMD&#xff09;”。复制粘贴以下代码&…

ChatGLM3-6B大语言模型离线执行

ChatGLM3-6B大语言模型离线执行 模型准备 一般而言&#xff0c;模型和模型参数可以通过如下三个模型源进行相应的下载&#xff1a; HuggingFace | ModelScope | WiseModel 本实例中&#xff0c;使用的是HuggingFace的源下载&#xff0c;相应的地址如下&#xff1a; HuggingFa…

在线视频下载工具lux(原annie)安装及使用教程

安装教程 下载ffmpeg&#xff0c;参考这篇文章&#xff1a;Python——Windows下载ffmpeg由于博主的系统为windows&#xff0c;所以选择不安装lux&#xff0c;直接下载.exe文件&#xff0c;进入lux的github网站后&#xff0c;选择右侧的Releases&#xff0c;下载下图的windows …

求职要做在线人才测评,如果找人代做会被发现吗?

理论上都有办法能避开&#xff0c;这种在线测评的一定都有这个弊端&#xff0c;什么录音&#xff0c;录屏&#xff0c;录像&#xff0c;只要你愿意琢磨下&#xff0c;就一定有办法避开&#xff0c;除非是现场有人看着你。 但是我的观点是没必要作弊&#xff0c;使这个巧&…

起诉状与反诉状有什么不同,二者在写法上有何区别?李秘书讲写作教教你!

起诉状与反诉状有什么不同&#xff0c;二者在写法上有何区别&#xff1f;李秘书讲写作教教你&#xff01; 起诉状和反诉状在民事诉讼中各自扮演着不同的角色&#xff0c;因此它们在内容和写法上有一些不同。#李秘书讲写作#这节就讲这个话题。 起诉状是原告向人民法院提起诉讼…

从 0 搭建公司Jenkins服务 Centos7

从 0 搭建公司Jenkins服务 Centos7 安装 (运维人员) 安装环境 配置DNS安装JDK17安装Jenkins安装Docker安装GIT安装Ansible启动Jenkins安装插件配置凭据配置共享库配置 (开发经理)使用 (开发、测试人员) 安装 (运维人员) 安装环境 配置DNS 新安装系统的服务器无法解析域名&a…

为什么选择成为一名程序员

选择成为一名程序员&#xff0c;其背后的原因往往既包含兴趣&#xff0c;也包含职业发展的考量。对于我来说&#xff0c;这两者并不是孤立的&#xff0c;而是相互交织&#xff0c;共同推动步入这一行业。 兴趣是选择成为程序员的重要驱动力。编程本身就是一种创造和解决问题的…

Calibre:如何在runset中设置LVS box

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 相关文章链接: Calibre:LVS 流程 在“Calibre: LVS 流程”文章里介绍了再rule file和图形界面如何设置lvs box,这里再分享一种lvs box设置方法: 通常我们会保存runset文件

CSS 基础:border 的 3 个基础属性和简写方法

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合集 264篇…

SpringCloudAlibaba-整合nacos(二)

目录地址&#xff1a; SpringCloudAlibaba整合-CSDN博客 一、nacos服务部分 1.下载nacos&#xff0c;并执行数据库脚本&#xff1a;nacos-mysql.sql 2.修改配置文件&#xff0c;配置mysql 3.启动nacos ./startup.sh -m standalone 4.访问&#xff1a;http://127.0.0.1:884…

Golang快速入门教程(一)

目录 一、环境搭建 1.windows安装 2.linux安装 3.开发工具 二、变量定义与输入输出 1.变量定义 2.全局变量与局部变量 3.定义多个变量 4.常量定义 5.命名规范 6.输出 7.输入 三、基本数据类型 1.整数型 2.浮点型 3.字符型 4.字符串类型 转义字符 多行字符…

基于51单片机的多床位病房呼叫器Proteus仿真

地址&#xff1a;https://pan.baidu.com/s/1WLTBD9WaWncZS_uRSjM-iA 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectroni…

企业微信认证后可以修改主体吗?

企业微信变更主体有什么作用&#xff1f;如果原有的公司注销了&#xff0c;或者要更换一家公司主体来运营企业微信&#xff0c;那么就可以进行变更主体&#xff0c;变更主体后才可以保留原来企业微信上的所有用户&#xff0c;否则就只能重新申请重新积累用户了。企业微信变更主…