mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-30 07:39:06 +08:00
docs(README): enhance foreign key documentation with usage examples
- Added sections on basic usage of foreign keys, self-referencing foreign keys, and circular foreign key references. - Provided TypeScript code examples to illustrate best practices and avoid common pitfalls. - Explained the rationale behind using soft references in SQLite for improved data integrity and simplified operations.
This commit is contained in:
parent
9c47937714
commit
44b85fa661
@ -88,6 +88,8 @@ Drizzle handles JSON serialization/deserialization automatically.
|
||||
|
||||
## Foreign Keys
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
// SET NULL: preserve record when referenced record is deleted
|
||||
groupId: text().references(() => groupTable.id, { onDelete: 'set null' })
|
||||
@ -96,6 +98,69 @@ groupId: text().references(() => groupTable.id, { onDelete: 'set null' })
|
||||
topicId: text().references(() => topicTable.id, { onDelete: 'cascade' })
|
||||
```
|
||||
|
||||
### Self-Referencing Foreign Keys
|
||||
|
||||
For self-referencing foreign keys (e.g., tree structures with parentId), **always use the `foreignKey` operator** in the table's third parameter:
|
||||
|
||||
```typescript
|
||||
import { foreignKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
export const messageTable = sqliteTable(
|
||||
'message',
|
||||
{
|
||||
id: uuidPrimaryKeyOrdered(),
|
||||
parentId: text(), // Do NOT use .references() here
|
||||
// ...other fields
|
||||
},
|
||||
(t) => [
|
||||
// Use foreignKey operator for self-referencing
|
||||
foreignKey({ columns: [t.parentId], foreignColumns: [t.id] }).onDelete('set null')
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
**Why this approach:**
|
||||
- Avoids TypeScript circular reference issues (no need for `AnySQLiteColumn` type annotation)
|
||||
- More explicit and readable
|
||||
- Allows chaining `.onDelete()` / `.onUpdate()` actions
|
||||
|
||||
### Circular Foreign Key References
|
||||
|
||||
**Avoid circular foreign key references between tables.** For example:
|
||||
|
||||
```typescript
|
||||
// ❌ BAD: Circular FK between tables
|
||||
// tableA.currentItemId -> tableB.id
|
||||
// tableB.ownerId -> tableA.id
|
||||
```
|
||||
|
||||
If you encounter a scenario that seems to require circular references:
|
||||
|
||||
1. **Identify which relationship is "weaker"** - typically the one that can be null or is less critical for data integrity
|
||||
2. **Remove the FK constraint from the weaker side** - let the application layer handle validation and consistency (this is known as "soft references" pattern)
|
||||
3. **Document the application-layer constraint** in code comments
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Break the cycle by handling one side at application layer
|
||||
export const topicTable = sqliteTable('topic', {
|
||||
id: uuidPrimaryKey(),
|
||||
// Application-managed reference (no FK constraint)
|
||||
// Validated by TopicService.setCurrentMessage()
|
||||
currentMessageId: text(),
|
||||
})
|
||||
|
||||
export const messageTable = sqliteTable('message', {
|
||||
id: uuidPrimaryKeyOrdered(),
|
||||
// Database-enforced FK
|
||||
topicId: text().references(() => topicTable.id, { onDelete: 'cascade' }),
|
||||
})
|
||||
```
|
||||
|
||||
**Why soft references for SQLite:**
|
||||
- SQLite does not support `DEFERRABLE` constraints (unlike PostgreSQL/Oracle)
|
||||
- Application-layer validation provides equivalent data integrity
|
||||
- Simplifies insert/update operations without transaction ordering concerns
|
||||
|
||||
## Migrations
|
||||
|
||||
Generate migrations after schema changes:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user