jwj 发布的文章

下载:https://downloads.mysql.com/archives/community/
解压到:C:\Program Files目录
创建my.ini配置文件

[mysql]
port = 3306
default-character-set=utf8

[mysqld]
default_authentication_plugin=mysql_native_password
port = 3306
basedir="C:/Program Files/mysql-5.7.28-winx64"
datadir="C:/Program Files/mysql-5.7.28-winx64/data"
log-error="C:/Program Files/mysql-5.7.28-winx64/logs/mysql.log"
character-set-server=utf8
default-storage-engine=INNODB

C:/Program Files/mysql-5.7.28-winx64加入环境变量
初始化mysql

mysqld --initialize-insecure --user=root

初始化后,默认用户为root,密码为空
安装服务

mysqld --install

执行返回

Service successfully installed.

启动服务

今天重新安装了下Wampserver3.2.0,安装好之后,发下MySQL8.0数据库的服务一直启动不了,查看事件查看器应用程序日志没有错误信息,查看MySQL日志也没有错误信息,这就很蛋疼了!

既然通过刚刚那些地方都找不到错误信息,只能在控制台尝试运行下mysql了。在控制台打开mysqlbin目录,然后输入

./mysqld

返回以下错误

2020-03-08T12:13:19.585857Z 0 [System] [MY-010116] [Server] C:\wamp64\bin\mysql\mysql8.0.18\bin\mysqld.exe (mysqld 8.0.18) starting as process 3132
2020-03-08T12:13:19.813867Z 0 [Warning] [MY-010091] [Server] Can't create test file C:\wamp64\bin\mysql\mysql8.0.18\data\mysqld_tmp_file_case_insensitive_test.lower-test
2020-03-08T12:13:19.814672Z 0 [Warning] [MY-010091] [Server] Can't create test file C:\wamp64\bin\mysql\mysql8.0.18\data\mysqld_tmp_file_case_insensitive_test.lower-test
2020-03-08T12:13:19.815318Z 0 [ERROR] [MY-013276] [Server] Failed to set datadir to 'C:\wamp64\bin\mysql\mysql8.0.18\data\' (OS errno: 2 - No such file or directory)
2020-03-08T12:13:19.988677Z 0 [ERROR] [MY-010119] [Server] Aborting
2020-03-08T12:13:19.997084Z 0 [System] [MY-010910] [Server] C:\wamp64\bin\mysql\mysql8.0.18\bin\mysqld.exe: Shutdown complete (mysqld 8.0.18)  MySQL Community Server - GPL.

通过错误信息,可以知道,因为文件无法创建,导致MySQL启动失败。再检查下MySQLdata目录,发现这个目录不存在。
然后回想下安装过程,安装的时候,因为没有提前安装好运行库,所以安装过程中,提示了几次缺少xxx.dll。我想应该是这个原因,导致MySQL初始化失败了。
那就手动初始化下吧~还是打开控制台,在控制台打开mysqlbin目录,然后输入以下命令

mysqld --initialize-insecure --user=root

执行完之后,再尝试启动MySQL的服务,正常运行~

最近,一打开v2ray.exe就闪退,日志文件里也没有记录到错误信息,试了好久都找不到具体原因。后面尝试在控制台打开v2ray.exe,才发现具体的错误信息,然后问题也顺利解决了。

打开CMD控制台,转到v2ray所在目录,然后执行下面的命令

# .\v2ray.exe

命令执行后,提示以下错误信息

V2Ray 4.21.3 (V2Fly, a community-driven edition of V2Ray.) Custom
A unified platform for anti-censorship.
Failed to start v2ray.com/core/app/proxyman/inbound: failed to listen TCP on 1080 > v2ray.com/core/transport/internet: failed to listen on address: 0.0.0.0:1080 > listen tcp 0.0.0.0:1080: bind: An attempt was made to access a socket in a way forbidden by its access permissions.

大概意思是

启动失败:无法监听TCP 1080端口,无法监听0.0.0.0:1080地址,无权访问socket

找了下资料,得到的信息很少,尝试把0.0.0.0换成127.0.0.1也不行。然后想,是不是端口被占用了,执行netstat -ano|findstr ":1080",也没有找到被谁占用,但还是尝试换了个端口,竟然没有问题了。

虽然问题解决了,但原因没找到,这就很纳闷了。一直到家里的电脑突然也出现了同样的问题以后,仔细想想,才知道是什么导致了的。
原来是我装了Hyper-V,导致1080端口被系统保留了,真是操蛋了~

用下面这命令在控制台执行,可以查看系统保留的tcp端口

netsh interface ipv4 show excludedportrange protocol=tcp

用下面这命令在控制台执行,可以查看系统保留的udp端口

netsh interface ipv4 show excludedportrange protocol=udp

如果确实解决不了系统保留的问题,我们就只能改端口了,改成系统保留范围外的端口。

TP框架的column数据库查询方法是一个非常方便的快捷查询方法,可以用该方法快速的返回结果集中的列,并且可以指定字段作为数据集的数组下标。但是在使用SQL函数后,却异常的返回了索引数组,而不是想要的关联数组。

我想查询本月每天的总营业额,所以使用了以下的方法查询

Db::table('ledger')->where('create_time', 'between', ['2020-01-01', '2020-01-31'])->group('DATE_FORMAT(ledger_date, "%m-%d")')->column('SUM(amount)', 'DATE_FORMAT(ledger_date, "%m-%d")')

我理想中的结果应该是

[
    [01-01] => 100.00,
    [01-02] => 200.00,
    [01-03] => 210.00,
    ...
]

但结果却是

[
    0 => 100.00,
    1 => 200.00,
    2 => 210.00,
    ...
]

这不对劲啊,怎么返回了索引数组,不是关联数组?
然后就看代码,断点调试,终于发现了问题,竟然是一个小小的空格导致的!!!
原来,我使用了SQL函数

DATE_FORMAT(ledger_date, "%m-%d")

然后TP框架在处理SQL列名称的时候,会以,分割列名称,然后使用trim来去除空格

$field = array_map('trim', explode(',', $field));

所以实际去查询的时候,列名称,两边的空格都被去除了,变成了

SELECT DATE_FORMAT(ledger_date,"%m-%d"),SUM(amount) FROM `ledger` ....

但是索引的字段名没变,所以最终

DATE_FORMAT(ledger_date, "%m-%d")

不等于

DATE_FORMAT(ledger_date,"%m-%d")

导致没能生成关联数组

最终的解决方法是,我们使用之前就把逗号两边的空格都去掉,就能得到自己想要的了。

Db::table('ledger')->where('create_time', 'between', ['2020-01-01', '2020-01-31'])->group('DATE_FORMAT(ledger_date,"%m-%d")')->column('SUM(amount)', 'DATE_FORMAT(ledger_date,"%m-%d")')

最近刚好用上模型事件,但手册上对事件的触发条件却没有详细的进行说明。那么,就只能自己进行测试了。

模型事件

首先,从手册上,我们可以知道模型支持以下事件:

事件描述事件方法名
after_read查询后onAfterRead
before_insert新增前onBeforeInsert
after_insert新增后onAfterInsert
before_update更新前onBeforeUpdate
after_update更新后onAfterUpdate
before_write写入前onBeforeWrite
after_write写入后onAfterWrite
before_delete删除前onBeforeDelete
after_delete删除后onAfterDelete
before_restore恢复前onBeforeRestore
after_restore恢复后onAfterRestore

建立模型

为了了解每个事件的触发条件,我们先建立以下模型

模型代码app/model/Users.php

<?php
namespace app\model;

use think\Model;
use think\model\concern\SoftDelete;

class Users extends Model
{
    // 软删除
    use SoftDelete;

    public static function onAfterRead($user) {
        dump('查询后');
    }

    public static function onBeforeInsert($user) {
        dump('新增前');
    }

    public static function onAfterInsert($user) {
        dump('新增后');
    }

    public static function onBeforeUpdate($user) {
        dump('更新前');
    }

    public static function onAfterUpdate($user) {
        dump('更新后');
    }

    public static function onBeforeWrite($user) {
        dump('写入前');
    }

    public static function onAfterWrite($user) {
        dump('写入后');
    }

    public static function onBeforeDelete($user) {
        dump('删除前');
    }

    public static function onAfterDelete($user) {
        dump('删除后');
    }

    public static function onBeforeRestore($user) {
        dump('恢复前');
    }

    public static function onAfterRestore($user) {
        dump('恢复后');
    }
}

数据表

CREATE TABLE `users` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
 `name` varchar(32) NOT NULL COMMENT '姓名',
 `area` varchar(32) NOT NULL COMMENT '区域',
 `address` varchar(64) NOT NULL COMMENT '地址',
 `balance` decimal(9,2) NOT NULL COMMENT '余额',
 `password` varchar(255) NOT NULL COMMENT '密码',
 `status` int(11) NOT NULL COMMENT '状态',
 `last` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后活跃时间',
 `phone` varchar(11) NOT NULL COMMENT '手机号',
 `delete_time` int(10) unsigned DEFAULT NULL COMMENT '删除时间',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户表'

测试代码

代码

然后通过以下代码去操作数据库

use \app\model\Users;
$data = [
    'name' => 'test',
    'phone' => '13888888888',
    'area' => '0',
    'address' => '广东省韶关市',
    'balance' => '0',
    'password' => '0',
    'status' => '0',
    'last' => '2019-01-01 00:00:00',
];

dump('Users::create($data)');
$users = Users::create($data);

dump('Users::insert($data)');
Users::insert($data);

dump('Users::where("id", $users["id"])->update(["area" => 1])');
Users::where("id", $users["id"])->update(["area" => 1]);

dump('Users::update(["area" => 1], ["id" => $users["id"]])');
Users::update(["area" => 1], ["id" => $users["id"]]);

dump('$users->save(["area" => 2])');
$users->save(["area" => 2]);

dump('Users::where("id", $users["id"])->delete()');
Users::where("id", $users["id"])->delete();

dump('Users::where("id", ">", 0)->find()');
$users = Users::where("id", ">", 0)->find();

dump('Users::destroy($users["id"])');
Users::destroy($users["id"]);

dump('$users->restore()');
$users->restore();

dump('$users->delete()');
$users->delete();

执行结果

执行之后,返回以下结果

^ "Users::create($data)"
^ "写入前"
^ "新增前"
^ "新增后"
^ "写入后"
^ "Users::insert($data)"
^ "Users::where("id", $users["id"])->update(["area" => 1])"
^ "Users::update(["area" => 1], ["id" => $users["id"]])"
^ "写入前"
^ "更新前"
^ "更新后"
^ "写入后"
^ "$users->save(["area" => 2])"
^ "写入前"
^ "更新前"
^ "更新后"
^ "写入后"
^ "Users::where("id", $users["id"])->delete()"
^ "Users::where("id", ">", 0)->find()"
^ "查询后"
^ "Users::destroy($users["id"])"
^ "查询后"
^ "删除前"
^ "删除后"
^ "$users->restore()"
^ "恢复前"
^ "恢复后"
^ "$users->delete()"
^ "删除前"
^ "删除后"

总结

方法查询后新增前新增后更新前更新后写入前写入后删除前删除后恢复前恢复后
create()
insert()
update()
save()
delete()
find()
destroy()
restore()

create()

模型创建数据方法,会触发写入前新增前新增后写入后。使用模型的save()saveAll()来新增方法也会触发这几个事件。

insert()

insert()是Db类的方法,不是模型方法,不会触发模型事件。

update()

update()是Db类的方法,不是模型方法,不会触发模型事件。

如果是模型静态调用update(),则执行的是模型的update方法,而模型的update方法会调用save()方法,所以跟模型的save()方法一样,会触发写入前更新前更新后写入后事件
感谢 @dejavu 的提醒

save()

使用模型的save()方法来更新数据,会触发写入前更新前更新后写入后事件。

delete()

如果是使用模型方法查询出来数据,然后再删除数据,则会触发删除前删除后事件。
如果是直接使用条件删除,则不会触发模型事件。因为直接使用条件删除,这时候的delete()方法不是模型方法。

find()

该查询方法会触发查询后事件

destroy()

该删除数据方法会触发查询后删除前删除后。所以,该方法是先查询出数据,然后再删除该数据。

restore()

该软删除恢复方法会触发恢复前恢复后方法