Android Jetpack Roomを利用してみる【Java】

公式サイト はこちら

公式サイトに記載があるように、ローカル データベースにデータを保存する場合は、「 Room を使用することを強くおすすめします」とのことだったので、使ってみます。

使ってみて忘れないために本記事を残しておきたいと思います。
まずは公式ドキュメントを見て理解します。

Roomのアーキテクチャの図(公式より)

主要コンポーネント(公式より)

データベースクラス
データベースを保持し、アプリの永続データに対する基礎的な接続のメイン アクセスポイントとして機能します。  

データエンティティ
アプリのデータベースのテーブルを表します。  

データアクセスオブジェクト(DAO)
アプリがデータベースのデータのクエリ、更新、挿入、削除に使用できるメソッドを提供します。

なるほど…

……

私が Androidの公式ドキュメントを読んだだけで理解することは、ほぼないので実際に使ってみたいと思います。

サンプルコード

ライブラリのインポート

build.gradle の dependencies に記載。

build.gradle

def room_version = "2.3.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"

テーブル構成

今回は、以下のような緯度経度が入った coordinates テーブルを作成していきます。

カラム名内容
idintID
latitudedouble緯度
longitudedouble経度
created_atString作成日

主要コンポーネント

コンポーネントクラス名役割
データベースクラス AppData アクセスポイント
データエンティティCoordinateテーブル構成
データアクセスオブジェクト(DAO)CoordinateDao データベースの操作、クエリ置き場のようなイメージ

またデータの取得やインサートなどの操作を命令する CoordinateHelperクラス も作成します。

Database

entitiesの指定と、versionの指定などを行います。
見よう見まねで使っている感じが強いです。

AppData.java

@Database(entities = {Coordinate.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    //DAO用メソッド
    public abstract CoordinateDao coordinateDao();
}

Entity

上述のテーブル構成はこちらに記載します。

テーブル名は、coordinates
プライマリーキーは id に設定しており、アノテーションを設定します。
autoGenerate は、インサート時に自動で生成されます。
auto_incrementと同じようなイメージでしょうか。
カラム名は特に指定がなければプロパティと同じになります。
ただし、カラム名はスネークケース、javaではプロパティ名をキャメルケースで書くのが一般的と思いますので、カラム名とプロパティ名を分けたい場合は、 ColumnInfo で設定します。

Coordinate.java

@Entity(tableName = "coordinates")
public class Coordinate {

    /**
     * 定義するカラム
     */
    @PrimaryKey(autoGenerate = true)
    public int id;
    public double latitude;
    public double longitude;

    // カラム名とプロパティ名が異なる場合は、ColumnInfoで設定する
    @ColumnInfo(name = "created_at")
    public String createdAt;

    /**
     * コンストラクタ
     * @param latitude 緯度
     * @param longitude 経度
     * @param createdAt 日付
     */
    public Coordinate(double latitude, double longitude, String createdAt) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.createdAt = createdAt;
    }

    public int getId() {
        return this.id;
    }

    public double getLatitude() {
        return this.latitude;
    }

    public double getLongitude() {
        return this.longitude;
    }

    public String getCreatedAt() {
        return this.createdAt;
    }

}

DAO

上述しましたが、クエリ置き場のようなイメージです。
insert、update、deleteはアノテーションさえ付ければsql文を書く必要がありません。

CoordinateDao.java

@Dao
public interface CoordinateDao {
    /**
     * 全データを昇順で取得
     * @return リスト
     */
    @Query("SELECT * FROM coordinates ORDER BY id ASC")
    List<Coordinate> getCoordinateList();

    /**
     * データを追加
     * @param coordinate 新規データ
     */
    @Insert
    void insert(Coordinate coordinate);

    /**
     * データを更新
     * @param coordinate 更新データ
     */
    @Update
    void update(Coordinate coordinate);

    /**
     * データを削除
     * @param coordinate 削除データ
     */
    @Delete
    void delete(Coordinate coordinate);
}

命令を下すHelperクラス

Roomでは、メインスレッドでクエリを発行しようとするとエラーを吐きますので、別スレッド(非同期処理)で行う必要があります。
サンプルでは、ExecutorService を利用しています。

CoordinateHelper.java

public class CoordinateHelper {

    public static final String DB_NAME = "coordinate.db";
    private final CoordinateDao mDao;
    private List<Coordinate> mCoordinateList;

    /**
     *  コンストラクタ
     */
    public CoordinateHelper(Context context) {
        AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, DB_NAME).build();
        mDao = db.coordinateDao();
    }

    /**
     *  DBからデータ取得してセット
     */
    public void setCoordinateLocationList() {
        // Roomは非同期でアクセスしないと、エラーを出すので注意
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    mCoordinateList = mDao.getCoordinateList();
                    // UI変更など
                    
                } catch (Exception e) {
                    // エラー処理
                }
            }
        });
    }

    /**
     * DBへデータ追加
     */
    public void insert(Coordinate coordinate){

        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    mDao.insert(coordinate);
                } catch (Exception e) {
                    // エラー処理
                }
            }
        });
    }

    /**
     * DBのデータを削除
     */
    public void delete(Coordinate coordinate){
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    mDao.delete(coordinate);
                } catch (Exception e) {
                    // エラー処理
                }
            }
        });
    }

}

アクティビティから呼び出し

データの追加例です。
Coordinateオブジェクトを生成して、命令を下すHelperオブジェクトにデータベースにデータを追加してもらっています。

SampleActivity.java

Coordinate coordinate = new Coordinate(latitude, longitude, createdAt);
CoordinateHelper coordinateHelper = new CoordinateHelper(context);
coordinateHelper.insert(coordinate); // データの追加

感想

軽く触ってみた感じだと、結構簡単にデータ操作が行えたなっていう印象ですね。
非同期処理が強いられるので、クエリ後の結果を使って操作する部分は、初学者にとっては難しそうだと感じました。
強くおすすめしているぐらいなので、これからはRoomを使うのが当たり前になるのでしょうか。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です