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

news2024/9/22 1:41:29

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

切换流程

项目引入postgresql驱动包

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

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

修改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语法踩坑

踩坑记录

TIMESTAMPTZ类型与LocalDateTime不匹配

  • 异常信息:

  • PSQLException: Cannot convert the column of type TIMESTAMPTZ to requested type java.time.LocalDateTime.
    
  • 如果postgres表的字段类型是TIMESTAMPTZ ,但是java对象的字段类型是LocalDateTime, 这时会无法转换映射上。 postgres表字段类型应该用timestamp 或者 java字段类型用Date

参数值不能用双引号

  • 错误例子:

  •  WHERE name = "jay"   ===>    WHERE name = 'jay'
    
  • 这里参数值"jay" 应该改成单引号 ‘jay’

字段不能用``包起来

  • 错误例子

  •  WHERE `name` = 'jay'  ==>    WHERE name = 'jay'
    
  • 这里的字段名name不能用``选取

json字段处理语法不同

  • -- mysql语法: 
    WHERE keywords_json->'$.name' LIKE CONCAT('%', ?, '%')
    
    -- postgreSQL语法:
    WHERE keywords_json ->>'name' LIKE CONCAT('%', ?, '%')
    
  • 获取json字段子属性的值mysql是用 -> '$.xxx'的语法去选取的, 而 postgreSQL 得用 ->>'xx' 语法选择属性

convert函数不存在

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

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

force index 语法不存在

  • -- mysql语法
    select xx FROM user force index(idx_audit_time)
    
  • mysql可以使用force index强制走索引, postgres没有,建议去掉

ifnull 函数不存在

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

  • 异常信息

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

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')
    

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)

事务异常问题

  • 异常信息

  • # 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貌似不会有这个问题

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

  • 在这里插入图片描述

类型转换异常 (大头)

  • 这个可以说是最坑的, 因为mysql是支持自动类型转换的。 在表字段类型和参数值之间如果类型不一样也会自动进行转换。 而postgreSQL是强数据类型,字段类型和参数值类型之间必须一样否则就会抛出异常。
  • 这时候解决办法一般有两种
    1. 手动修改代码里的字段类型和传参类型保证 或者 postgreSQL表字段类型,反正保证双方一一对应
    2. 添加自动隐式转换函数,达到类似mysql的效果
布尔值和int类型类型转换错误
  • select查询时的转换异常信息

  • Cause: org.postgresql.util.PSQLException: ERROR: operator does not exist: smallint = boolean
    
  • SELECT   xx fom xx    WHERE   enable = ture
    
  • 错误原因: enable字段是smallint类型查询却传了一个布尔值类型

  • 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 异常 就是在操作符比较的时候有多个转换逻辑不知道用哪个了,死循环了

PostgreSQL辅助脚本

批量修改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 $$;
    

批量设置时间默认值脚本

  • 批量修改模式名下的所有字段类型为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 $$;
    

注意事项

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

美容美发行业收银系统源码、美业SaaS系统源码

美业SaaS系统 连锁多门店美业收银系统源码 多门店管理 / 会员管理 / 预约管理 / 排班管理 / 商品管理 / 促销活动 PC管理后台、手机APP、iPad APP、微信小程序

leetcode-设计LRU缓存结构-112

题目要求 思路 双链表哈希表 代码实现 struct Node{int key, val;Node* next;Node* pre;Node(int _key, int _val): key(_key), val(_val), next(nullptr), pre(nullptr){} };class Solution { public: unordered_map<int, Node*> hash; Node* head; Node* tail; int …

【520】《架构演进之路》选题通过

选题通过 碰巧是520这一天&#xff0c;清华社编辑今天下午回复书籍&#xff08;架构演进之路&#xff09;选题通过&#xff0c;一个有纪念意义的日子。 架演共创者联盟成立 目前参与者有8位成员&#xff0c;涉及前后端、大数据、app、数字孪生&#xff0c;可比一个真实项目团队…

k8s笔记 | Prometheus安装

kube-prometheus 基于github安装 选择对应的版本 这里选择 https://github.com/prometheus-operator/kube-prometheus/tree/release-0.11 下载修改为国内镜像源 image: quay.io 改为 quay.mirrors.ustc.edu.cn image: k8s.gcr.io 改为 lank8s.cn 创建 prometheus-ingres…

C++开源库glog使用封装--自定义日志输出格式,设置日志保留时间

glog下载和编译 glog开源地址 https://github.com/google/glog glog静态库编译 cd /home/wangz/3rdParty/hldglog/glogmkdir out mkdir build && cd buildcmake .. -DCMAKE_INSTALL_PREFIX../out -DCMAKE_BUILD_TYPERelease -DBUILD_SHARED_LIBSOFF本文选择的glo…

如何利用Ubuntu服务器运行深度学习项目?

一、整体思路 先配置好服务器端的软件环境&#xff08;工程源码&#xff0c;miniconda&#xff0c;cuda&#xff0c;显卡驱动等&#xff09;&#xff0c;然后用自己电脑的pycharm远程连接服务器运行代码。一句话总结&#xff1a;借用服务器资源运行代码&#xff0c;本地pycharm…

第三节 mybatis-spring-boot-starter 案例分析

tips&#xff1a;可以利用 docker-desktop 快速搭建 MySQL、Redis 等中间件Docker 安装 Redis | 菜鸟教程 上一章&#xff0c;我们完成了一个自定义 Starter &#xff1b; 这一章&#xff0c;我们来看看 Mybatis 是如何使用 Starter&#xff0c;通过学习 mybatis-spring-boot-s…

深度学习模型keras第二十二讲:使用KerasCV进行语义分割

1、语义分割的概念 1.1语义分割的定义 语义分割是一种计算机视觉领域的图像分割技术&#xff0c;其目标是将一张图像中的每个像素分配给预定义的类别。 在图像领域&#xff0c;语义指的是对图像意思的理解。语义分割就是按照“语义”给图像上目标类别中的每一点打上一个标签…

C#屏蔽基类成员

可以用与积累成员名称相同的成员来屏蔽 要让编译器知道你在故意屏蔽继承的成员&#xff0c;可以用new修饰符。否则程序可以成功编译&#xff0c;但是编译器会警告你隐藏了一个继承的成员 using System;class someClass {public string F1 "Someclass F1";public v…

第四节 Starter 加载时机和源码理解

tips&#xff1a;每个 springBoot 的版本不同&#xff0c;代码的实现存会存在不同。 上一章&#xff0c;我们聊到 mybatis-spring-boot-starter&#xff1b; 简单分析了它的结构。 这一章我们将着重分析 Starter 的加载机制&#xff0c;并结合源码进行分析理解。 一、加载实际…

若依解决使用https上传文件返回http路径问题

若依通过HTTPS请求进行文件上传时却返回HTTP的文件链接地址&#xff0c;主要原因是使用了 request.getRequestURL 获取链接地址。 解决办法&#xff1a; 在nginx配置文件location处加上&#xff1a;proxy_set_header X-Forwarded-Scheme $scheme; 然后代码通过request.getHea…

在程序运行中动态改变变量

场景 出于测试目的&#xff0c;需要在程序运行中去改变程序中的参数 思路 动态的去读第三方存储&#xff0c;比如数据库&#xff0c;缓存&#xff0c;甚至是文件 我的实现 这里使用了gflags&#xff0c;通过flaks实现一个api提供flag的修改 Gflags 是 Google 内部使用的命…

人工智能(Educoder)-- 机器学习 -- 神经网络(初级)

第一关 注&#xff1a; 神经网络的起源和应用 起源&#xff1a;神经网络最早由心理学家和神经学家开创&#xff0c;目的是模拟生物神经系统对真实世界物体的交互反应。应用&#xff1a;现代神经网络用于分类&#xff08;如图像识别、文本分类&#xff09;和数值预测&#xff08…

docker启动clickhouse

docker启动clickhouse 创建clickhouse目录拉取镜像启动临时容器, 生成配置文件正式启动 clickhouse越来越流行&#xff0c;本地想安装个测试环境 创建clickhouse目录 后续作为挂载卷使用 mkdir -p /home/gugu/ckdata/data mkdir -p /home/gugu/ckdata/conf mkdir -p /home/gu…

信捷XD系列PLC通讯失败程序无法下载如何设置

如题:最近在使用信捷PLC&#xff0c;有时会出现通讯不上的问题&#xff0c;下面将通讯配置步骤及注意事项分享。 一、确保PLC通电&#xff0c;电脑使用USB通讯线和PLC连接。 二、打开程序&#xff0c;点击串口标识&#xff0c;会弹出通信配置窗口。 三、双击USB通讯这条进行设…

【Python小案例】Python+mysql+PyQt5健康体检报告查询

下载安装Python3.7.8 python官网&#xff1a;https://www.python.org/ PyQt5配置 安装PyQt5 pip install PyQt5pip install qt5_toolspytcharm创建项目 配置外部工具 QTDesigner的Arguments语句不用填 QTDesigner的Working directory语句:$ProjectFileDir$ Pyuic的Argume…

【GDAL】GDAL库学习(C#版本)

1.GDAL 2.VS2022配置GDAL环境&#xff08;C#&#xff09; VS2022工具–NuGet包管理器–管理解决方案的NuGet程序包&#xff0c;直接安装GDAL包。 并且直接用应用到当前的控制台程序中。 找一张tiff格式的图片&#xff0c;或者用格式转换网站&#xff1a;https://www.zamzar.c…

go升级后 编译的exe在win7上无法正常运行

D:/Go/src/runtime/sys_windows_amd64.s:65 x75 fpx22fca sp-0x22fc8日 升级到go 1.21后报一堆错误&#xff0c;要死了啊 原来是go 1.21不支持win7了&#xff0c;必须把go退回到1.20版本 谷歌发布编程语言 Go 1.21 版本&#xff1a;取消支持微软 Win7/8 及苹果 macOS 10.13/10…

爬虫技术升级:如何结合DrissionPage和Auth代理插件实现数据采集

背景/引言 在大数据时代&#xff0c;网络爬虫技术已经成为数据收集的重要手段之一。爬虫技术可以自动化地从互联网上收集数据&#xff0c;节省大量人力和时间成本。然而&#xff0c;当使用需要身份验证的代理服务器时&#xff0c;许多现有的爬虫框架并不直接支持代理认证。这就…

[力扣]——231.2的幂

题目描述&#xff1a; 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2x &#xff0c;则认为 n 是 2 的幂次方。 bool isPowerOfTwo(int n){ if(n0)retur…