hyperf 二十五 数据迁移 一

教程: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.mfbz.cn/a/434990.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JS】关于this的使用

this 前言一、this是什么?二、做什么?1.全局环境2.函数环境3.new实例对象4.apply、bind、call绑定4.1 apply()4.2 call()4.3 bind() 三、为什么用this?四、如何改变this?五、应用场景?总结 前言 痛点 经常写Vue项目&a…

day36 贪心算法part5

435. 无重叠区间 中等 给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。 气球问题稍加改动就可ac 一个交叉区间里,最终只能保留一个,其他的全部要去掉。…

软考66-上午题-【面向对象技术】-小结+杂题

一、杂题 真题1: 真题2: 真题4: 真题5: 真题6: 二、面向对象设计-总结 2-1、考题分析 选择题:11道(11分) 综合分析题:2道(30分) java程序设计…

Common Sense Machines(CSM):立志成为图像生成适用于游戏引擎的3D资产AI产品

详细说明 Common Sense Machines(CMS):立志成为图像生成适用于游戏引擎的3D资产AI产品-喜好儿aigc详细说明:https://heehel.com/CSM-3d 官方网站:https://www.csm.ai/ 使用体验网址:https://3d.csm.ai/ 来…

Rust错误处理和Result枚举类异常错误传递

Rust 有一套独特的处理异常情况的机制,它并不像其它语言中的 try 机制那样简单。 首先,程序中一般会出现两种错误:可恢复错误和不可恢复错误。 可恢复错误的典型案例是文件访问错误,如果访问一个文件失败,有可能是因…

微信小程序用户登陆和获取用户信息功能实现

官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 接口说明: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html 我们看官方这个图,梳理一下用户…

【Python爬虫实战】抓取省市级城市常务会议内容

🍉CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一|统计学|干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项,参与研究经费10w、40w级横向 文…

Three.js--》探寻Cannon.js构建震撼的3D物理交互体验(二)

我们用three.js可以绘制出各种酷炫的画面,但是当我们想要一个更加真实的物理效果的话,这个时候我们就需要一个物理的库,接下来我们就讲解一下今天要学习的canon,它可以给我们提供一个更加真实的物理效果,像物体的张力、…

Python - Pycharm 配置 autopep8 并设置快捷键

什么是 PEP8 官方:PEP 8 – Style Guide for Python Code | peps.python.org PEP8 是 Python 官方推出的一套编码的规范,只要代码不符合它的规范,就会有相应的提示,还可以让代码自动的格式化 Pycharm 自带的代码格式化 ​ 但这…

【C++】String常用的函数总结

目录 一、string的构造函数方式: 二、常用的大小/容量相关操作: 三、string的常用修改操作: 四、string的遍历: 五、string的任意位置插入 / 删除: 六:补充: 一、string的构造函数方式&a…

JavaWeb环境配置 IDE2022版

一、新建一个javaweb文件 文件名可以自己随意改 二、给建立的项目添加框架支持 勾选Web Application,点击确定 建立成功界面,会生成一个新的web文件夹 三、配置tomcat 1、两种打开配置文件方式: 第一种 第二种 2、打开后,点击号&#xf…

LLM | Gemma的初体验

一起来体验一下吧~ google/gemma-7b-it Hugging Face 此型号卡对应于 Gemma 型号的 7B 指令版本。还可以选择 2B 基本模型、7B 基本模型和 2B 指导模型的模型卡。 微调 使用 QLoRA 对 UltraChat 数据集执行监督微调 (SFT) 的脚本在 TPU 设备上使用 FS…

鸿蒙Harmony应用开发—ArkTS声明式开发(手势处理:绑定手势方法)

为组件绑定不同类型的手势事件,并设置事件的响应方法。 说明: 从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 绑定手势识别 通过如下属性给组件绑定手势识别,手势识别成功后可以通过事…

LVS负载均衡集群基础概念

目录 一、集群 1、集群概述 1.1 什么是集群 1.2 集群系统扩展方式 1.2.1 Scale UP(纵向扩展) 1.2.2 Scale OUT(横向扩展) 1.2.3 区别 1.3 分布式系统 1.4 分布式与集群 1.5 集群设计原则 1.6 集群设计实现 1.6.1 基础…

手回科技:人生的“小雨伞”,能否撑起自己的增长路?

有道是一年之计在于春。新年伊始,多家券商发布研报表达了对2024年保险市场表现的观点。 比如,开源证券表示,政策组合拳带来beta催化,保险业务端和弹性占优;中国银行证券指出,2024年,保险行业景…

Leetcode 第 125 场双周赛题解

Leetcode 第 125 场双周赛题解 Leetcode 第 125 场双周赛题解题目1:3065. 超过阈值的最少操作数 I思路代码复杂度分析 题目2:3066. 超过阈值的最少操作数 II思路代码复杂度分析 题目3:3067. 在带权树网络中统计可连接服务器对数目思路代码复杂…

Marin说PCB之POC电路layout设计仿真案例---01

最近娃哈哈饮料突然爆火,看新闻后才知道春晚的的时候宗老已经病的很严重了,现在也已经离我们而去了,宗老是一个值得我们尊敬爱戴的伟大企业家。于是乎小编我立马去他们的直播间买了一箱娃哈哈AD钙奶支持一下我们的国货。 中午午休的时候&…

智慧城市的未来:利用数字孪生技术推动智慧城市的智能化升级

目录 一、引言 二、数字孪生技术概述 三、数字孪生技术在智慧城市中的应用 1、城市规划与建设 2、城市管理与运营 3、公共服务与民生改善 4、应急管理与灾害防控 四、数字孪生技术推动智慧城市的智能化升级的价值 1、提高城市管理的智能化水平 2、优化城市资源配置 …

python将conda环境打入docker环境中

1.假设你本地已经安装好了conda相关的 ubuntu安装python以及conda-CSDN博客 并且已经创建启动过相关的环境,并且install了相关的包。 我本地的conda环境叫做,gptsovits_conda3 2.下载conda打包工具 conda install conda-pack pip install conda-pack 3.打包 con…

java八股文复习-----2024/03/04----基础

相关资源 大彬八股文 2024八股文 2024秋招八股文 1.了解Java的包装类型吗?为什么需要包装类? Java 是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将 int 、double 等类型…
最新文章