Skip to content

数据库支持

sz-admin 同时支持 MySQL 8.0+PostgreSQL 9.4+,建议 16+。数据库切换由两部分共同决定:

  1. application.yml 中的 DB_TYPE,决定运行时加载哪个数据库连接配置。
  2. Maven profile,决定构建产物中引入哪个数据库驱动模块。

IMPORTANT

DB_TYPE 与 Maven profile 必须保持一致。例如 DB_TYPE=postgresql 时,构建或 IDEA Maven Profile 也应选择 postgresql

支持版本

数据库最低版本说明
MySQL8.0.17需要 JSON_OVERLAPS 函数以及多值索引支持。
PostgreSQL9.4,建议 16+需要数组操作符 && 以及 GIN 索引支持。

切换方式

第一步:修改 DB_TYPE

sz-service/sz-service-admin/src/main/resources/application.yml 中修改 DB_TYPE

yaml
DB_TYPE: mysql       # 可选:mysql | postgresql

项目启动时会根据 DB_TYPE 自动加载对应数据库配置文件:

text
config/${spring.profiles.active}/${DB_TYPE}.yml

例如:

当前 profileDB_TYPE加载配置
localmysqlconfig/local/mysql.yml
localpostgresqlconfig/local/postgresql.yml
prodmysqlconfig/prod/mysql.yml
prodpostgresqlconfig/prod/postgresql.yml

第二步:选择 Maven profile

sz-service-admin 模块通过 Maven profile 控制数据库驱动模块:

Profile作用是否默认
mysql引入 sz-common-db-mysql
postgresql引入 sz-common-db-postgresql

默认不指定 profile 时使用 MySQL:

bash
mvn -pl sz-service/sz-service-admin -am compile -DskipTests

显式构建 MySQL 版本:

bash
mvn -pl sz-service/sz-service-admin -am compile -DskipTests -Pmysql

构建 PostgreSQL 版本:

bash
mvn -pl sz-service/sz-service-admin -am compile -DskipTests -Ppostgresql

WARNING

不要同时启用 mysqlpostgresql。数据库驱动模块同一时间只应启用一个,否则可能出现驱动、类型处理器或方言注册冲突。

IDEA 本地开发

本地开发推荐也使用 Maven profile,不再通过注释或放开 pom.xml 依赖来切换数据库。

使用 MySQL

  1. application.yml 保持 DB_TYPE: mysql
  2. IDEA Maven Profiles 不需要勾选任何数据库 profile,或只勾选 mysql
  3. 刷新 Maven 后启动 AdminApplication

使用 PostgreSQL

  1. 修改 application.yml
yaml
DB_TYPE: postgresql
  1. IDEA Maven Profiles 勾选 postgresql
  2. 不要同时勾选 mysql
  3. 刷新 Maven 后启动 AdminApplication

生产部署

生产环境推荐显式指定数据库 profile,使构建产物和目标数据库清晰匹配。

MySQL:

bash
mvn -pl sz-service/sz-service-admin -am package -DskipTests -Pmysql

PostgreSQL:

bash
mvn -pl sz-service/sz-service-admin -am package -DskipTests -Ppostgresql

部署时还需要确认运行环境中的配置文件一致:

yaml
# MySQL 部署
DB_TYPE: mysql

# PostgreSQL 部署
DB_TYPE: postgresql

对应连接信息分别维护在:

text
config/prod/mysql.yml
config/prod/postgresql.yml

为什么使用 profile 切换驱动

数据库驱动和数据库方言属于构建期依赖。使用 Maven profile 的好处是:

  • 本地和生产使用同一套切换方式,降低理解成本。
  • 不需要用户修改 pom.xml 正文,避免产生本地脏改动。
  • 构建产物更明确,MySQL 版本只带 MySQL 模块,PostgreSQL 版本只带 PostgreSQL 模块。
  • DB_TYPE 形成清晰分工:profile 管依赖,DB_TYPE 管运行时配置。

配置文件说明

MySQL 配置

config/{profile}/mysql.yml

yaml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/sz_admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
    username: your_username
    password: your_password
    hikari:
      pool-name: HikariCP_MYSQL
      minimum-idle: 5
      idle-timeout: 600000
      maximum-pool-size: 10
      auto-commit: true
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1
  liquibase:
    change-log: classpath:db/changelog/changelog-master.xml
    enabled: true
    database-change-log-lock-table: databasechangeloglock
    database-change-log-table: databasechangelog

PostgreSQL 配置

config/{profile}/postgresql.yml

yaml
spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://127.0.0.1:5432/sz_admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    username: your_username
    password: your_password
    hikari:
      pool-name: HikariCP_PG
      minimum-idle: 5
      idle-timeout: 600000
      maximum-pool-size: 10
      auto-commit: true
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1
  liquibase:
    change-log: classpath:db/changelog/changelog-master.xml
    enabled: true
    database-change-log-lock-table: databasechangeloglock
    database-change-log-table: databasechangelog

类型差异对照

两种数据库在字段类型上存在差异。Liquibase 脚本通过 <property> 变量统一处理通用差异,业务代码通常无感知。

用途MySQLPostgreSQLLiquibase 变量
布尔标识,T/FCHAR(1)VARCHAR(1)${bool.type}
时间字段DATETIMETIMESTAMP${datetime.type}
大文本MEDIUMTEXTTEXT按目标库分别处理
部门范围字段JSONBIGINT[]按目标库分别处理
标识符包裹反引号双引号方言层自动处理

dept_scope 字段说明

dept_scope 是数据权限的核心字段,用于记录数据创建者所属部门 ID 集合。由于 MySQL 和 PostgreSQL 的数组存储机制不同,该字段在两种数据库中的类型和查询方式也不同。

方面MySQLPostgreSQL
字段类型JSON,如 [16, 17]BIGINT[],如 {16,17}
部门过滤 SQLJSON_OVERLAPS(dept_scope, '[16,17]')dept_scope && ARRAY[16,17]::BIGINT[]
推荐索引多值索引USING GIN (dept_scope)
Java 映射LongListTypeHandlerLongListTypeHandler

业务实体中统一使用:

java
@Column(typeHandler = LongListTypeHandler.class)
private List<Long> deptScope;

数据权限方言自动激活

框架根据 JDBC 驱动名自动注册对应的数据权限方言:

text
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  -> 激活 MysqlPermissionDialect

spring.datasource.driver-class-name=org.postgresql.Driver
  -> 激活 PostgresqlPermissionDialect

两个方言都继承自 AbstractPermissionDialect,统一数据权限流程,只在 SQL 片段上做差异化处理。

抽象方法MySQL 实现PostgreSQL 实现
buildUserUnitDeptSqlEXISTS 子查询,反引号标识符EXISTS 子查询,双引号标识符
buildDeptUnitSqlJSON_OVERLAPS(dept_scope, '[...]')dept_scope && ARRAY[...]::BIGINT[]
buildAllowAdminViewSqlEXISTS + 反引号EXISTS + 双引号
buildExcludeAdminSqlNOT EXISTS + 反引号NOT EXISTS + 双引号
buildUserListSql反引号包裹字段并拼接 IN双引号包裹字段并拼接 IN

新增业务表时的注意事项

新增业务表时,先确认该模块是否需要同时兼容 MySQL 和 PostgreSQL。

  • 如果项目已经固定使用 MySQL,只需要按 MySQL 类型编写脚本,例如 dept_scope=JSON,大文本使用 MEDIUMTEXT
  • 如果项目已经固定使用 PostgreSQL,只需要按 PostgreSQL 类型编写脚本,例如 dept_scope=BIGINT[],大文本使用 TEXT
  • 如果模块需要同时兼容 MySQL 和 PostgreSQL,类型差异无法通过变量统一时,应分别编写两个 changeSet,并通过 dbms 属性区分。

示例:

xml
<!-- MySQL 版本:dept_scope 使用 JSON,大文本使用 MEDIUMTEXT -->
<changeSet id="demo-struct-your-table-202605100838" author="sz" dbms="mysql">
    <preConditions onFail="MARK_RAN">
        <not><tableExists tableName="your_table"/></not>
    </preConditions>
    <createTable tableName="your_table">
        <column name="del_flag" type="${bool.type}" defaultValue="F"/>
        <column name="create_time" type="${datetime.type}"/>
        <column name="dept_scope" type="JSON" remarks="部门范围"/>
        <column name="content" type="MEDIUMTEXT" remarks="内容"/>
    </createTable>
</changeSet>

<!-- PostgreSQL 版本:dept_scope 使用 BIGINT[],大文本使用 TEXT -->
<changeSet id="demo-struct-your-table-pg-202605100838" author="sz" dbms="postgresql">
    <preConditions onFail="MARK_RAN">
        <not><tableExists tableName="your_table"/></not>
    </preConditions>
    <createTable tableName="your_table">
        <column name="del_flag" type="${bool.type}" defaultValue="F"/>
        <column name="create_time" type="${datetime.type}"/>
        <column name="dept_scope" type="BIGINT[]" remarks="部门范围"/>
        <column name="content" type="TEXT" remarks="内容"/>
    </createTable>
</changeSet>

NOTE

bool.typedatetime.type 这类通用差异可以通过 <property> 变量统一处理,单个 changeSet 即可兼容两种数据库。dept_scope、大文本等无法统一的差异,才需要按数据库拆分 changeSet

更多 Liquibase 编写规则请参考 Liquibase 使用说明