From 1c996075e3f4d24c30f54bcc73b3fdbd82c7c510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Happi=20=28=E5=93=88=E6=AF=94=29?= Date: Tue, 10 Mar 2026 12:41:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0upsert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repositories/user_repository_impl.dart | 20 ++++++------- .../database_repository_impl.dart | 24 +++++++++++++++ .../repositories/database_repository.dart | 30 +++++++++++++++++-- .../presentation/facade/storage_sdk_api.dart | 24 +++++++++++++-- .../wiring/storage_sdk_api_impl.dart | 8 +++++ 5 files changed, 91 insertions(+), 15 deletions(-) diff --git a/apps/im_app/lib/data/repositories/user_repository_impl.dart b/apps/im_app/lib/data/repositories/user_repository_impl.dart index b32f153..e42f00f 100644 --- a/apps/im_app/lib/data/repositories/user_repository_impl.dart +++ b/apps/im_app/lib/data/repositories/user_repository_impl.dart @@ -282,8 +282,10 @@ class UserRepositoryImpl implements UserRepository { @override Future insertOrReplaceUser(User user) async { if (user.requireUpsert) { - await _storage.insertOrReplace(_toCompanion(user)); + await upsertUser(user); } + + await _storage.insertOrReplace(_toCompanion(user)); } @override @@ -331,7 +333,9 @@ class UserRepositoryImpl implements UserRepository { /// /// 内部委托给 [upsertUsers]。 @override - Future upsertUser(User user) => upsertUsers([user]); + Future upsertUser(User user) async { + await _storage.insertOnConflictUpdate(_toPartialCompanion(user)); + } /// 批量 upsert:不存在则插入,存在则仅更新非 null 字段。 /// @@ -343,15 +347,9 @@ class UserRepositoryImpl implements UserRepository { /// 适用场景:从服务端收到部分用户信息,不希望覆盖本地已有的其他字段。 @override Future upsertUsers(List users) async { - await _storage.transaction(() async { - for (final user in users) { - await _storage.insert(_toCompanion(user)); - await _storage.updateWhere( - _toPartialCompanion(user), - (t) => t.uid.equals(user.uid), - ); - } - }); + await _storage.batchInsertOnConflictUpdate( + users.map(_toPartialCompanion).toList(), + ); } // ── 删除 ───────────────────────────────────────────────────────────────── diff --git a/packages/storage_sdk/lib/src/data/repositories/database_repository_impl.dart b/packages/storage_sdk/lib/src/data/repositories/database_repository_impl.dart index 41cbfde..7c71440 100644 --- a/packages/storage_sdk/lib/src/data/repositories/database_repository_impl.dart +++ b/packages/storage_sdk/lib/src/data/repositories/database_repository_impl.dart @@ -49,6 +49,30 @@ class DatabaseRepositoryImpl implements DatabaseRepository { ); } + @override + Future insertOnConflictUpdate( + TableInfo table, + Insertable companion, + ) async { + final db = _db; + if (db == null) return; + await db.into(table).insertOnConflictUpdate(companion); + } + + @override + Future batchInsertOnConflictUpdate( + TableInfo table, + List> companions, + ) async { + final db = _db; + if (db == null) return; + await db.transaction(() async { + for (final companion in companions) { + await db.into(table).insertOnConflictUpdate(companion); + } + }); + } + // ── 更新 ───────────────────────────────────────────────────────────────── @override diff --git a/packages/storage_sdk/lib/src/domain/repositories/database_repository.dart b/packages/storage_sdk/lib/src/domain/repositories/database_repository.dart index 4e8ec1e..228b818 100644 --- a/packages/storage_sdk/lib/src/domain/repositories/database_repository.dart +++ b/packages/storage_sdk/lib/src/domain/repositories/database_repository.dart @@ -13,7 +13,10 @@ import 'package:drift/drift.dart'; abstract class DatabaseRepository { // ── 插入 ───────────────────────────────────────────────────────────────── - /// 插入或替换(主键冲突时覆盖)。 + /// 插入或替换(主键冲突时删除原行再插入)。 + /// + /// 注意:底层是 DELETE + INSERT,rowid 变更,触发 DELETE 级联。 + /// 如需原地更新,请使用 [insertOnConflictUpdate]。 Future insertOrReplace( TableInfo table, Insertable companion, @@ -25,12 +28,35 @@ abstract class DatabaseRepository { Insertable companion, ); - /// 批量插入或替换。 + /// 批量插入或替换(主键冲突时删除原行再插入)。 + /// + /// 注意:底层是 DELETE + INSERT,rowid 变更,触发 DELETE 级联。 + /// 如需原地更新,请使用 [batchInsertOnConflictUpdate]。 Future batchInsertOrReplace( TableInfo table, List> companions, ); + /// 插入,主键冲突时原地更新全部字段(INSERT ... ON CONFLICT DO UPDATE SET)。 + /// + /// 与 [insertOrReplace] 的区别: + /// - [insertOrReplace] → DELETE + INSERT,rowid 变更,触发 DELETE 级联 + /// - [insertOnConflictUpdate] → 原地 UPDATE,rowid 不变,不触发 DELETE 级联 + /// + /// 适用场景:upsert 语义,且不希望影响依赖 rowid 的其他逻辑。 + Future insertOnConflictUpdate( + TableInfo table, + Insertable companion, + ); + + /// 批量插入,主键冲突时原地更新全部字段。 + /// + /// 与 [batchInsertOrReplace] 的区别同 [insertOnConflictUpdate]。 + Future batchInsertOnConflictUpdate( + TableInfo table, + List> companions, + ); + // ── 更新 ───────────────────────────────────────────────────────────────── /// 按条件更新。 diff --git a/packages/storage_sdk/lib/src/presentation/facade/storage_sdk_api.dart b/packages/storage_sdk/lib/src/presentation/facade/storage_sdk_api.dart index d6e6861..17b3873 100644 --- a/packages/storage_sdk/lib/src/presentation/facade/storage_sdk_api.dart +++ b/packages/storage_sdk/lib/src/presentation/facade/storage_sdk_api.dart @@ -79,7 +79,10 @@ abstract class StorageSdkApi { // ── 插入 ───────────────────────────────────────────────────────────────── - /// 插入或替换(主键冲突时覆盖)。 + /// 插入或替换(主键冲突时删除原行再插入)。 + /// + /// 注意:底层是 DELETE + INSERT,会导致 rowid 变更,并触发 DELETE 级联。 + /// 如需原地更新,请使用 [insertOnConflictUpdate]。 /// /// 示例: /// ```dart @@ -92,9 +95,26 @@ abstract class StorageSdkApi { /// 插入或忽略(主键冲突时跳过)。 Future insert(Insertable companion); - /// 批量插入或替换。 + /// 批量插入或替换(主键冲突时删除原行再插入)。 + /// + /// 注意:底层是 DELETE + INSERT,rowid 变更,触发 DELETE 级联。 + /// 如需原地更新,请使用 [batchInsertOnConflictUpdate]。 Future batchInsertOrReplace(List> companions); + /// 插入,主键冲突时原地更新全部字段(INSERT ... ON CONFLICT DO UPDATE SET)。 + /// + /// 与 [insertOrReplace] 的区别: + /// - [insertOrReplace] → DELETE + INSERT,rowid 变更,触发 DELETE 级联 + /// - [insertOnConflictUpdate] → 原地 UPDATE,rowid 不变,不触发 DELETE 级联 + /// + /// 适用场景:upsert 语义,且不希望影响依赖 rowid 的其他逻辑。 + Future insertOnConflictUpdate(Insertable companion); + + /// 批量插入,主键冲突时原地更新全部字段。 + /// + /// 与 [batchInsertOrReplace] 的区别同 [insertOnConflictUpdate]。 + Future batchInsertOnConflictUpdate(List> companions); + // ── 更新 ───────────────────────────────────────────────────────────────── /// 按条件更新。 diff --git a/packages/storage_sdk/lib/src/presentation/wiring/storage_sdk_api_impl.dart b/packages/storage_sdk/lib/src/presentation/wiring/storage_sdk_api_impl.dart index a5e8cba..ab5b118 100644 --- a/packages/storage_sdk/lib/src/presentation/wiring/storage_sdk_api_impl.dart +++ b/packages/storage_sdk/lib/src/presentation/wiring/storage_sdk_api_impl.dart @@ -67,6 +67,14 @@ class StorageSdkApiImpl implements StorageSdkApi, StorageSdkLifecycle { Future batchInsertOrReplace(List> companions) => _core.repo.batchInsertOrReplace(_tableFor(), companions); + @override + Future insertOnConflictUpdate(Insertable companion) => + _core.repo.insertOnConflictUpdate(_tableFor(), companion); + + @override + Future batchInsertOnConflictUpdate(List> companions) => + _core.repo.batchInsertOnConflictUpdate(_tableFor(), companions); + // ── 更新 ───────────────────────────────────────────────────────────────── @override