[Doctrine Migrations] 数据库迁移组件的深入解析四:集成diff方式迁移组件
场景及优势
熟悉Symfony框架之后,深刻感受到框架集成的ORM组件Doctrine2的强大之处,其中附带的数据迁移也十分方便。Doctrine2是使用Doctrine DBAL组件把代码里面的表结构与实际数据库中的表结构进行对比的方式进行数据迁移。这种方式比之前版本管理的方式更加精准也更方便。
Symfony框架是自身ORM组件支持,但是很多项目并没有使用其中的ORM功能,或者有自己的ORM组件,又该如何集成diff方式的迁移呢?下面我们就来完成这个任务。
源码解析
在研究组件源码时,发现一个类:Doctrine\DBAL\Migrations\Provider\SchemaDiffProvider,其中getSqlDiffToMigrate方法就是我们编写diff脚本的关键。
/**
* @param Schema $fromSchema
* @param Schema $toSchema
* @return string[]
*/
public function getSqlDiffToMigrate(Schema $fromSchema, Schema $toSchema)
{
return $fromSchema->getMigrateToSql($toSchema, $this->platform);
}
该方法通过传入的两个入参(fromSchema和toSchema),来对比旧数据结构和新数据结构的差异,最后返回相关的sql语句。那么我们只要拿到现有数据库中的表结构,以及代码里面的表结构,就能实现diff方式的数据迁移了。
获取现有数据库的表结构可以通过上面的createFromSchema方法直接获取。
然后我们需要把数据结构写入代码,再从代码中获取实时的表结构,最后diff。
编写diff脚本
首先在项目创建diff文件,并赋予执行权限。之后是根据上面的思路来编写脚本,以下是完整代码示例:
#!/usr/bin/env php
<?php
require_once 'vendor/autoload.php';
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Migrations\Provider\SchemaDiffProvider;
use Doctrine\DBAL\Schema\MySqlSchemaManager;
use Doctrine\DBAL\Schema\Schema;
// 读取数据库配置信息
$db_config = include 'config/db.php';
$db_params = [
'driver' => 'pdo_mysql',
'host' => $db_config['host'],
'port' => $db_config['port'],
'dbname' => $db_config['dbname'],
'user' => $db_config['user'],
'password' => $db_config['password'],
];
try {
$connection = DriverManager::getConnection($db_params);
} catch (DBALException $e) {
echo $e->getMessage() . PHP_EOL;
exit;
}
// 获取数据库表结构
$schema_manager = new MySqlSchemaManager($connection);
$sdp = new SchemaDiffProvider($schema_manager, $schema_manager->getDatabasePlatform());
$from_schema = $sdp->createFromSchema();
// 获取写在脚本里面的表结构
$to_schema = new Schema();
$schema_class_list = glob(__DIR__ . '/db/tables/*.php');
foreach ($schema_class_list as $file) {
require_once $file;
$class = '\\db\\tables\\' . basename($file, '.php');
if (class_exists($class)) {
$cls = new $class;
if (method_exists($cls, 'up')) {
$cls->up($to_schema);
}
}
}
// 进行对比,输出结果
$diff = $sdp->getSqlDiffToMigrate($from_schema, $to_schema);
if ($diff) {
foreach ($diff as $sql) {
echo $sql . ';' . PHP_EOL;
}
}
diff方式的迁移使用
之后,所有的数据表都需要在根目录下db/tables目录里面建立对应的表结构脚本(任意文件名本),下面,我们以test_user表举例:
创建db/tables/testUser.php文件,编写代码:
<?php
namespace db\tables;
use Doctrine\DBAL\Schema\Schema;
class testUser
{
public function up(Schema $schema): void
{
$table = $schema->createTable('test_user');
$table->addColumn('id', 'integer')->setUnsigned(true)->setAutoincrement(true);
$table->addColumn('name', 'string')->setLength(20)->setComment('用户名');
$table->addColumn('age', 'integer')->setUnsigned(true)->setDefault(0)->setComment('年龄');
$table->addColumn('sex', 'string')->setLength(2)->setDefault('')->setComment('性别');
$table->addColumn('is_del', 'boolean')->setDefault(false)->setComment('是否删除');
$table->setPrimaryKey(['id'])->addIndex(['is_del']);
}
}
然后在根目录执行./diff命令,就会看到输出的sql信息。
添加黑名单过滤
到这一步diff方式的迁移已经能正常使用了,但是我们发现,如果是在已有的项目中途加入迁移功能时,使用diff命令会把已经存在的表删除掉,另外,也有时候会有其他表不需要管理的,我们就需要过滤掉这些表。现在我们来添加黑名单功能。
在进行对比之前,加入以下代码:
// 过滤黑名单
$black_list = ['migration_versions', 'test1', 'test2', 'test3'];
foreach ($black_list as $black_table) {
$from_schema->hasTable($black_table) && $from_schema->dropTable($black_table);
$to_schema->hasTable($black_table) && $to_schema->dropTable($black_table);
}
再执行diff命令时,就会过滤掉$black_list变量数组里面的表,当然这个黑名单变量完全可以用其他配置文件来代替,可以任意方式的自定义。
脚本增强
最好再对diff脚本进一步的增强,让它可以直接执行sql。
修改最后的对比代码为以下内容:
// 进行对比,输出结果
$diff = $sdp->getSqlDiffToMigrate($from_schema, $to_schema);
if (empty($diff)) {
echo 'No need to update the schema.' . PHP_EOL;
} elseif ($diff) {
$statement = '';
foreach ($diff as $sql) {
$statement .= $sql;
echo $sql . ';' . PHP_EOL;
}
echo 'Do you want to execute these SQLs? (Y/n)';
$flag = trim(fgets(STDIN));
if ($flag === 'Y') {
$connection->beginTransaction();
try {
$connection->executeUpdate($statement);
$connection->commit();
} catch (\Exception $e) {
try {
$connection->rollBack();
} catch (ConnectionException $e) {
echo $e->getMessage();
exit;
}
echo $e->getMessage() . PHP_EOL;
exit;
}
echo 'SQL executed successfully' . PHP_EOL;
}
}
现在,执行diff命令时就会提示是否需要执行sql(默认不执行)。
结语
现在,diff方式的数据迁移就已经比较完美的集成与项目中了,但是diff方式也有缺陷,就是不能对数据进行管理,比如有时候菜单或者或者权限就需要数据也能同步。所以两种方式要根据自己的项目情况去选择。
此系列的所有代码都可以在文章最后的代码库链接中找到对应的代码,并且每次commit对应每篇文章,方便读者对应文章和代码。
现在此系列就已经完结,希望这个系列的文章能对你使用数据迁移组件有所帮助,感谢您的阅读,也希望提出您宝贵的意见。
在我的代码库可以查看这篇文章的详细代码,欢迎star。
[Doctrine Migrations] 数据库迁移组件的深入解析四:集成diff方式迁移组件的更多相关文章
- [Doctrine Migrations] 数据库迁移组件的深入解析三:自定义数据字段类型
自定义type 根据官方文档,新建TinyIntType类,集成Type,并重写getName,getSqlDeclaration,convertToPHPValue,getBindingType等方 ...
- [Doctrine Migrations] 数据库迁移组件的深入解析二:自定义集成
自定义命令脚本 目录结构 目前的项目结构是这样的(参照代码库): 其中,db/migrations文件夹是迁移类文件夹,config/db.php是我们项目原有的db配置,migrations.php ...
- [Doctrine Migrations] 数据库迁移组件的深入解析一:安装与使用
场景分析 团队开发中,每个开发人员对于数据库都修改都必须手动记录,上线时需要人工整理,运维成本极高.而且在多个开发者之间数据结构同步也是很大的问题.Doctrine Migrations组件把数据库变 ...
- 使用vue开发输入型组件更好的一种解决方式(子组件向父组件传值,基于2.2.0)
(本人想封装一个带有input输入框的组件) 之前使用vue开发组件的时候,在遇到子组件向父组件传递值时我采用的方法是这样的: 比如子组件是一个输入框,父组件调用时需要获取到子组件输入的值,子组件通过 ...
- MVC5中Model层开发数据注解 EF Code First Migrations数据库迁移 C# 常用对象的的修饰符 C# 静态构造函数 MSSQL2005数据库自动备份问题(到同一个局域网上的另一台电脑上) MVC 的HTTP请求
MVC5中Model层开发数据注解 ASP.NET MVC5中Model层开发,使用的数据注解有三个作用: 数据映射(把Model层的类用EntityFramework映射成对应的表) 数据验证( ...
- EFCodeFirst Migrations数据库迁移
EFCodeFirst Migrations数据库迁移 数据库迁移 1.生成数据库 修改类文件PortalContext.cs的静态构造函数,取消当数据库模型发生改变时删除当前数据库重建新数据库的设置 ...
- 鸿蒙开源第三方组件 ——B站开源弹幕库引擎的迁移(上)
鸿蒙入门指南,小白速来!0基础学习路线分享,高效学习方法,重点答疑解惑--->[课程入口] 目录: 一.弹幕库的基础知识 二.弹幕库的使用方法 三.sample解析 四.作者系列文章合集 前言 ...
- 如何使用doctrine:migrations:migrate
doctrine:migrations:migrate: 可以生成数据库表 当新建完实体之后需要执行 doctrine:migrations:diff 更新差异到db 然后就ok了,这时候你的app/ ...
- 可展开的列表组件——ExpandableListView深入解析
可展开的列表组件--ExpandableListView深入解析 一.知识点 1.ExpandableListView常用XML属性 2.ExpandableListView继承BaseExpanda ...
随机推荐
- JDK1.9怎么配置环境变量
- Avalon探索之旅
avalon2是一款基于虚拟DOM与属性劫持的 迷你. 易用. 高性能 的 前端MVVM框架, 拥有超优秀的兼容性, 支持移动开发, 后端渲染, WEB Component式组件开发, 无需编译, 开 ...
- 有关从经典部署模型迁移到 Azure Resource Manager 部署模型的常见问题
此迁移计划是否影响 Azure 虚拟机上运行的任何现有服务或应用程序? 不可以. VM(经典)是公开上市的完全受支持的服务. 你可以继续使用这些资源来拓展你在 Azure 上的足迹. 如果我近期不打算 ...
- Python初学者第十三天 三级菜单程序小作业
13day 作业题目: 三级菜单 作业需求: 数据结构: menu = { '北京':{ '海淀':{ '五道口':{ 'soho':{}, '网易':{}, 'google':{} }, '中关村' ...
- Exchange Server 2007 多名称证书配置
Exchange Server 2007上配置多名称证书,有两种方式,一种是通过Exchange Management Shell利用命令行工具进行创建:另一种是通过证书管理器控制台进行创建.本文将介 ...
- find bugs设置
- memcached源码剖析5:并发模型
网络连接建立与分发 前面分析了worker线程的初始化,以及主线程创建socket并监听的过程.本节会分析连接如何建立与分发. 初始状态 A,可以摸清楚master线程的大致逻辑: 1)初始化各个wo ...
- python process,queue
#-*- coding:utf-8 -*- from multiprocessing import Process,Queue import os,time,random def write(q): ...
- gluoncv faster_rcnn 参数修改
https://github.com/dmlc/gluon-cv/blob/master/gluoncv/model_zoo/faster_rcnn/faster_rcnn.py 对你选用的模块,修改 ...
- 【[HEOI2014]大工程 】
可能是虚树板子题了 首先先把虚树建出来,但是这里和那道虚树的入门题不一样,这里所有的询问点都得在虚树里,所以不会存在那种直接不如栈的点 之后我们考虑一下这个三个要求的东西 第一个操作我们需要统计虚树上 ...