hyperf 二十五 数据迁移 一

news2025/1/22 21:43:36

教程:Hyperf

版本说明

一 生成迁移

php bin/hyperf.php gen:migration create_users_table

执行文件:Hyperf\Database\Commands\Migrations\GenMigrateCommand

功能:创建迁移文件

参数:

  1. name  文件名称

选项:

  1. create 创建。设置create未设置table,则create的值设置给table,其值重新赋值为true。

  2. table 表名。table未设置,则根据参数name文件名正则出表名。

  3. path 路径

  4. realpath 绝对路径

参数通过Hyperf\Database\Commands\Migrations\GenMigrateCommand::getArguments()设置,选项通过Hyperf\Database\Commands\Migrations\GenMigrateCommand::getOptions()设置。

文件创建通过调用Hyperf\Database\Migrations\MigrationCreator::create()实现。

若根据name转化出的类名对应的类存在会抛出错误,类似于"[ERROR] Created Migration: A CreateUserinfoTable class already exists."。

若table为空,则Stub文件为blank.stub;create为true,stub文件为create.stub,否则为update.stub。这些stub文件均为系统的模板文件,存放于vendor\hyperf\database\src\Migrations\stubs。

stub文件用于字符串替换。最后会将替换后的字符串写入path对应的文件,文件名为name。

二 迁移结构

生成的迁移文件\migrations\2024_03_02_034344_create_userinfo_table.php,中包括up()和down()两个方法。up()用于添加新的数据表,字段或者索引到数据库。down()反之用于回滚。

2.1 up

通过 Hyperf\Database\Schema\Schema::__callStatic()获取链接对象。

Schema::__callStatic()调用ConnectionResolver::connection()。connection()参数为空,则使用default数据库链接,否则从加载的配置文件信息读取对应的数据库链接信息。

源码中,数据库连接使用default。

ConnectionResolver::connection()调用Hyperf\Pool\Pool::get()中通过Hyperf\DbConnection\Pool\DbPool::createConnection()创建的Hyperf\DbConnection\Connection类实体对象。

Hyperf\DbConnection\Connection::__construct()根据配置获取链接,比如mysql驱动使用Hyperf\Database\MySqlConnection。MySqlConnection父类为Hyperf\Database\Connection。

所以Schema::__callStatic()最后调用Hyperf\Database\Connection::getSchemaBuilder()或其子类的getSchemaBuilder()方法,返回的Hyperf\Database\Schema\Builder类实体对象。

所以Schema::create(),就是调用Hyperf\Database\Schema\Builder::create()。Builder::create()调用Hyperf\Database\Schema\Blueprint::build()。

还是以mysql驱动为例,设置默认grammar为Hyperf\Database\Schema\Grammars\MySqlGrammar。Blueprint::build()中使用MySqlGrammar::compileCreate()生成对应命令的sql字符串,再通过Hyperf\Database\Connection::statement()执行。

根据源码,Hyperf\Database\Schema\Schema::__callStatic()应该是执行Hyperf\Database\Schema\Blueprint的方法。

2.2 down

根据up()的逻辑,down实际执行Schema::dropIfExists(),即为执行Hyperf\Database\Schema\Blueprint::dropIfExists()。使用mysql驱动,会执行Hyperf\Database\Schema\Grammars\MySqlGrammar::compileDropIfExists()。

三 运行迁移

运行命令

php bin/hyperf.php migrate

没有参数,只有选项。

选项:

  1. database 设置链接使用的数据库

  2. seed 指示是否应该重新运行种子任务
  3. force 在生产环境中强制运行该操作

根据database获取数据库链接,默认default,为配置D:\config\autoload\databases.php文件中数组最外层的key值。根据其获取数据库连接的配置信息。根据对应链接类比如Hyperf\Database\MySqlConnection,其中的getSchemaBuilder()方法获取的Hyperf\Database\Schema\MySqlBuilder类的实体对象,执行MySqlBuilder::hasTable()判断表是否存在。

可能是源码版本的问题,没找到用户设定的force使用代码。

使用seed参数执行db:seed命令,其命令参数--force设置为true,用于填充数据。执行Hyperf\Database\Commands\Seeders\SeedCommand::handle()。可能需要先运行gen:seeder,执行Hyperf\Database\Commands\Seeders\GenSeederCommand::handle()。根据vendor\hyperf\database\src\Seeders\stubs文件生成种子文件,用于SeedCommand::handle()中执行。

3.1 强制执行

php bin/hyperf.php migrate --force

3.2 回滚迁移

3.2.1 回滚指定迁移

php bin/hyperf.php migrate:rollback

选项:

  1. database 设置链接使用的数据库
  2. pretend  转储将要运行的SQL查询

  3. step 执行步数

执行文件Hyperf\Database\Commands\Migrations\RollbackCommand,RollbackCommand::handle()运行Hyperf\Database\Migrations\Migrator::rollback()。

Migrations\Migrator::rollback()使用参数step。step参数意味着回滚的步数,需要配合数据库记录回滚的操作,通过使用step作为limit参数创建sql语句,返回批次号列表。未设置step参数返回最有一次迁移的批次号。

Migrator::rollback()实际运行迁移结构中的down(),最后删除管理迁移记录表中的对应数据。

3.2.2 回滚所有迁移

php bin/hyperf.php migrate:reset

选项:

  1. database 设置链接使用的数据库
  2. pretend  转储将要运行的SQL查询

执行文件Hyperf\Database\Commands\Migrations\ResetCommand,运行Hyperf\Database\Migrations\Migrator::reset()。

Migrator::reset()和Migrator::rollback()不同,获取批次号从大到小排列的全部数据。便利数据时运行down()。但是没有管理迁移记录表数据的删除操作。

3.3 回滚并迁移

php bin/hyperf.php migrate:refresh

选项:

  1. database 设置链接使用的数据库
  2. pretend  转储将要运行的SQL查询

  3. force 在生产环境中强制运行该操作

  4. path 路径

  5. realpath 绝对路径

  6. seed 指示是否应该重新运行种子任务

  7. seeder 设置种子的类名

  8. step 执行步数

执行文件Hyperf\Database\Commands\Migrations\RefreshCommand。step大于0执行migrate:rollback,否则执行migrate:reset,最后执行migrate。设置seed或seeder后执行db:seed。

选项的使用参数其他命令。其设置的选项也是在其他命令中运行。其本身没有其他逻辑。

3.4 重建数据库

php bin/hyperf.php migrate:fresh

选项:

  1. database 设置链接使用的数据库
  2. drop-views 删除所有视图

  3. path  路径

  4. realpath 绝对路径

  5. force 在生产环境中强制运行该操作

  6. step 执行步数

  7. seed 指示是否应该重新运行种子任务

  8. seeder 设置种子的类名

执行文件Hyperf\Database\Commands\Migrations\FreshCommand。

drop-views为true则删除视图,以myql驱动为例,执行Hyperf\Database\Schema\MySqlBuilder::dropAllViews(),MySqlBuilder::dropAllViews()调用Hyperf\Database\Schema\Grammars\MySqlGrammar::compileDropAllViews()返回的sql字符串,在Hyperf\Database\MySqlConnection的父类Hyperf\Database\Connection::statement()中执行。

四 测试

show databases;
use test1;
show tables;

 运行结果

#show databases;
test
test1
#show tables;
null

 证明test1数据库中无表。

数据库链接配置文件

#config\autoload\databases.php

declare (strict_types = 1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
return [
    'default' => [
        'driver' => env('DB_DRIVER', 'mysql'),
        'host' => env('DB_HOST', '127.0.0.1'),
        'database' => env('DB_DATABASE', 'test'),
        'port' => env('DB_PORT', 3306),
        'username' => env('DB_USERNAME', 'root'),
        'password' => env('DB_PASSWORD', 'root'),
        'charset' => env('DB_CHARSET', 'utf8'),
        'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
        'prefix' => env('DB_PREFIX', ''),
    ],
    'default2' => [
        'driver' => env('DB_DRIVER', 'mysql'),
        'host' => '127.0.0.1',
        'database' => 'test1',
        'port' => env('DB_PORT', 3306),
        'username' => env('DB_USERNAME', 'root'),
        'password' => env('DB_PASSWORD', 'root'),
        'charset' => env('DB_CHARSET', 'utf8'),
        'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
        'prefix' => env('DB_PREFIX', ''),
    ],
];

使用env文件会先取env文件的值,所以在env有值的情况下设置的自定值无效。即env("DB_DATABASE",'test')和env("DB_DATABASE",'test1')值相同,都是env文件DB_DATABASE的值。

执行命令

php bin/hyperf.php gen:migration create_userinfo_table

 生成文件migrations\2024_03_02_034344_create_userinfo_table.php

 php bin/hyperf.php migrate --database=default2 --pretend
#show tables;
migrations
userinfo
#show CREATE table migrations;
CREATE TABLE `migrations` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `batch` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

此时俩表都没有数据。添加字段之后,应该也是仅适合表结构迁移。

表数据迁移可以使用工具或者sql文件执行。

为测试回滚等操作,生成新的迁移文件。

php bin/hyperf.php gen:migration create_userinfo2_table --table=userinfo
php bin/hyperf.php gen:migration create_userinfo3_table --create=userinfo

 在对应表迁移文件已有的基础上,检查类名已存在会报错。判断类名已存之前未判断--create和--table参数,所以设置这俩不会解决报错,除非换路径或把想同的类名改掉。

使用table生成的文件如下

class CreateUserinfo2Table extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('userinfo', function (Blueprint $table) {
            //
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('userinfo', function (Blueprint $table) {
            //
        });
    }
}

使用create生成文件如下

class CreateUserinfo3Table extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('userinfo', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('userinfo');
    }
}

 migrations表没数据,回滚会报错"Nothing to rollback."。所以需要看下migrations表何时写入数据。经过查询发现,应该是pretend选项导致。源码执行up()之后,判断pretend参数,为true则执行Hyperf\Database\Migrations\Migrator::pretendToRun(),然后return。pretend默认为false,最后执行Hyperf\Database\Migrations\DatabaseMigrationRepository::log(),写入migrations表。

删表重来一次。

DROP TABLE migrations;
DROP TABLE userinfo;
php bin/hyperf.php migrate --database=default2

 会在test1数据库没有userinfo表存在的情况下,报数据表userinfo重复,因为test数据库有userinfo。这个报错还是执行create后报的错,应为create执行在test数据库中。

但是加选项pretend执行Hyperf\Database\Migrations\Migrator::pretendToRun(),就可以正常执行。

由于测试环境为,一个项目使用两个数据库连接配置链接本地两个库,而不使用pretend的情况下迁移文件默认使用default。即使用同一个项目环境迁移数据库必须使用prepend,但是迁移后的库就记录不了迁移记录。

可以稍微改下

#Hyperf\Database\Migrations\Migrator

protected function runMigration(object $migration, string $method): void {
    ……
    $this->resolver->setDefaultConnection($migration->getConnection() ?: $this->connection);
    ……
}

#改为
protected function runMigration(object $migration, string $method): void {
    ……
    $this->resolver->setDefaultConnection($this->connection ?: $migration->getConnection());
    ……
}

归结原因是按照之前逻辑获取迁移文件对象的getConnection()方法,因为是新对象获取的就是default。所以应该改为先判断当前对象的connect属性是否有值,没有则采用新对象的默认值。

执行

 php bin/hyperf.php migrate --database=default2

成功

Migrating: 2024_03_06_082623_create_userinfo_table
Migrated:  2024_03_06_082623_create_userinfo_table

此时migrations表中数据正常。

select * from migrations

 

执行回滚

php bin/hyperf.php migrate:rollback --database=default2

成功

Rolling back: 2024_03_06_082623_create_userinfo_table
Rolled back:  2024_03_06_082623_create_userinfo_table

此时 migragtions没有数据,test1库中没有userinfo表。

五 源码修改内容

#Hyperf\Database\Migrations\Migrator

protected function runMigration(object $migration, string $method): void {
    ……
    $this->resolver->setDefaultConnection($migration->getConnection() ?: $this->connection);
    ……
}

#改为
protected function runMigration(object $migration, string $method): void {
    ……
    $this->resolver->setDefaultConnection($this->connection ?: $migration->getConnection());
    ……
}

 修改原因:执行时使用新对象默认的数据库链接(default),不能使用自定义的数据库链接。

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

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

相关文章

Docker连接Mysql

Docker连接mysql Docker通过云服务器,与本地连接 引言:这个东西我看狂神秒解决,我就试试了,结果G了,花了我两个小时, 希望内容能帮你解决问题 话不多说,直接上内容: navicat连接…

记一次 .NET某设备监控自动化系统 CPU爆高分析

一:背景 1. 讲故事 先说一下题外话,一个监控别人系统运行状态的程序,结果自己出问题了,有时候想一想还是挺讽刺的,哈哈,开个玩笑,我们回到正题,前些天有位朋友找到我,说…

redis IO多路复用模型详解

一、IO 1.1、IO模型 我们常说的IO,指的是文件的输入和输出 ,但是在操作系统层面是如何定义IO的呢?到底什么样的过程可以叫做是一次IO呢? 拿一次磁盘文件读取为例,我们要读取的文件是存储在磁盘上的,我们的…

Redis的BitMap的使用

简介 Redis的Bitmap不是一个独立的数据结构类型,而是基于字符串(String)类型实现的一种功能 ,存储的是二进制的文件,布隆过滤器就是基于BitMap实现的。 语句的使用 新增操作 setbit key offset value offset的首位…

车充芯片方案 H4112 异步整流芯片 12V转5V 24V转5V 24V转9V 24V转12V

车充芯片方案的工作原理: 利用PWM(脉冲宽度调制)技术来调节开关管的开关时间,从而实现稳定的输出电压,就是通过微处理器的数字输出来对模拟电路进行控制,根据面积等效原理,通过对一系列脉冲的宽…

支持向量机 SVM | 非线性可分:核函数

目录 一. 情景引入二. 核函数1. 核函数的分类1.1 线性核函数(Linear Kernel)1.2 多项式核函数(Polynomial Kernel)1.3 高斯核函数(Radial Basis Function Kernel)1.4 Sigmoid核函数(Sigmoid Kernel) 2. 核函数小节 前面我们讲述了SVM算法的线性可分问题,即对应硬间隔…

集简云新增通义千问qwen 72b chat、qwen1.5 等多种大语言模型,提升多语言支持能力

通义千问再开源!继发布多模态模型后,通义千问 1.5 版本也在春节前上线。 此次大模型包括六个型号:0.5B、1.8B、4B、7B、14B 和 72B,性能评测基础能力在在语言理解、代码生成、推理能力等多项基准测试中均展现出优异的性能&#x…

个人商城系统开源(登录)

原文地址:个人商城系统开源(登录) - Pleasure的博客 下面是正文内容: 前言 由于近期实在没有什么话题可写和一些有趣的项目教程可以分享。所以我只能决定将我自己亲手编写的一个迷你迷你商城系统进行开源。 也就是放在我博客右边…

【unity实战】事件(Event)的基本实战使用

文章目录 最终效果前言一、素材二、角色金币交互1. 拾取金币2. 显示金币数UI 完结 最终效果 前言 之前我们介绍过委托的用法,具体可以跳转:【unity小技巧】委托(Delegate)的基础使用和介绍 这期来讲讲事件,使用你会发…

IDEA稀奇古怪问题的解决方案

idea在电脑死机重启后,启动项目报错 尝试了各种办法,重新导入项目,删除.idea文件重新导入,把本地代码删除重新pull下来再次导入,均无法解决。而且代码在eclipse中可以正常启动,遂排除代码和网络环境原因。…

【VTKExamples::PolyData】第四十九期 Silhouette

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享VTK样例Silhouette,并解析接口vtkPolyDataSilhouette,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 1. Silhouett…

消息队列实现AB进程对话

进程A代码&#xff1a; #include <stdio.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <stdlib.h>#include <string.h>#define MSG_EXCEPT 020000struct msgbuf{long mtype;char mtext[100];};int main(in…

Redis6 搭建主从集群架构

文章目录 搭建Redis主从集群架构1.集群结构2.准备实例和配置3.启动4.开启主从关系5.测试 搭建Redis主从集群架构 安装部署单机版Redis6可参考&#xff1a; 安装部署单机版Redis6 1.集群结构 我们搭建的主从集群结构如图&#xff1a; 我们计划是在一台虚拟机里去部署三个R…

Docker数据卷篇

1. 数据卷&#xff08;容器数据管理&#xff09; 引言&#xff1a;在之前的nginx案例中&#xff0c;修改nginx的html页面时&#xff0c;需要进入nginx内部。并且因为没有编辑器&#xff0c;修改文件也很麻烦。 这就是因为容器与数据&#xff08;容器内文件&#xff09;耦合带…

重新下载各种编辑器、软件

全是资源编辑器&#xff0c;书写&#xff0c;有需要书写资料或者是代码编程的可以找我呀&#xff0c;闲暇时间接一下副业。 git任务也重启了&#xff0c;原家厨房项目也重启了&#xff0c;浓心项目也在重启。如下图 后续再慢慢联系吧&#xff0c;先书写到这里&#xff0c;晚安…

Day29-Ubuntu介绍及企业标准安装实战

Day29-Ubuntu介绍及企业标准安装实战 第1章 Ubuntu Linux系统介绍1.Ubuntu是什么2.Ubuntu Linux基本特点3.为什么要讲Ubuntu&#xff1f;4.Ubuntu服务器版本选择5.如何学习Ubuntu使用6.Linux发展前景说明简介 第2章 Ubuntu安装环境搭建准备第3章 Ubuntu安装实战1. 开机安装Ubun…

【无标题】day6网路

#include<myhead.h> int main(int argc, const char *argv[]) {int workid 0;char name[20] "";double salary 0;sqlite3 *ppDb NULL;//数据库句柄指针//打开数据库&#xff0c;如果数据库不存在&#xff0c;则创建数据库//将数据库句柄由参数2返回if((sql…

JVM工作原理与实战(四十二):JVM常见面试题目

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、JVM常见面试题目 1.请阐述JVM的概念及其核心功能&#xff0c;并简要介绍其组成部分和常用的实现。 2.请阐述Java字节码文件的组成部分。 3.请描述JVM的运行时数据区及其组成部分…

Android中的传感器类型和接口名称

本文将介绍传感器坐标轴、基础传感器和复合传感器&#xff08;动作传感器、姿势传感器、未校准传感器和互动传感器&#xff09;。 1. 传感器坐标轴 许多传感器的传感器事件值在相对于设备静止的特定坐标系中表示。 1.1 移动设备坐标轴 Sensor API 仅与屏幕的自然方向相关&a…

前端将html导出pdf文件解决分页问题

这是借鉴了qq_251025116大佬的解决方案并优化升级完成的&#xff0c;原文链接 1.安装依赖 npm install jspdf html2canvas2.使用方法 import htmlToPdffrom ./index.jsconst suc () > {message.success(success);};//记得在需要打印的div上面添加 idlet dom document.que…