Hive
キャッシュライブラリ
Hive
概要
HiveはDartで書かれた軽量で高速なキー・バリューデータベースです。純粋なDartで実装されており、Flutterアプリケーションでのローカルデータストレージソリューションとして最適化されています。SQLiteのような重いデータベースの代替として、シンプルなAPIと高い性能を提供し、iOS、Android、デスクトップ、Webプラットフォームで動作します。
詳細
Hiveは、モバイルアプリケーションでの軽量なデータ永続化のために設計されたNoSQLデータベースです。トランザクション、型安全性、CRUD操作をサポートしながら、最小限の設定で利用できます。内部的にはバイナリ形式でデータを保存し、高速な読み書き性能を実現しています。
主要な特徴
- 純粋なDart実装: 外部依存なしでクロスプラットフォーム対応
- 高性能: バイナリ形式による高速なデータアクセス
- 型安全性: ジェネリクス対応による型安全なデータ操作
- トランザクション: 原子性を保証するwrite/readトランザクション
- 自動キー生成: リスト形式での使用時の自動インクリメントキー
- カスタムオブジェクト対応: シリアライゼーション機能による複雑なデータ構造の保存
- 軽量: 最小限のメモリフットプリントとディスク使用量
アーキテクチャ
- Box: データを格納するコンテナ(テーブルに相当)
- Adapter: カスタムオブジェクトのシリアライゼーション/デシリアライゼーション
- Transaction: write()、read()によるトランザクション制御
- Type Safety: ジェネリクス(
Box<T>)による型制約
メリット・デメリット
メリット
- シンプルさ: 最小限の設定で即座に利用開始可能
- 高いパフォーマンス: SQLiteより高速な読み書き性能
- 型安全性: コンパイル時の型チェックによるバグ削減
- 軽量: アプリサイズへの影響が最小限
- クロスプラットフォーム: Flutter対応の全プラットフォームで利用可能
- オフライン対応: ネットワーク接続不要でのデータ永続化
デメリット
- 複雑なクエリ非対応: SQLのような高度なクエリ機能なし
- 関係データ非対応: リレーショナルデータベース機能なし
- 同時アクセス制限: 複数プロセスからの同時アクセスは非推奨
- 大容量データ不向き: 大量のデータには適さない場合がある
参考ページ
書き方の例
基本的なセットアップとBox操作
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Hiveの初期化
final directory = await getApplicationDocumentsDirectory();
Hive.defaultDirectory = directory.path;
runApp(MyApp());
}
// 基本的なデータ操作
void basicOperations() {
// デフォルトBoxを開く
final box = Hive.box();
// データの保存
box.put('name', 'Alice');
box.put('age', 25);
box.put('city', 'Tokyo');
// データの取得
final name = box.get('name');
final age = box.get('age', defaultValue: 0);
print('Name: $name, Age: $age');
// Map形式での操作
box['email'] = '[email protected]';
final email = box['email'];
// 複数データの一括保存
box.putAll({
'country': 'Japan',
'language': 'Japanese'
});
}
名前付きBoxと型安全性
// 名前付きBoxの作成
void namedBoxExample() {
final settingsBox = Hive.box(name: 'settings');
final cacheBox = Hive.box(name: 'cache');
// 設定データの保存
settingsBox.put('theme', 'dark');
settingsBox.put('notifications', true);
// キャッシュデータの保存
cacheBox.put('lastUpdate', DateTime.now().toIso8601String());
}
// 型安全なBox
void typeSafeExample() {
// String型のみを格納するBox
final Box<String> stringBox = Hive.box<String>(name: 'strings');
stringBox.put('greeting', 'Hello, World!');
stringBox.put('farewell', 'Goodbye!');
// コンパイル時にエラーになる
// stringBox.put('number', 123); // Error!
// 安全な取得
final greeting = stringBox.get('greeting') ?? 'Default';
}
リスト形式でのデータ操作
void listLikeOperations() {
final box = Hive.box();
// リスト形式でのデータ追加
box.add('First item');
box.add('Second item');
box.add('Third item');
// インデックスでの取得
print(box.getAt(0)); // First item
print(box.getAt(1)); // Second item
// Map形式と混在可能
box.put('key1', 'Value by key');
print(box.getAt(3)); // Value by key
// インデックスでの更新
box[0] = 'Updated first item';
// データの削除
box.deleteAt(1); // Second itemを削除
}
カスタムオブジェクトの保存
// カスタムクラスの定義
class User {
User({required this.name, required this.email, required this.age});
// JSONからのファクトリコンストラクタ
factory User.fromJson(Map<String, dynamic> json) => User(
name: json['name'] as String,
email: json['email'] as String,
age: json['age'] as int,
);
final String name;
final String email;
final int age;
// JSONへの変換
Map<String, dynamic> toJson() => {
'name': name,
'email': email,
'age': age,
};
@override
String toString() => '$name ($email) - $age歳';
}
void customObjectExample() {
// アダプターの登録
Hive.registerAdapter('User', User.fromJson);
final box = Hive.box();
// カスタムオブジェクトの保存
final user = User(
name: 'Bob',
email: '[email protected]',
age: 30,
);
box.put('currentUser', user);
// カスタムオブジェクトの取得
final retrievedUser = box.get('currentUser') as User?;
print('取得したユーザー: $retrievedUser');
}
トランザクション処理
void transactionExample() {
final box = Hive.box();
// 書き込みトランザクション
box.write(() {
box.put('account1', 1000);
box.put('account2', 500);
box.put('account3', 750);
});
// 読み込みトランザクション
box.read(() {
final total = (box.get('account1', defaultValue: 0) as int) +
(box.get('account2', defaultValue: 0) as int) +
(box.get('account3', defaultValue: 0) as int);
print('Total balance: $total');
});
// 原子性の例(エラー時にロールバック)
box.put('balance', 1000);
try {
box.write(() {
box.put('balance', 500);
throw Exception('何かエラーが発生');
});
} catch (e) {
print('エラー発生: $e');
}
print('残高: ${box.get('balance')}'); // 1000(ロールバックされる)
}
FlutterアプリでのHive活用例
class TodoApp extends StatefulWidget {
@override
_TodoAppState createState() => _TodoAppState();
}
class _TodoAppState extends State<TodoApp> {
final Box<String> todoBox = Hive.box<String>('todos');
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Hive Todo App')),
body: Column(
children: [
// 新規TODO追加
Padding(
padding: EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'TODOを入力',
),
),
),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
if (_controller.text.isNotEmpty) {
setState(() {
todoBox.add(_controller.text);
_controller.clear();
});
}
},
),
],
),
),
// TODOリスト表示
Expanded(
child: ListView.builder(
itemCount: todoBox.length,
itemBuilder: (context, index) {
final todo = todoBox.getAt(index);
return ListTile(
title: Text(todo ?? ''),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
todoBox.deleteAt(index);
});
},
),
);
},
),
),
],
),
);
}
}
非同期処理とIsolate
import 'dart:isolate';
void isolateExample() async {
final box = Hive.box();
// メインスレッドでデータ保存
box.put('message', 'Hello from main');
// 別のIsolateでHive操作
await Hive.compute(() {
// 同じBoxに他のIsolateからアクセス
final box = Hive.box();
print(box.get('message')); // Hello from main
// データ更新
box.put('message', 'Updated from isolate');
});
// メインスレッドで確認
print(box.get('message')); // Updated from isolate
}
データクリーンアップとメンテナンス
void maintenanceOperations() {
final box = Hive.box();
// 特定のキーの削除
box.delete('oldKey');
// 複数キーの削除
box.deleteAll(['key1', 'key2', 'key3']);
// 全データの削除
box.clear();
// Boxのサイズ確認
print('Box size: ${box.length}');
print('Box is empty: ${box.isEmpty}');
// 全てのキーの取得
final keys = box.keys.toList();
print('All keys: $keys');
// 全ての値の取得
final values = box.values.toList();
print('All values: $values');
}