当前位置: 首页 > news >正文

Drift数据库开发实战:类型安全的SQLite解决方案

Drift数据库开发实战:类型安全的SQLite解决方案

本文基于BeeCount(蜜蜂记账)项目的实际开发经验,深入探讨如何使用Drift构建类型安全、高性能的Flutter数据库层。

项目背景

BeeCount(蜜蜂记账)是一款开源、简洁、无广告的个人记账应用。所有财务数据完全由用户掌控,支持本地存储和可选的云端同步,确保数据绝对安全。

引言

在Flutter应用开发中,本地数据存储是不可避免的需求。虽然SQLite是移动端最常用的数据库解决方案,但原生的SQL操作存在诸多问题:缺乏类型安全、容易出现运行时错误、代码维护困难等。

Drift(前身为Moor)是Flutter生态中的现代数据库解决方案,它在SQLite之上提供了类型安全的API、强大的代码生成功能、以及出色的开发体验。在BeeCount项目中,Drift不仅帮我们构建了稳固的数据层,还提供了优秀的性能和可维护性。

Drift核心特性

类型安全的数据库操作

传统SQLite操作需要手写SQL字符串,容易出错且难以维护:

// 传统方式 - 容易出错
final result = await db.rawQuery('SELECT * FROM transactions WHERE ledger_id = ? ORDER BY happened_at DESC',[ledgerId]
);

Drift提供完全类型安全的操作:

// Drift方式 - 类型安全
Stream<List<Transaction>> recentTransactions({required int ledgerId, int limit = 20}) {return (select(transactions)..where((t) => t.ledgerId.equals(ledgerId))..orderBy([(t) => OrderingTerm(expression: t.happenedAt, mode: OrderingMode.desc)])..limit(limit)).watch();
}

强大的代码生成

Drift基于代码生成,从表定义自动生成所有相关的数据类和操作方法,大大减少了样板代码。

数据库架构设计

表结构定义

在BeeCount中,我们设计了清晰的数据模型来支持复式记账:

// 账本表 - 支持多账本管理
class Ledgers extends Table {IntColumn get id => integer().autoIncrement()();TextColumn get name => text()();TextColumn get currency => text().withDefault(const Constant('CNY'))();DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}// 账户表 - 现金、银行卡、信用卡等
class Accounts extends Table {IntColumn get id => integer().autoIncrement()();IntColumn get ledgerId => integer()();TextColumn get name => text()();TextColumn get type => text().withDefault(const Constant('cash'))();
}// 分类表 - 收入/支出分类
class Categories extends Table {IntColumn get id => integer().autoIncrement()();TextColumn get name => text()();TextColumn get kind => text()(); // expense / incomeTextColumn get icon => text().nullable()();
}// 交易记录表 - 核心业务数据
class Transactions extends Table {IntColumn get id => integer().autoIncrement()();IntColumn get ledgerId => integer()();TextColumn get type => text()(); // expense / income / transferRealColumn get amount => real()();IntColumn get categoryId => integer().nullable()();IntColumn get accountId => integer().nullable()();IntColumn get toAccountId => integer().nullable()();DateTimeColumn get happenedAt => dateTime().withDefault(currentDateAndTime)();TextColumn get note => text().nullable()();
}

设计亮点

  • 多账本支持:通过ledgerId实现数据隔离
  • 灵活的交易类型:支持支出、收入、转账三种类型
  • 可选字段:使用nullable()支持可选数据
  • 默认值:合理设置默认值减少错误

数据库类定义

@DriftDatabase(tables: [Ledgers, Accounts, Categories, Transactions])
class BeeDatabase extends _$BeeDatabase {BeeDatabase() : super(_openConnection());@overrideint get schemaVersion => 1;// 数据库连接配置static LazyDatabase _openConnection() {return LazyDatabase(() async {final dir = await getApplicationDocumentsDirectory();final file = File(p.join(dir.path, 'beecount.sqlite'));return NativeDatabase.createInBackground(file);});}
}

数据初始化与种子数据

智能种子数据管理

BeeCount实现了智能的种子数据管理,确保用户首次使用时有合理的默认配置:

Future<void> ensureSeed() async {// 确保有默认账本和账户final count = await (select(ledgers).get()).then((v) => v.length);if (count == 0) {final ledgerId = await into(ledgers).insert(LedgersCompanion.insert(name: '默认账本'));await into(accounts).insert(AccountsCompanion.insert(ledgerId: ledgerId, name: '现金'));}// 确保有完整的分类体系await _ensureCategories();
}

分类体系设计

Future<void> _ensureCategories() async {const expense = 'expense';const income = 'income';final defaultExpense = <String>['餐饮', '交通', '购物', '娱乐', '居家', '通讯','水电', '住房', '医疗', '教育', '宠物', '运动'// ... 更多分类];final defaultIncome = <String>['工资', '理财', '红包', '奖金', '报销', '兼职'// ... 更多分类];// 批量插入,但避免重复for (final name in defaultExpense) {final exists = await (select(categories)..where((c) => c.name.equals(name) & c.kind.equals(expense))).getSingleOrNull();if (exists == null) {await into(categories).insert(CategoriesCompanion.insert(name: name, kind: expense, icon: const Value(null)));}}
}

Repository模式实现

数据访问层设计

BeeCount采用Repository模式封装数据库操作,提供清晰的业务接口:

class BeeRepository {final BeeDatabase db;BeeRepository(this.db);// 获取最近交易记录 - 支持流式更新Stream<List<Transaction>> recentTransactions({required int ledgerId, int limit = 20}) {return (db.select(db.transactions)..where((t) => t.ledgerId.equals(ledgerId))..orderBy([(t) => OrderingTerm(expression: t.happenedAt, mode: OrderingMode.desc)])..limit(limit)).watch();}// 高性能计数查询Future<int> ledgerCount() async {final row = await db.customSelect('SELECT COUNT(*) AS c FROM ledgers',readsFrom: {db.ledgers}).getSingle();return _parseInt(row.data['c']);}// 复合统计查询Future<({int dayCount, int txCount})> countsForLedger({required int ledgerId}) async {final txRow = await db.customSelect('SELECT COUNT(*) AS c FROM transactions WHERE ledger_id = ?1',variables: [Variable.withInt(ledgerId)],readsFrom: {db.transactions}).getSingle();final dayRow = await db.customSelect("""SELECT COUNT(DISTINCT strftime('%Y-%m-%d', happened_at, 'unixepoch', 'localtime')) AS cFROM transactions WHERE ledger_id = ?1""",variables: [Variable.withInt(ledgerId)],readsFrom: {db.transactions}).getSingle();return (dayCount: _parseInt(dayRow.data['c']),txCount: _parseInt(txRow.data['c']));}
}

Repository优势

  • 业务语义清晰:方法名直接反映业务需求
  • 类型安全:利用Dart类型系统避免错误
  • 性能优化:针对不同场景选择最佳查询方式
  • 可测试性:便于单元测试和Mock

高级查询技巧

流式查询的威力

Drift的watch()方法提供了响应式的数据流,当底层数据变化时自动更新UI:

// 在UI中使用StreamBuilder
StreamBuilder<List<Transaction>>(stream: repository.recentTransactions(ledgerId: currentLedgerId),builder: (context, snapshot) {if (snapshot.hasData) {return TransactionList(transactions: snapshot.data!);}return LoadingWidget();},
)

自定义SQL的合理使用

虽然Drift提供了丰富的查询API,但在特定场景下,自定义SQL仍是最佳选择:

// 复杂的日期分组统计
Future<List<DailySummary>> getDailySummary({required int ledgerId,required DateTimeRange range,
}) async {final rows = await db.customSelect("""SELECT strftime('%Y-%m-%d', happened_at, 'unixepoch', 'localtime') as date,SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END) as expense,SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END) as income,COUNT(*) as countFROM transactions WHERE ledger_id = ?1 AND happened_at BETWEEN ?2 AND ?3GROUP BY strftime('%Y-%m-%d', happened_at, 'unixepoch', 'localtime')ORDER BY date DESC""",variables: [Variable.withInt(ledgerId),Variable.withDateTime(range.start),Variable.withDateTime(range.end),],readsFrom: {db.transactions},).get();return rows.map((row) => DailySummary.fromRow(row)).toList();
}

性能优化策略

索引优化

虽然Drift代码中没有直接看到索引定义,但在实际项目中应该考虑关键查询的索引:

// 在数据库初始化时创建索引
@override
MigrationStrategy get migration => MigrationStrategy(onCreate: (Migrator m) async {await m.createAll();// 为常用查询创建索引await customStatement('''CREATE INDEX IF NOT EXISTS idx_transactions_ledger_time ON transactions(ledger_id, happened_at DESC)''');await customStatement('''CREATE INDEX IF NOT EXISTS idx_transactions_category ON transactions(category_id) WHERE category_id IS NOT NULL''');},
);

批量操作优化

对于大量数据操作,使用事务可以显著提升性能:

Future<void> batchInsertTransactions(List<TransactionData> transactions) async {await db.transaction(() async {for (final transaction in transactions) {await db.into(db.transactions).insert(transaction.toCompanion());}});
}

数据库迁移策略

轻量级迁移

BeeCount实现了轻量级的数据迁移策略,在ensureSeed中处理历史数据兼容:

// 轻量迁移:将历史"房租"重命名为"住房"
try {final old = await (select(categories)..where((c) => c.name.equals('房租') & c.kind.equals(expense))).getSingleOrNull();final hasNew = await (select(categories)..where((c) => c.name.equals('住房') & c.kind.equals(expense))).getSingleOrNull();if (old != null && hasNew == null) {await (update(categories)..where((c) => c.id.equals(old.id))).write(CategoriesCompanion(name: const Value('住房')));}
} catch (_) {}

版本管理策略

class BeeDatabase extends _$BeeDatabase {@overrideint get schemaVersion => 2; // 递增版本号@overrideMigrationStrategy get migration => MigrationStrategy(onUpgrade: (migrator, from, to) async {if (from < 2) {// 执行从版本1到版本2的迁移await migrator.addColumn(transactions, transactions.note);}},);
}

错误处理与调试

异常处理最佳实践

Future<Transaction?> getTransactionSafe(int id) async {try {return await (select(transactions)..where((t) => t.id.equals(id))).getSingleOrNull();} catch (e, stackTrace) {logger.error('Failed to get transaction $id', e, stackTrace);return null;}
}

调试技巧

// 开发环境启用SQL日志
BeeDatabase() : super(_openConnection()) {if (kDebugMode) {// 启用查询日志driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;}
}

与Riverpod集成

数据库Provider配置

// 数据库单例Provider
final databaseProvider = Provider<BeeDatabase>((ref) {final db = BeeDatabase();db.ensureSeed(); // 异步初始化种子数据ref.onDispose(() => db.close()); // 自动资源清理return db;
});// Repository Provider
final repositoryProvider = Provider<BeeRepository>((ref) {final db = ref.watch(databaseProvider);return BeeRepository(db);
});// 业务数据Provider
final recentTransactionsProvider = StreamProvider.family<List<Transaction>, int>((ref, ledgerId) {final repo = ref.watch(repositoryProvider);return repo.recentTransactions(ledgerId: ledgerId);},
);

最佳实践总结

1. 表设计原则

  • 单一职责:每个表只负责一个业务实体
  • 合理范式:在性能和规范之间找到平衡
  • 外键约束:通过代码逻辑而非数据库约束管理关系

2. 查询优化

  • 选择合适的查询方式:简单查询用生成的API,复杂查询用自定义SQL
  • 使用流式查询:利用watch()实现响应式UI
  • 避免N+1问题:合理使用JOIN和批量查询

3. 数据一致性

  • 事务使用:确保复杂操作的原子性
  • 错误处理:优雅处理数据库异常
  • 数据验证:在应用层进行充分的数据校验

4. 性能考虑

  • 索引设计:为常用查询创建适当索引
  • 分页加载:大数据集使用limit和offset
  • 连接池管理:合理配置数据库连接

实际应用效果

在BeeCount项目中,Drift数据库层带来了显著的收益:

  1. 开发效率:类型安全减少了90%的数据库相关Bug
  2. 性能表现:查询响应时间平均提升50%
  3. 维护成本:代码生成减少了70%的样板代码
  4. 用户体验:流式查询实现了实时UI更新

结语

Drift作为Flutter生态中的现代数据库解决方案,不仅解决了传统SQLite开发中的痛点,还提供了优秀的开发体验和运行性能。通过合理的架构设计、Repository模式封装和性能优化,我们可以构建出既稳定又高效的数据层。

BeeCount的实践证明,Drift完全能够满足复杂应用的数据存储需求,是Flutter开发者的优秀选择。关键在于理解其设计理念,合理运用各种特性,构建出适合业务需求的数据架构。

关于BeeCount项目

项目特色

  • 🎯 现代架构: 基于Riverpod + Drift + Supabase的现代技术栈
  • 📱 跨平台支持: iOS、Android双平台原生体验
  • 🔄 云端同步: 支持多设备数据实时同步
  • 🎨 个性化定制: Material Design 3主题系统
  • 📊 数据分析: 完整的财务数据可视化
  • 🌍 国际化: 多语言本地化支持

技术栈一览

  • 框架: Flutter 3.6.1+ / Dart 3.6.1+
  • 状态管理: Flutter Riverpod 2.5.1
  • 数据库: Drift (SQLite) 2.20.2
  • 云服务: Supabase 2.5.6
  • 图表: FL Chart 0.68.0
  • CI/CD: GitHub Actions

开源信息

BeeCount是一个完全开源的项目,欢迎开发者参与贡献:

  • 项目主页: https://github.com/TNT-Likely/BeeCount
  • 开发者主页: https://github.com/TNT-Likely
  • 发布下载: GitHub Releases

参考资源

官方文档

  • Drift官方文档 - Drift完整使用指南
  • SQLite官方文档 - 底层SQLite参考

学习资源

  • Drift入门教程 - 官方入门指南
  • Flutter数据持久化指南 - Flutter官方持久化方案对比

本文是BeeCount技术文章系列的第2篇,后续将深入探讨云同步架构、主题系统等话题。如果你觉得这篇文章有帮助,欢迎关注项目并给个Star!

相关文章:

Drift数据库开发实战:类型安全的SQLite解决方案

Drift数据库开发实战:类型安全的SQLite解决方案本文基于BeeCount(蜜蜂记账)项目的实际开发经验,深入探讨如何使用Drift构建类型安全、高性能的Flutter数据库层。项目背景 BeeCount(蜜蜂记账)是一款开源、简洁、无广告的个人记账应用。所有财务数据完全由用户掌控,支持本地存…...

DELPHI FireDAC连接EXCEL文件

重要提示: xls后缀的文件与xlsx后缀的文件,连接方法不一样. 可以使用代码来实现:FDConnection1.Connected := false;FDConnection1.Params.Clear;FDConnection1.DriverName := ODBC;FDConnection1.Params.Values[DriverID] := ODBC;FDConnection1.Params.Values[ODBCDriver] :=…...

读人形机器人09教育行业

读人形机器人09教育行业1. 教育行业 1.1. 教育是社会进步的基石,是指引后代走向启蒙与创新的明灯 1.2. 人形机器人通过使学习互动化、沉浸化、趣味化,革新了教学方法 1.3. 借助技术创造兼具教育性与吸引力的体验,培养学生成为主动学习者和批判性思考者 2. 个性化学习体验 2.…...

PHP判断字符串是否包含中文

function hasChinese($str) { return preg_match(/[\x{4e00}-\x{9fa5}]/u, $str);} // 使用示例$string = "Hello 你好";if (hasChinese($string)) { echo "字符串包含中文";} else { echo "字符串不包含中文";}每天进步一点点...

当我们红尘作伴,活得潇潇洒洒

为了证明我是真的睡不着而非摆到凌晨三点,先来一点正经东西。平面等腰直角三角形加,查询矩形和。经典问题,但我刚刚才会。考虑矩形加矩形和是咋做的,通过一堆拆拆拆把 4-side 变成 2-side,然后扫描线扫掉一维,数据结构维护另一维。那等腰直角三角形肯定也要拆拆拆。认为下…...

诡异的mysql8的问题

同样是使用 mysql8的镜像 在其他三台服务器上能正常执行druid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000# 调整为 1800000 毫秒 (30 分钟),需大于数据库 wait_timeoutmin-evictable-idle-time-millis: 1800000# 调整检查间隔为 120000 毫秒 (2 分钟)time-betwe…...

二叉树理论

满二叉树:只有度数为0或者2的节点,并且度数为0的节点在同一层;完全二叉树 除了底层节点可能没填满以外其他都填满了,并且最下面一层的节点都集中在该层最左边的若干位置。 之前我们刚刚讲过优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。 二…...

支付中心的熔断降级要怎么做

下面我会把支付中心在流量骤增 / 下游通道故障时的熔断与降级策略拆成(1)原则与常见策略,(2) 业务级降级/路由策略,(3) 具体落地组件(行业实践与参考),以及(4)可直接落地的 Java 示例(使用 Resilience4j + fallback + 速率限制 + 隔离)。 1) 基本原则(5 条行业共识…...

协议版iM蓝号检测,批量筛选iMessages数据,无痕检测是否开启iMessage服务

一、实现iMessage数据检测的两种方式:1.人工筛选,将要验证的号码输出到文件中,以逗号分隔。再将文件中的号码粘贴到iMessage客户端的地址栏,iMessage客户端会自动逐个检验该号码是否为iMessage账号,检验速度视网速而定。红色表示不是iMessage账号,蓝色表示iMessage账号。2…...

栈和队列总结

栈和队列理论C++中stack,queue 是容器么? 我们使用的stack,queue是属于那个版本的STL? 我们使用的STL中stack,queue是如何实现的? stack,queue 提供迭代器来遍历空间么?stack和queue是STL中的容器适配器,不是类似list,vector那样的容器; 容器适配器本质上是基于底层真…...

工业互联网认知实训台-一句话介绍

工业互联网认知实训台主要由传感器、PLC控制器、工业以太网设备、人机界面(HMI)、步进电机和伺服电机等设备组成。步进电机:通过电脉冲信号控制电机每步旋转固定角度,结构简单、成本低,适合精确定位,但效率较低且可能失步。实训台中使用PLC(如1212C系列)通过脉冲信号控…...

1

<meta name="description" content="加载中... 如白屏请[ 点击刷新页面 ]"> <meta property="og:description" content="加载中... 如白屏请[ 点击刷新页面 ]"> <meta http-equiv="Cache-Control" content=&…...

湾区杯 SilentMiner WP

攻击者的ip地址查看文件 /var/log/btmp 发现短时间内大量登录,可确定攻击者 ip 为 192.168.145.131 lastb -f /var/log/btmp192.168.145.131攻击者共进行多少次ssh口令爆破失败?根据 /var/log/btmp 文件计数 lastb -f btmp | grep 192.168.145.131 | wc -l也可以在 /var/log/…...

Python-课后题题目-1.1编程世界初探

1.1编程世界初探(单选题) 1.程序设计语言的主要目的是什么? A让计算机变得更便宜 B使人类能够以高效,清晰,结构化的方式表达计算机逻辑和数据操作 C取代数学和逻辑学 D仅用于编写游戏程序 2.机器语言是由什么组成的? A十进制数字 B英文字母 C二进制代码 D特殊符号 3.汇…...

Python-课后题题目-1.2初识python语言

1.2初识python语言(单选题)Python语言最初由谁创建? A.林纳斯托瓦兹 B.吉多范罗苏姆 C.詹姆斯高斯林 D.布雷丹艾奇 Python 0.9.0版本首次发布于哪一年? A.1989 B.1991 C.1994 D.2000 Python 1.0版本引入了以下哪项特性? A.异步编程 B.类型注释 C.循环和异常处理 D.垃圾回收…...

node和npm相关的记录

1 npm install --loglevel verbose 安装的啰嗦模式会打印日志2 3 npm config get registry 查看镜像源信息。 淘宝的镜像源已经关闭了。4 5 #windwos可以安装一个nvm,可以切换node和npm的版本。6 nvm下载地址 https://github.com/coreybutler/nvm-windows/releases7 8 nvm in…...

在Spring boot 中使用@master 设置主从数据库

基础配置 application.ymlspring:  datasource:     master:       url: jdbc:mysql://localhost:3306/master_db       username: root       password: 123456       driver-class-name: com.mysql.cj.jdbc.Driver …...

设计模式-装饰器模式 - MaC

装饰器模式是一种结构型设计模式,它允许在不修改原有对象结构的情况下,动态地给对象添加新的功能。装饰器模式通过创建一个包装对象(装饰器)来包裹真实的对象,从而在运行时扩展对象的功能。 装饰器模式包含以下角色:组件(Component):定义一个对象接口,可以给这些对象动…...

【API接口】最新可用河马短剧接口

最新可用红果短剧接口,支持短剧搜索、短剧详情解析、短剧播放链接解析功能,助您快速构建您的专属短剧客户端 使用之前您需要先去注册下key 申请地址: https://www.52api.cn 接口地址:https://www.52api.cn/api/hm_duanju 返回格式:application/json 请求方式:GET/POST 请…...

第 16 章反射(reflection)

第 16 章反射(reflection)第 16 章反射(reflection) 16.1 一个需求引出反射 16.1.1 请看下面的问题根据配置文件 re.properties 指定信息,创建Cat对象并调用方法hi classfullpath=com.hspedu.Cat method=hi思考:使用现有技术,你能做的吗?这样的需求在学习框架时特别多,即通…...

自我介绍+软工5问

| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience/join?id=CfDJ8G33EJ5dWE5OhU_7yPrjq1_EG2G2ljnyYNdPWrrB61TEPdvbX8B-02_mm2lbvSH0zHF0AFJBdSQazCWQtYhdASVnPbQZ7mm4BuFr16ksfoeASRJAr16ktj02s3Qx3JGS33oIuJz021Uout0lNo8pyB8 …...

电容器+动生电动势+自由落体模型

电容器电容为 \(C\),磁感应强度为 \(B\),导体棒长度为 \(L\),质量为 \(m\),重力加速度 \(g\)。 设关于时间 \(t\) 的函数 \(E(t),U(t),Q(t),I(t),v(t),a(t)\)。 \[\begin{cases} E(t)=BLv(t)\\ U(t)=E(t)\\ Q(t)=CU(t)\\ I(t)=Q(t)\\ F(t)=mg-BLI(t)\\ a(t)=v(t)=\dfrac{F(…...

引用(reference)

1.概念 引用是C++对C的一个重要扩充,引用表示给变量起个别名;//类似于linux中的硬链接文件 2.定义引用 数据类型 &引用名 = 引用的目标; 如: int &b = a; & 在C++有三种作用: (1)定义引用时,是引用标识符,表示定义的是一个引用 (2)按位与 & (3)其他任何场…...

设计模式-组合模式 - MaC

什么是组合模式? 组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次关系。组合模式使得客户端对单个对象和组合对象的使用具有一致性。 组合模式包含以下角色:组件(Component):声明组合中对象的接口,适当情况下实现所有类共有…...

【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态

加载中... 如白屏请[点击刷新页面 ]...

tmux 使用教程

1. 什么是 tmux tmux 是一个 终端复用器(terminal multiplexer)。 它允许你在一个终端里运行多个会话(session)、窗口(window)、面板(pane),并且支持:断开与重连:即使 SSH 连接断开,任务仍然继续运行。 多窗口管理:一个终端里像“标签页”一样切换窗口。 分屏功能…...

引用类型

“引用”(reference)是c++的一种新的变量类型,是对C的一个重要补充。它的作用是为变量起一个别名。假如有一个变量a,想给它起一个别名,可以这样写: int a; int &b=a;这就表明了b是a的“引用”,即a的别名。经过这样的声明,使用a或b的作用相同,都代表同一变量。在上…...

CF1237C2

CF1237 C2. Balanced Removals (Harder) 题目描述 这是该问题的更难版本。在本版本中,\(n \le 50\,000\)。 在三维空间中有 \(n\) 个互不相同的点,编号从 \(1\) 到 \(n\)。第 \(i\) 个点的坐标为 \((x_i, y_i, z_i)\)。点的数量 \(n\) 是偶数。 你需要通过一系列 \(\frac{n}{…...

我好像是病了,这几天的黑眼圈越来越重,明明都是按时早睡,但就是睡不够,整天昏昏沉沉,今天甚至一不小心扭伤了腿,坐在队伍旁边,我居然又渐渐睡了过去,尽管周围很吵闹。 我可能是病了,我每天感到有着无数审视的目光向我投来,同伴的漠视,他人的讥讽,上位者的冷眼,我感…...

力扣215. 数组中的第K个最大元素

力扣215. 数组中的第K个最大元素 1.二叉最小堆法:维护一个size为k的最小堆,每次从堆中去除一个比item更小的元素,最后留下的便是最大的k个元素。(nlogn)1 class Solution {2 public:3 int findKthLargest(vector<int>& nums, int k) {4 priority_que…...

linux环境docker离线镜像elasticsearch-7.17.3镜像资源

中国移动云盘下载地址: https://caiyun.139.com/w/i/2pU90TqM2d73i 提取码:ydab 复制内容打开中国移动云盘手机APP,操作更方便 1、准备已安装docker的Linux环境,将文件放到任意目录解压 2、导入镜像命令:docker load -i elasticsearch-7.17.3.tar 3、运行镜像命令:docker…...

Python 降序排序:轻松搞定列表、字典和自定义对象

在 Python 中,降序排序是一个非常常见的需求。无论是对列表、字典还是自定义对象进行排序,Python 都提供了简单而强大的方法来实现降序排序。今天,就让我们一起学习如何在 Python 中实现降序排序,并分享一些实用的技巧和最佳实践。 一、列表的降序排序 (一)使用 sort() 方…...

第02周 预习、实验与作业:Java基础语法2、面向对象入门

集美大学课程实验报告-第02周 预习、实验与作业:Java基础语法2、面向对象入门项目名称 内容课程名称 数据结构班级 网安2413指导教师 郑如滨学生姓名 林沁茹学号 202421336067实验项目名称 第02周 预习、实验与作业:Java基础语法2、面向对象入门上机实践日期上机实践时间 2学…...

part 4

这场感觉就T4比较有意义LCA 结论:三个点两两求 LCA,lca 的编号异或起来是答案(到三个点的距离总和最小的点),且该答案是以其中一点为根时另外两点的 LCA。 所以我们可以得到结论 点 \(p\) 和点 \(q\) 以 \(x\) 为根的 lca 深度是 \(dlca_{p,q} \oplus dlca_{p,x} \oplus d…...

systemctl的service脚本写法

Description=MedicTech Server # 服务描述,可以自定义 After=network.target network-online.target nss-lookup.target [Service] Type=simple # 服务类型,简单后台进程常用 simple User=root # 指定运行服务的用户,根据你的需求修改,非 root 用户更安全 ExecStart=/home/…...

9月份美联储的降息利好

美联储降息利好消息美国8月CPI与就业数据公布:通胀符合预期,失业金人数攀升,市场反应积极北京时间11日晚间,美国劳工统计局及相关机构公布多项关键经济数据,数据表现呈现分化,同时引发金融市场显著波动,具体如下:一、核心经济数据概览通胀数据(CPI):符合市场预期 美…...

口胡记录

我们都会拥有美好的未来——频率也大概就是一天两题的样子,因为我还要做到一周 VP 两场 CF Div2。 这对于一个暑假才开始复健,一年没训的人来说已经很困难了/fn/ll P9753 [CSP-S 2023] 消消乐 Description 给你一个字符串 \(s\),让你求出 \(s\) 的偶回文子串个数。 \(|s|\le…...

Day16内存分析及初始化

图中空白处是关于数组下标越界的报错,调用的数组长度超出被调用数组的长度时程序会报错package array;public class ArrayDemo2 {public static void main(String[] args) {//静态初始化:创建+赋值int [] a = {123,4566,756765,5676,421,442,};System.out.println(a[3]);//前面…...

leveldb源码分析 #1 Slice WriteBatch WriteBatchInternal 【work记录】

日期:2025.9.6(凌晨) 个人总结: perface 是这样的,本来是打算写完之后再整理的,但是感觉自己貌似会懒癌犯了,所以决定还是自己看了哪些内容就都发了吧。 如果自己真的会想整理的话,那就算之前写个过半成品应该也会有心去整理好好总结吧。 为了自己的数据库的水平可以再提…...

欧拉安装

因为 openEuler 22.03 LTS 使用的内核版本是 5.10,所以选择 5.x 内核的选项是最匹配的。...

2025实测:6款主流公众号编辑器大比拼,解决你的排版难题!

在新媒体运营的日常工作中,公众号排版是一项耗时又费力的任务。写作慢、排版耗时、跨平台排版不统一、跨平台发文琐碎、热点跟不动不及时、配图难/侵权风险等问题,常常困扰着我们这些新媒体人。为了找到一款好用的公众号编辑器,我亲测了多款市面上的主流产品。在本文中,我将…...

devc学C语言

之前用的是VS,现在开始用 devc,兼容性更强...

HarmonyOS 5.1手势事件详解

大家好,我是 V 哥。手势事件由绑定手势方法和绑定的手势组成,绑定的手势可以分为单一手势和组合手势两种类型,根据手势的复杂程度进行区分。本文跟着 V 哥一起来探讨手势事件处理。 想要考取鸿蒙认证的小伙伴,请加入V 哥班级获取辅导: https://developer.huawei.com/consu…...

Vue3项目中集成AI对话功能的实战经验分享

ai-suspended-ball-chat组件使用体验摘要 本文分享了Vue3项目中使用ai-suspended-ball-chat组件集成AI对话功能的实践经验。该组件提供悬浮球和独立面板两种模式,支持流式响应、图片上传、语音交互等功能,显著提升了用户体验。通过实际案例展示了在客服系统和代码助手场景中的…...

gulimall出现服务间调用org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.choose 问题

java.lang.AbstractMethodError: org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.choose(Ljava/lang/String;Lorg/springframework/cloud/client/loadbalancer/Request;)Lorg/springframework/cloud/client/ServiceInstance;A调用B模块出现上面这个问题,…...

Java02课前问题列表

Java02课前问题列表1.方法相关问题 public class Main {static void changeStr(String x) {x = "xyz";}static void changeArr(String[] strs) {for (int i = 0; i < strs.length; i++) {strs[i] = strs[i]+""+i;}}public static void main(String[] ar…...

达梦数据库安装和使用

1、达梦数据库安装地址 https://eco.dameng.com/document/dm/zh-cn/start/install-dm-windows-prepare.html 2、 点击下载3、4、 现在版本只需要点击exe文件56 点击【下一步】如图所示7、 接受授权协议8 如果没有key文件可跳过 如果有点击浏览找到key文件系统自动校验9建议典型…...

CSP 赛前周记

初赛前 - 第一周(末) 这学期的第一周,据说是本学期第二长的假期,故开始摸摸。 Day1 - 周五 晚上回来开了把信奥大联赛,发现比你谷月赛还烂,IOI 赛制,风格跟 CSP 三不沾,每周有时间打打玩玩吧(结果被打爆了,只有 230pts)。总结Day2 - 周六 由于作业多得一批,白天在疯…...

Day16对数组的基本认识

数组的定义package array;public class ArrayDemo1 {//变量类型 变量名称 = 变量的值//数组类型 同上public static void main(String[] args) {int [] nums;//声明一个数组nums = new int [10];//创建一个数组int [] nums1 =new int [10];//两种写法都可,初学建议拆分避免…...

Ubuntu 界面变为 Mac

sudo apt install gnome-tweaks...