首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >项目从 MySQL 切换 PostgreSQL,踩了太多的坑!!!

项目从 MySQL 切换 PostgreSQL,踩了太多的坑!!!

作者头像
良月柒
发布于 2024-07-17 07:39:37
发布于 2024-07-17 07:39:37
1.5K02
代码可运行
举报
运行总次数:2
代码可运行

0、前言

原项目框架 SpringBoot + MybatisPlus + Mysql

1、切换流程

1.1、项目引入postgresql驱动包

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
</dependency>
1.2、修改jdbc连接信息

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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不匹配

异常信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PSQLException: Cannot convert the column of type TIMESTAMPTZ to requested type java.time.LocalDateTime.

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

2.2、参数值不能用双引号

错误例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 WHERE name = "jay"   ===>    WHERE name = 'jay'

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

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

错误例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 WHERE `name` = 'jay'  ==>    WHERE name = 'jay'

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

2.4、json字段处理语法不同
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 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函数替换

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- mysql语法: 
select convert(name, DECIMAL(20, 2))

-- postgreSQL语法:
select CAST(name as DECIMAL(20, 2))
2.6、force index 语法不存在
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- mysql语法
select xx FROM user force index(idx_audit_time)

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

2.7、ifnull 函数不存在

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

异常信息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cause: org.postgresql.util.PSQLException: ERROR: function ifnull(numeric, numeric) does not exist
2.8、date_format 函数不存在

异常信息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Cause: org.postgresql.util.PSQLException: ERROR: function date_format(timestamp without time zone, unknown) does not exist

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

替换例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// %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语法问题

异常信息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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则没有这个要求,非聚合列会随机取值

错误例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select name, age, count(*)
from user 
group by age, score

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

2.10、事务异常问题

异常信息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 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貌似不会有这个问题

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

2.11 类型转换异常 (大头)

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

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

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

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

1、select查询时的转换异常信息
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Cause: org.postgresql.util.PSQLException: ERROR: operator does not exist: smallint = boolean
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT   xx fom xx    WHERE   enable = ture

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

2、update更新时的转换异常信息
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Cause: org.postgresql.util.PSQLException: ERROR: column "name" is of type smallint but expression is of type boolean
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
update from xx set name = false  where  name = true

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

解决办法:

postgres数据库添加boolean <-> smallint 的自动转换逻辑

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 创建函数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;

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 删除函数
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脚本

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

ps:

  • timestamp without time zone 就是 timestamp
  • timestamp with time zone 就是 timestamptz

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 注意 || 号拼接的后面的字符串前面要有一个空格
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数据库的字段类型对应,但是有些依赖的框架底层自己操作数据库可能就无法修改源码了,只能修改数据库表字段类型了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的成长之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
解决 MyBatis-Plus + PostgreSQL 中的 org.postgresql.util.PSQLException 异常
在使用 MyBatis-Plus 和 PostgreSQL 数据库时,有时候会遇到 org.postgresql.util.PSQLException 异常,错误信息为 “conversion to class java.time.OffsetDateTime from int4 not supported”。这个异常通常是由于数据库字段类型与实体类属性类型不匹配引起的。本文将介绍如何通过在实体类中添加 @TableField 注解并指定参数来解决这个问题。
猫头虎
2024/04/08
2.1K0
解决 MyBatis-Plus + PostgreSQL 中的 org.postgresql.util.PSQLException 异常
Oracle/Mysql迁移到Postgresql事务回滚行为差异及改造方法
Mysql或Oracle迁移到Postgresql系产品后,经常会发生事务回滚导致的问题,具体问题一般都是类似于:
mingjie
2022/05/12
1.2K0
Oracle/Mysql迁移到Postgresql事务回滚行为差异及改造方法
纯分享:将MySql的建表DDL转为PostgreSql的DDL.md
现在信创是搞得如火如荼,在这个浪潮下,数据库也是从之前熟悉的Mysql换到了某国产数据库。
低级知识传播者
2023/09/12
1.6K0
纯分享:将MySql的建表DDL转为PostgreSql的DDL.md
云数据库MySQL导入云数据仓库PostgreSQL最佳实践
本文描述问题及解决方法基于 腾讯云 云数据仓库 PostgreSQL(CDWPG)。
岳涛
2021/03/20
5.2K0
云数据库MySQL导入云数据仓库PostgreSQL最佳实践
想熟悉PostgreSQL?这篇就够了
PostgreSQL是自由的对象-关系型数据库服务器,在灵活的BSD风格许可证下发行。它在其他开放源代码数据库系统和专有系统之外,为用户又提供了一种选择。 我们还是建议您使用云数据库进行搭建,省去数据迁移等麻烦操作,数据库详见:https://cloud.tencent.com/product/cdb-overview
angel_郁
2018/07/20
3.6K0
POSTGRESQL 带时区的日期的技术与狠活
最近最热门的歇后语就是,“技术与狠活”, 数据库中的POSTGRESQL 的日期数据有什么技术与狠活,咱们今天来说说。
AustinDatabases
2022/12/12
3.4K0
POSTGRESQL   带时区的日期的技术与狠活
进阶数据库系列(三):PostgreSQL 常用管理命令
psql是PostgreSQL的一个命令行交互式客户端工具,它具有非常丰富的功能,类似于Oracle的命令行工具sqlplus。
民工哥
2023/08/22
1.3K0
进阶数据库系列(三):PostgreSQL 常用管理命令
PostgreSQL JSONB 使用入门
Photo by Tobias Fischer[9] on Unsplash[10]
goodspeed
2020/12/22
8.6K0
PostgreSQL JSONB 使用入门
.net访问PostgreSQL数据库发生“找不到函数名”的问题追踪
    PostgreSQL是一个使用广泛的免费开源的数据库,与MySQL比较,它更适合复杂的企业计算任务,而MySQL在互联网领域应用更为广泛,究其原因,可能是PostgreSQL拥有支持最多的数据类型,甚至包括数组类型,IP地址类型等,可以使用C,SQL,PL/Pgsql,Phython等多种方式编写强大的自定义函数,因此特别适合处理复杂的计算问题。如果想要将SqlServer数据库迁移到其它类型的数据库,PostgreSQL是比较好的选择。     尽管PostgreSQL使用比较广泛,但在国内相关资
用户1177503
2018/02/27
1.9K0
PostgreSQL连接Oracle数据库 原
1.下载最新源码: https://github.com/laurenz/oracle_fdw/releases https://github.com/laurenz/oracle_fdw/archive/ORACLE_FDW_1_5_0.tar.gz 2.设置环境变量: ORACLE_HOME=/opt/oracle/product/11.2.0/db export ORACLE_HOME PATH=$PATH:$ORACLE_HOME/bin export PATH PGHOME=/opt/di
用户2836074
2018/08/15
2.1K0
PostgreSQL12安装及配置
PostgreSQL安装成功之后,会默认创建一个名为postgres的Linux用户,
码客说
2023/07/24
1.1K0
拒绝停服,随时回退:MS SQL 到 PostgreSQL 的无缝数据库双向迁移方案
本项目旨在将关键业务应用从 MS SQL 数据库平滑迁移至 PostgreSQL。
Tapdata
2025/06/09
1840
拒绝停服,随时回退:MS SQL 到 PostgreSQL 的无缝数据库双向迁移方案
进阶数据库系列(十一):PostgreSQL 存储过程
工作中可能会存在业务比较复杂,重复性工作比较多,需要批量处理数据的情况,此时使用存储过程会方便很多,存储过程的执行效率也会快很多,能帮助我们节省很多代码和时间。
民工哥
2023/08/22
5.6K0
进阶数据库系列(十一):PostgreSQL 存储过程
PostgreSQL与MySQL基本操作语法区别分析
在当今的数据库管理系统中,PostgreSQL和MySQL都是极为流行的开源数据库。尽管它们有许多相似之处,但在实际操作和语法上仍存在不少差异。
炒香菇的书呆子
2024/12/06
6740
MyBatis 所有的 jdbcType类型
当我们使用java.util.Date作为实体的日期类型时(JAVA没有DateTime这个类,Date类能够同时表示日期和时间),java.util.Date实际上是能够表示MySQL的三种字段类型:
全栈程序员站长
2022/11/01
2K0
MyBatis 所有的 jdbcType类型
MySQL基础SQL编程学习2
描述:主要学习数据库的DDL数据库定义语言,比如CREATE , DROP, ALTER 等等:
全栈工程师修炼指南
2022/09/29
8K0
MySQL基础SQL编程学习2
PostgreSQL - invalid input syntax for type timestamp with time zone
由于coalesce()要求输入参数是null或字符串,而now()返回的结果是带有时区的时间戳,所以就会报错;需要把时间戳转换成字符串才可以,如下所示:
雨临Lewis
2022/01/11
6480
避免WHERE子句中使用函数的索引优化策略
在日常数据库性能调优中,开发者常遇到这样的场景:明明已经建立了索引,但查询性能却未达预期。通过EXPLAIN命令分析执行计划时,会看到"Using where; Using filesort"的提示,这意味着数据库引擎未能有效利用索引。这种现象往往与WHERE子句中函数的使用密切相关。
Jimaks
2025/06/05
1580
避免WHERE子句中使用函数的索引优化策略
MySQL建表语句转PostgreSQL建表语句全纪录
个人习惯用MySQL workbench EER数据建模,然后生成SQL语句到数据库中执行,这样表之间的关系比较直观。
用户1516716
2019/07/10
3.5K0
ruoyi mysql切换 pgsql
pgsql information_schema.tables 不会存储表的创建时间或更新时间 所以关于根据时间查询的条件需要删除
IT小马哥
2025/03/26
2190
推荐阅读
相关推荐
解决 MyBatis-Plus + PostgreSQL 中的 org.postgresql.util.PSQLException 异常
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档