From bba0b720db3103d963291b1b258863900d11a56a Mon Sep 17 00:00:00 2001 From: muqiuhan Date: Wed, 19 Feb 2025 05:50:06 +0000 Subject: deploy: adae24e1e3421dd2d625937b06a939bc3fe3a37d --- .../index.html" | 275 ++++++++++++++++ .../index.html" | 344 +++++++++++++++++++++ .../19/Repository-pattern-in-Typescript/index.html | 308 ++++++++++++++++++ 3 files changed, 927 insertions(+) create mode 100644 "2025/01/19/\350\272\253\344\275\223\345\276\200\345\211\215\345\200\276\357\274\214\347\201\265\351\255\202\345\220\221\345\220\216\346\221\207/index.html" create mode 100644 "2025/02/18/Prisma-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\347\232\204-Self-relations/index.html" create mode 100644 2025/02/19/Repository-pattern-in-Typescript/index.html (limited to '2025') diff --git "a/2025/01/19/\350\272\253\344\275\223\345\276\200\345\211\215\345\200\276\357\274\214\347\201\265\351\255\202\345\220\221\345\220\216\346\221\207/index.html" "b/2025/01/19/\350\272\253\344\275\223\345\276\200\345\211\215\345\200\276\357\274\214\347\201\265\351\255\202\345\220\221\345\220\216\346\221\207/index.html" new file mode 100644 index 00000000..6abbd905 --- /dev/null +++ "b/2025/01/19/\350\272\253\344\275\223\345\276\200\345\211\215\345\200\276\357\274\214\347\201\265\351\255\202\345\220\221\345\220\216\346\221\207/index.html" @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + +身体往前倾,灵魂向后摇 | 暮秋小屋 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+
+ + + +
+
+
+ + +
+
+
+ + +
+ +
+ +
+ +
+
+
+

后摇( Post-Rock )是用摇滚乐器作非摇滚的用途,吉他对音色和结构的推动作用超过了Riff和主旋律。后摇的关键问题除了电子的启用之外,还有多层次打击乐器的出现。

+

现代社会的后摇创作者们不必为任何形式和任何器物所限制,庞大的音乐历史使得任何形式都展现为能为现代人所应用的形式,现代技术所提供的音乐制作功能则使得作为乐器的器物在后摇中不必再被特地强调。

+

而对于后摇的受众即聆听者而言,这一现代性的音乐则在正真意义上解放了听众,使得听众成为肆意解读的读者。后摇的解放意义完成了作品自创作完成后从作者向听众的让渡,作品的拥有者不是作者本人,而恰恰是一群无从计数的听众, 作为独特个体而非集体的听众。

+

因此,后摇的出现意味着创作者与受众的双向失责。两者在彼此自私自取的艺术行为中消弭了主体与客体这一古老的二分法。

+

极致的喧闹与绝对的安静并无区别。

+ +
+ + + + + +
+ + + + + + + +
+ + +
+
+
+ + + +
+ + +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + + + + diff --git "a/2025/02/18/Prisma-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\347\232\204-Self-relations/index.html" "b/2025/02/18/Prisma-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\347\232\204-Self-relations/index.html" new file mode 100644 index 00000000..c4c33004 --- /dev/null +++ "b/2025/02/18/Prisma-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\347\232\204-Self-relations/index.html" @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + +Prisma 关系型数据库的 Self-relations | 暮秋小屋 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+
+ + + +
+
+
+ + +
+
+
+ + +
+ +
+ +
+ +
+
+
+
+

A relation field can also reference its own model, in this case the relation is called a self-relation. Self-relations can be of any cardinality, 1-1, 1-n and m-n.

+
+

一对一

1
2
3
4
5
6
7
model User {
id Int @id @default(autoincrement())
name String?
successorId Int? @unique
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}
+ +

User 展现了这样一个模型:

+
    +
  • User 可以有一个或零个前驱(predecessor)
  • +
  • User 可以有一个或零个后继(successor)
  • +
+

注意:不能要求前驱和后继都必须存在,这两个必须有一个是可选的,否则没办法创建第一个 User。

+

要创建一对一的 self-relation:

+
    +
  • 关系的两端都必须定义一个共享相同名称的 @relation 属性(BlogOwnerHistory)
  • +
  • 关系字段必须是完全注释的。例如 successor 字段需要定义 field 和 references 参数。
  • +
  • 关系字段必须由外键支持。successor 字段由 successorId 外键提供支持,该外键引用 id 字段中的值。successorId 还需要 @unique 属性来保证一对一的关系。****
  • +
+
+

一对一的 self-relation 需要两个端点,即使这两个端点是同一条数据。

+
+

而在关系型数据库中,一对一的 self-relation 可以用如下 SQL 描述:

+
1
2
3
4
5
6
7
8
9
CREATE TABLE "User" (
id SERIAL PRIMARY KEY,
"name" TEXT,
"successorId" INTEGER
);

ALTER TABLE "User" ADD CONSTRAINT fk_successor_user FOREIGN KEY ("successorId") REFERENCES "User" (id);

ALTER TABLE "User" ADD CONSTRAINT successor_unique UNIQUE ("successorId");
+ +

一对多

1
2
3
4
5
6
7
model User {
id Int @id @default(autoincrement())
name String?
teacherId Int?
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
}
+ +

User 展现了这样一个模型:

+
    +
  • 一个 User 只能有零个或一个 teacher
  • +
  • 一个 User 可以有零个或多个 students
  • +
+
+

可以通过将 teacher 字段设为 required 来要求每个 User 都有一名 teacher。

+
+

用 SQL 描述 User model:

+
1
2
3
4
5
6
7
CREATE TABLE "User" (
id SERIAL PRIMARY KEY,
"name" TEXT,
"teacherId" INTEGER
);

ALTER TABLE "User" ADD CONSTRAINT fk_teacherid_user FOREIGN KEY ("teacherId") REFERENCES "User" (id);
+ +

teacherId 没有使用 UNIQUE 约束,这代表着多个 students 可以有同一个 teacher

+

多对多

1
2
3
4
5
6
model User {
id Int @id @default(autoincrement())
name String?
followedBy User[] @relation("UserFollows")
following User[] @relation("UserFollows")
}
+ +
    +
  • 一个 User 可以被零个或多个 Users 关注
  • +
  • 一个 User 可以关注零个或多个 Users
  • +
+
+

对于关系型数据库,多对多的关系是隐式的,这意味着 Prisma ORM 会在底层数据库中维护一个 relation table:
A relation table (also sometimes called a JOIN, link or pivot table) connects two or more other tables and therefore creates a relation between them. Creating relation tables is a common data modelling practice in SQL to represent relationships between different entities. In essence it means that “one m-n relation is modeled as two 1-n relations in the database”.

+

We recommend using implicit m-n-relations, where Prisma ORM automatically generates the relation table in the underlying database. Explicit m-n-relations should be used when you need to store additional data in the relations, such as the date the relation was created.

+
+

如果需要需要通过多对多的关系来保存其他字段,也可以创建显式的多对多 self 关系:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
model User {
id Int @id @default(autoincrement())
name String?
followedBy Follows[] @relation("followedBy")
following Follows[] @relation("following")
}

model Follows {
followedBy User @relation("followedBy", fields: [followedById], references: [id])
followedById Int
following User @relation("following", fields: [followingId], references: [id])
followingId Int

@@id([followingId, followedById])
}
+ +

在关系型数据库中,可以用如下 SQL 描述:

+
1
2
3
4
5
6
7
8
CREATE TABLE "User" (
id integer DEFAULT nextval('"User_id_seq"'::regclass) PRIMARY KEY,
name text
);
CREATE TABLE "_UserFollows" (
"A" integer NOT NULL REFERENCES "User"(id) ON DELETE CASCADE ON UPDATE CASCADE,
"B" integer NOT NULL REFERENCES "User"(id) ON DELETE CASCADE ON UPDATE CASCADE
);
+ +

在同一模型上建立多个 self-relations

1
2
3
4
5
6
7
8
9
model User {
id Int @id @default(autoincrement())
name String?
teacherId Int?
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
followedBy User[] @relation("UserFollows")
following User[] @relation("UserFollows")
}
+ +

REFS.

+ +
+ + + + + +
+ + + + + + + +
+ + +
+
+
+ + + +
+ + +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + + + + diff --git a/2025/02/19/Repository-pattern-in-Typescript/index.html b/2025/02/19/Repository-pattern-in-Typescript/index.html new file mode 100644 index 00000000..45e5ddc2 --- /dev/null +++ b/2025/02/19/Repository-pattern-in-Typescript/index.html @@ -0,0 +1,308 @@ + + + + + + + + + + + + + + + + + + + + + +Repository pattern in Typescript | 暮秋小屋 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+
+ + + +
+
+
+ + +
+
+
+ + +
+ +
+ +
+ +
+
+
+
+

The Repository design pattern is a structural pattern that abstracts data access, providing a centralized way to manage data operations. By separating the data layer from business logic, it enhances code maintainability, testability, and flexibility, making it easier to work with various data sources in an application.

+
+

抽象化数据访问层的主要作用是将应用的业务逻辑与对数据库的数据访问等实现细节进行解耦。DB 框架的更改不应该影响到应用程序的核心服务,并且它们应该对业务逻辑代码透明。

+

业务服务应该只依赖于抽象,而不是实现,数据服务同理。

+
+

Intro.

例如,此时有一个书籍存储的微服务,并且有一个“添加新书籍”的操作用例,在此用例中可以通过使用 Book 这个 Repository 来添加新书籍,而 Book 具体使用什么数据库,怎么存并不是业务需要关心的事情。

+

具体实现方式

假设有以下实体类型定义:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export class Author {
firstName: string;
lastName: string;
}

export class Genre {
name: string;
}

export class Book {
title: string;
author: Author;
genre: Genre;
publishDate: Date;
}
+ +

抽象化

第一步是抽象化出一个通用的 Repository ,其中包含通用的操作:

+
1
2
3
4
5
6
7
8
9
export abstract class IGenericRepository<T> {
abstract getAll(): Promise<T[]>;

abstract get(id: string): Promise<T>;

abstract create(item: T): Promise<T>;

abstract update(id: string, item: T);
}
+ +
    +
  • 这里具体有那些函数可以根据需要(业务)自己定义。
  • +
  • 类型参数 T 表示每个实体。
  • +
+

接着,定义一个数据服务,其中包含使用 IGenericRepositoiry 定义的具体实例对应的 _Repository_:

+
1
2
3
4
5
6
7
8
9
10
import { Author, Book, Genre } from '../entities';
import { IGenericRepository } from './generic-repository.abstract';

export abstract class IDataServices {
abstract authors: IGenericRepository<Author>;

abstract books: IGenericRepository<Book>;

abstract genres: IGenericRepository<Genre>;
}
+ +
    +
  • 这里有三个 Repository 分别对应不同的实体。
  • +
  • IGenericRepository 中定义的函数是每个 Repository 公开的通用存储函数。
  • +
+

以上这些就是对业务中的实体的 Repository 抽象,这样就隔离开了业务和存储逻辑,例如使用 MongoDB,可以实现一个 MongoGenericRepository:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { Model } from 'mongoose';
import { IGenericRepository } from '../../../core';

export class MongoGenericRepository<T> implements IGenericRepository<T> {
private _repository: Model<T>;
private _populateOnFind: string[];

constructor(repository: Model<T>, populateOnFind: string[] = []) {
this._repository = repository;
this._populateOnFind = populateOnFind;
}

getAll(): Promise<T[]> {
return this._repository.find().populate(this._populateOnFind).exec();
}

get(id: any): Promise<T> {
return this._repository.findById(id).populate(this._populateOnFind).exec();
}

create(item: T): Promise<T> {
return this._repository.create(item);
}

update(id: string, item: T) {
return this._repository.findByIdAndUpdate(id, item);
}
}
+ +

然后实现一个 MongoDataServices:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { Injectable, OnApplicationBootstrap } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { IDataServices } from '../../../core';
import { MongoGenericRepository } from './mongo-generic-repository';
import {
Author,
AuthorDocument,
Book,
BookDocument,
Genre,
GenreDocument,
} from './model';

@Injectable()
export class MongoDataServices
implements IDataServices, OnApplicationBootstrap
{
authors: MongoGenericRepository<Author>;
books: MongoGenericRepository<Book>;
genres: MongoGenericRepository<Genre>;

constructor(
@InjectModel(Author.name)
private AuthorRepository: Model<AuthorDocument>,
@InjectModel(Book.name)
private BookRepository: Model<BookDocument>,
@InjectModel(Genre.name)
private GenreRepository: Model<GenreDocument>,
) {}

onApplicationBootstrap() {
this.authors = new MongoGenericRepository<Author>(this.AuthorRepository);
this.books = new MongoGenericRepository<Book>(this.BookRepository, [
'author',
'genre',
]);
this.genres = new MongoGenericRepository<Genre>(this.GenreRepository);
}
}
+ +

以上。

+ +
+ + + + + +
+ + + + + + + +
+ + +
+
+
+ + + +
+ + +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + + + + -- cgit v1.2.3