[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 ...
随机推荐
- Java笔记-添加自定义公共类库
大型项目,为了方便团队开发,需要建立公共类库,提高类库的重用性和维护性步骤如下: --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/387 ...
- Asp.Net MVC4 系列-- 进阶篇之路由
原文 http://blog.csdn.net/lan_liang/article/details/22993839 创建一个路由 打开 RouteConfig.cs ,发现已经创建了一个默认路由 ...
- [翻译] MMMaterialDesignSpinner
MMMaterialDesignSpinner Usage To run the example project, clone the repo, and run pod install from t ...
- Linux入门-5 用户及权限基础
1. Linux用户基础 用户 相关文件 查看登录的用户 添加用户 修改用户信息 删除用户 组 2. Linux权限机制 权限 UGO 修改文件所属用户和组 修改权限 3. Linux权限扩展 默认权 ...
- Python学习---DjangoForm的学习
DjangoForm之创建工程 Form是什么东西: 用于验证用户请求数据合法性的一个组件 普通的Form提交的弊端: 1.用户提交数据的验证 2.前台需要进行错误信息的提示 3.需要保留上次用户输入 ...
- 【MySQL】MySQL数据库再安装
解决问题 安装时提示此产品配置信息损坏,怎么办? 环境检测时未响应,怎么办? 服务不能启动,怎么办? 输入密码不能登陆,不使用密码却能登录,是什么原因? 涉及到的错误代码:windows启动MySQL ...
- Samba文件共享服务器配置
Samba起源: 早期网络想要在不同主机之间共享文件大多要用FTP协议来传输,但FTP协议仅能做到传输文件却不能直接修改对方主机的资料数据,这样确实不太方便,于是便出现了NFS开源文件共享程序:NFS ...
- SpringBoot接口返回去掉空字段
返回的接口中存在值为null或者空的字段过滤掉 @Configuration public class JacksonConfig { @Bean @Primary @ConditionalOnMis ...
- 解决Bug步骤
1.看报错.将bug定位到文件.类.方法. 2.打断点.将bug定位到具体代码行号. 3.分析断点输出结果. 4.结合报错和断点结果,修改代码. 总之:拆解问题.定位问题.分析问题.解决问题.
- 使用MVVM设计模式构建WPF应用程序
使用MVVM设计模式构建WPF应用程序 本文是翻译大牛Josh Smith的文章,WPF Apps With The Model-View-ViewModel Design Pattern,译者水平有 ...