当前位置: 首页 > news >正文

SpringBoot整合Liquibase

1、是什么?

Liquibase官网

Liquibase是一个开源的数据库管理工具,可以帮助开发人员管理和跟踪数据库变更。它可以与各种关系型数据库和NoSQL数据库一起使用,并提供多种数据库任务自动化功能,例如数据库迁移、版本控制和监控。Liquibase还提供了一个Web界面,可以方便地管理和跟踪数据库变更。它支持Java、Python、Ruby等多种语言,可以轻松地集成到现有的开发环境中。

2、能干嘛?

Liquibase主要功能包括:

  • 数据库迁移:可以方便地将数据库从一个版本迁移到另一个版本。
  • 版本控制:可以跟踪数据库变更的历史记录,并可以根据需要回滚到以前的版本。
  • 监控:可以监控数据库变更,并在发生变更时收到通知。
  • 自动化:可以自动化数据库任务,例如在应用程序部署之前检查数据库完整性。

Liquibase可以帮助开发人员更加高效地管理数据库,并减少由于数据库变更而导致的错误。

Liquibase的优点:

  • 配置文件支持SQL、XML、JSON 或者 YAML
  • 版本控制按序执行
  • 可以用上下文控制sql在何时何地如何执行
  • 支持schmea的变更
  • 根据配置文件自动生成sql语句用于预览
  • 可重复执行迁移
  • 可插件拓展
  • 可回滚
  • 可兼容14中主流数据库如oracle,mysql,pg等,支持平滑迁移
  • 支持schema方式的多租户(multi-tenant)

3、怎么玩?

这里主要使用SpringBoot整合Liquibase实现对数据库进行版本管理

(1) 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.ly</groupId><artifactId>springboot-liquibase</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.0</version></parent><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><dependency><groupId>org.liquibase</groupId><artifactId>liquibase-core</artifactId><version>4.23.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.18</version></dependency></dependencies>
</project>
(2) 配置数据源
package com.ly.config;import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;import javax.sql.DataSource;import static com.ly.config.DataSourcesConfig.SPRING_DATASOURCE;/*** @author ly (个人博客:https://www.cnblogs.com/ybbit)* @date 2023-07-22  16:28* @tags 喜欢就去努力的争取*/
@ConfigurationProperties(prefix = SPRING_DATASOURCE)
@SpringBootConfiguration
@Data
public class DataSourcesConfig {public static final String SPRING_DATASOURCE = "spring.datasource";private String driverClassName;private String url;private String username;private String password;/*** 数据源配置** @return*/@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driverClassName);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(username);return dataSource;}
}
(3) 配置Liquibase
package com.ly.config;import liquibase.integration.spring.SpringLiquibase;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;import javax.sql.DataSource;/*** @author ly (个人博客:https://www.cnblogs.com/ybbit)* @date 2023-07-22  14:53* @tags 喜欢就去努力的争取*/
@ConditionalOnProperty(value = "spring.profiles.active", havingValue = "dev")
@SpringBootConfiguration
public class LiquibaseConfig {public static final String CHANGE_LOG_PATH = "classpath:/liquibase/db.changelog-master.xml";@Beanpublic SpringLiquibase liquibase(DataSource dataSource) {SpringLiquibase liquibase = new SpringLiquibase();liquibase.setChangeLog(CHANGE_LOG_PATH);liquibase.setDataSource(dataSource);liquibase.setShouldRun(true);return liquibase;}}
(4) 创建db.changelog-master.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLogxmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangeloghttp://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"><!--1:includeAll 标签可以把一个文件夹下的所有 changelog 都加载进来。如果单个加载可以用 include。2:includeAll 标签里有两个属性:path 和 relativeToChangelogFile。2.1:path (在 include 标签里是 file):指定要加载的文件或文件夹位置2.2:relativeToChangelogFile :文件位置的路径是否相对于 root changelog 是相对路径,默认 false,即相对于 classpath 是相对路径。--><!--    <includeAll path="change/" relativeToChangelogFile="true"/>--><!--加入一张test_create_table表--><include file="classpath:liquibase/change/changelog_v1.0.xml"></include><!--给test_create_table表加一个email字段--><include file="classpath:liquibase/change/changelog_v2.0.xml"></include><!--修改test_create_table表加email字段--><include file="classpath:liquibase/change/changelog_v3.0.xml"></include><!--向test_create_table表加一条数据--><include file="classpath:liquibase/change/changelog_v4.0.xml"></include></databaseChangeLog>
(5) 创建每一项的变更文件(推荐把各个模块的变更都分门别类的整理好)

changelog_v1.0.xml

<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"><!--changeSet:每一个changeSet对应一个数据库相关的操作author:修改人id:唯一--><!--加入一张表【test_create_table】--><changeSet author="ly" id="2023072201-1"><createTable remarks="用户表" tableName="test_create_table"><column autoIncrement="true" name="id" type="INT" remarks="主键"><constraints nullable="false" primaryKey="true" unique="true"/></column><column name="username" remarks="用户名" type="VARCHAR(32)"><constraints unique="true" nullable="false"/></column><column name="password" remarks="密码" type="VARCHAR(100)"><constraints unique="false" nullable="false"/></column></createTable></changeSet>
</databaseChangeLog>

changelog_v2.0.xml

<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"><changeSet author="ly" id="2023072202-1"><!--加入一个email字段--><addColumn tableName="test_create_table"><column name="email" type="VARCHAR(32)" remarks="邮箱"><constraints nullable="true"/></column></addColumn></changeSet>
</databaseChangeLog>

changelog_v3.0.xml

<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"><changeSet author="ly" id="2023072203-1"><!--重命名email列名称--><renameColumn tableName="test_create_table" oldColumnName="email" newColumnName="newEmail" columnDataType="VARCHAR(50)"/></changeSet>
</databaseChangeLog>

changelog_v4.0.xml

<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"><changeSet author="ly" id="2023072204-1"><!--插入一条数据--><insert tableName="test_create_table"><column name="id" value="1"></column><column name="username" value="zs"></column><column name="password" value="123"></column><column name="newEmail" value="zs@163.com"></column></insert></changeSet>
</databaseChangeLog>
(6) SpringBoot配置文件
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/qbb01username: rootpassword: rootprofiles:active: dev
(7) Main
package com.ly;import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;/*** @author ly (个人博客:https://www.cnblogs.com/ybbit)* @date 2023-07-22  14:52* @tags 喜欢就去努力的争取*/
@EnableConfigurationProperties
@SpringBootApplication
public class LiquibaseApplication {public static void main(String[] args) {new SpringApplicationBuilder(LiquibaseApplication.class).run(args);}
}
(7) 启动日志

image

(8) 数据库

image

image

4、ChangeSet标签集与作用

(1) add
标签说明
addAutoIncrement将已存在的列改为自增列
addColumn增加列
addDefaultValue增加列的默认值
addForeignKeyConstraint增加外键
addLookupTable创建外键的关联表
addNotNullConstraint增加非空值约束
addPrimaryKey增加主键
addUniqueConstraint增加唯一值约束
(2) create
标签说明
createIndex创建索引
createProcedure创建存储过程
createSequence创建序列
createTable创建表
createView创建视图
(3) drop
标签说明
dropAllForeignKeyConstraints删除全部外键约束
dropColumn删除列
dropDefaultValue删除默认值
dropForeignKeyConstraint删除某一外键约束
dropNotNullConstraint删除空值约束
dropPrimaryKey删除主键
dropProcedure删除存储过程
dropSequence删除序列
dropTable删除表
dropUniqueConstraint删除唯一约束
dropView删除视图
(4) rename
标签说明
renameColumn重命名列
renameSequence重命名序列
renameTable重命名表
renameView重命名视图
5、sql
标签说明
sqlsql语句
sqlFilesql文件
6、其他
标签说明
insert插入数据
update更新数据
delete删除数
empty空操作
executeCommand执行命名
alterSequence修改序列
customChange自定义操作,需自己实现
loadData导入csv数据至已存在的表中
loadUpdateData导入csv数据至表中,表不存在则新建
mergeColumns合并列
modifyDataType修改数据类型
output输出日志
setColumnRemarks增加列说明
setTableRemarks增加表说明
stop停止liquibase
tagDatabase打标签用于将来回滚

5、集成Maven插件

(1) 在pom.xml中加入下面的插件配置
<build><plugins><plugin><groupId>org.liquibase</groupId><artifactId>liquibase-maven-plugin</artifactId><configuration><!--properties文件路径,该文件记录了数据库连接信息等--><propertyFile>src/main/resources/liquibase.properties</propertyFile><propertyFileWillOverride>true</propertyFileWillOverride><!--生成文件的路径--><outputChangeLogFile>src/main/resources/liquibase/change/changelog_base.xml</outputChangeLogFile></configuration></plugin></plugins></build>
(2) 在resources目录下加入liquibase.properties配置文件
#要连接库配置信息
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/qbb01
username=root
password=root
#liquibase
changeLogFile=src/main/resources/liquibase/db.changelog-master.xml
(3) 根据当前配置的数据源生成changelog

image

(4) 结果

image

代码仓库:springboot-liquibase

相关文章:

SpringBoot整合Liquibase

1、是什么&#xff1f; Liquibase官网 Liquibase是一个开源的数据库管理工具&#xff0c;可以帮助开发人员管理和跟踪数据库变更。它可以与各种关系型数据库和NoSQL数据库一起使用&#xff0c;并提供多种数据库任务自动化功能&#xff0c;例如数据库迁移、版本控制和监控。Li…...

虚幻5|给武器添加碰撞检测与伤害

本章内容衔接上两章&#xff0c;需要完成上两章才能用本章内容 虚幻5|角色武器装备的数据库学习&#xff08;不只是用来装备武器&#xff0c;甚至是角色切换也很可能用到&#xff09;-CSDN博客虚幻5|普通攻击&#xff0c;使用接口更方便-CSDN博客 如有疑问&#xff0c;可访问…...

RESTful API设计指南:构建高效、可扩展的Web服务

目录 引言 一.RESTful API概述 二.设计原则 2.1. 资源导向 2.2. 使用标准的HTTP方法 2.3. 无状态通信 2.4. 可缓存响应 2.5. 分层系统 2.6. 按需加载代码&#xff08;可选&#xff09; 2.7. HATEOAS 三.最佳实践 3.1. 明确资源和子资源 3.2. 使用合适的HTTP状态码 …...

黑马头条vue2.0项目实战(九)——编辑用户资料

目录 1. 创建组件并配置路由 2. 页面布局 3. 展示用户信息 4. 修改昵称 5. 修改性别 6. 修改生日 7. 修改头像 7.1 图片上传预览 7.2 使用纯客户端的方式处理用户头像上传预览 7.3 头像裁切 7.4 纯客户端的图片裁切上传流程 7.5 Cropper.js 图片裁剪器的基本使用 …...

43.【C语言】指针(重难点)(F)

目录 15.二级指针 *定义 *演示 16.三级以及多级指针 *三级指针的定义 *多级指针的定义 17.指针数组 *定义 *代码 18.指针数组模拟二维数组 往期推荐 15.二级指针 *定义 之前讲的指针全是一级指针 int a 1; int *pa &a;//一级指针 如果写成 int a 1; int *pa &a…...

【STM32+HAL】杆球控制系统

一、前言 2017年电赛出了道板球控制系统题目&#xff0c;现写一个简化版本——杆球控制系统&#xff0c;以此记录电赛集训生活。 二、题目分析 最终采取的方案是&#xff1a;OpenMV读取小球的当前位置&#xff0c;并将坐标值传给STM32端&#xff0c;再由32通过电机改变杆的位置…...

用Python实现9大回归算法详解——04. 多项式回归算法

多项式回归 是线性回归的一种扩展&#xff0c;它通过将输入特征的多项式项&#xff08;如平方、立方等&#xff09;引入模型中&#xff0c;以捕捉数据中非线性的关系。虽然多项式回归属于线性模型的范畴&#xff0c;但它通过增加特征的多项式形式&#xff0c;使得模型能够拟合非…...

vue打包更新packge.json版本号

VUE项目打包自动更新版本号 此方法只针对 Vue 如果使用其他框架&#xff0c;可以此参照作为参考 一、先看效果 二、创建 buildVersion.js 文件 文件内容 目前只针对3位版本号 递增规则是 每次更新 加一次小版本&#xff0c;10次小版本向前递增一个版本。如&#xff1a;1.0.9 递…...

计算机视觉技术解析:从基础到前沿

第一部分&#xff1a;计算机视觉基础与基本原理 计算机视觉是人工智能领域的一个重要分支&#xff0c;旨在使计算机能够理解和处理图像和视频数据。随着深度学习技术的飞速发展&#xff0c;计算机视觉已经在许多实际应用场景中取得了显著的成果&#xff0c;如图像识别、目标检…...

unity游戏开发003:深入理解Unity中的坐标系

Unity游戏开发 “好读书&#xff0c;不求甚解&#xff1b;每有会意&#xff0c;便欣然忘食。” 本文目录&#xff1a; Unity游戏开发 Unity游戏开发深入理解Unity中的坐标系前言1. 坐标轴2. 左手坐标系3. 世界坐标系 vs. 局部坐标系4. 坐标变换5. 注意事项 总结 深入理解Unity中…...

伊索寓言两则

马和驴 马为自己精美的马具感到骄傲&#xff0c;在大马路上遇见了驴子子正驮着重担挪着步子&#xff0c;挡了路&#xff0c;马儿没法过去&#xff0c;就不耐烦叫道&#xff1a;真想踢你两脚&#xff0c;好让你走快点。驴子沉默不语&#xff0c;但没忘马儿的傲慢。不久后马儿患…...

嵌入式硬件产品开发:编码文件规则

目录 简介 文件内容的一般规则 文件名命名的规则 简介 一个工程是往往由多个文件组成。 这些文件怎么管理、怎么命名都是非常重要的。 文件内容的一般规则 【规则1】每个头文件和源文件的头部必须包含文件头部说明和修改记录。 源文件和头文件的头部说明必须包含的内容和次…...

设计模式 - 组合模式

💝💝💝首先,欢迎各位来到我的博客!本文深入理解设计模式原理、应用技巧、强调实战操作,提供代码示例和解决方案,适合有一定编程基础并希望提升设计能力的开发者,帮助读者快速掌握并灵活运用设计模式。 💝💝💝如有需要请大家订阅我的专栏【设计模式】哟!我会定…...

打靶记录11——Billu_b0x

靶机&#xff1a; https://download.vulnhub.com/billu/Billu_b0x.zip难度&#xff1a; 中&#xff08;两种攻击路线&#xff09; 目标&#xff1a; 取得root权限 涉及的攻击方法&#xff1a; 主机发现端口扫描Web信息收集SQL注入&#xff08;Sqlmap跑不出来&#xff09;…...

一、在cubemx上配置sd和fatfs示例演示

一、sd和fatfs的配置流程界面 1、选择sd4bits 根据自己的sd卡的硬件插槽进行选择。 2、fatfs配置由于使用的是sd卡所以直接选择sd选项 3、程序中对sd卡的初始化需要进行改动&#xff0c;直接使用默认的参数sd卡是挂载不上的。 4、在sd卡挂载好后&#xff0c;就可以使用文件系统…...

C++ 语言特性02 - 命名空间

一&#xff1a;概述 现代C中的命名空间是什么&#xff1f; C中的命名空间允许用户在命名空间范围内对类、方法、变量和函数等实体进行分组&#xff0c;而不是在全局范围内使用。这可以防止大型项目中的类、方法、函数和变量之间发生命名冲突。命名空间将开发人员编写的代码组织…...

drools规则引擎 规则配置文件drl语法使用案例

前提&#xff1a;环境搭建&#xff0c;参考博文springboot整合drools规则引擎 示例入门-CSDN博客案例1&#xff0c;商城系统消费赠送积分 100元以下, 不加分 100元-500元 加100分 500元-1000元 加500分 1000元 以上 加1000分订单pojo编写 package cn.beijing.model;import lom…...

C++编程:高性能通信组件Capnproto与Protobuf的对比分析

文章目录 0. 概要1. 测试环境2. 测试方法3. 测试结果及分析3.1 延迟测试3.2 吞吐量测试3.3 稳定性测试3.4 一对二测试记录3.5 二对一测试记录3.6 Inter-process 单个点开销分析 4. CapnProto 与 Protobuf 的对比测试总结 0. 概要 本文主要探讨了两种高性能通信组件&#xff1a…...

【Python读书数据,并计算数据的相关系数、方差,均方根误差】

为了处理Python中的读书数据&#xff08;假设这里指的是一系列关于书籍阅读量或评分的数据&#xff09;&#xff0c;并计算这些数据的相关系数、方差以及均方根误差&#xff08;RMSE&#xff09;&#xff0c;我们首先需要明确数据的结构。这里&#xff0c;我将假设我们有一组关…...

垃圾收集器G1ZGC详解

G1收集器(-XX:UseG1GC) G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征. G1将Java堆划分为多个大小相等的独立区域&#xff08;Region&#xff09;&#xff0c;JVM目…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...