Hive

FlutterDartローカルデータベースキー・バリュー軽量NoSQLモバイル

キャッシュライブラリ

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');
}