Android で利用した場合の使い方をメモする。
#ORMLite とは
Android でも使える O/R マッパー。
メソッド名を見ればなんとなく使い方が分かるので、使いやすそうな印象。
#Hello World
##インストール
dependencies {
compile 'com.j256.ormlite:ormlite-android:4.48'
}
##実装
package com.example.ormlitesample;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
@DatabaseTable
public class TestTable {
@DatabaseField(id=true)
private Long id;
@DatabaseField
private String code;
@DatabaseField
private String value;
@SuppressWarnings("unused")
private TestTable() {}
public TestTable(Long id, String code, String value) {
this.id = id;
this.code = code;
this.value = value;
}
@Override
public String toString() {
return "TestTable [id=" + id + ", code=" + code + ", value=" + value + "]";
}
}
package com.example.ormlitesample;
import java.sql.SQLException;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;
public class MyDatabaseHelper extends OrmLiteSqliteOpenHelper {
private static final String DATABASE_NAME = "ormlite.sample";
private static final int DATABASE_VERSION = 1;
public MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
System.out.println("MyDatabaseHelper.onCreate()");
try {
TableUtils.createTable(connectionSource, TestTable.class);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
System.out.println("MyDatabaseHelper.onUpgrade()");
}
}
package com.example.ormlitesample;
import java.sql.SQLException;
import android.os.Bundle;
import com.j256.ormlite.android.apptools.OrmLiteBaseActivity;
import com.j256.ormlite.dao.Dao;
public class MainActivity extends OrmLiteBaseActivity<MyDatabaseHelper> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Dao<TestTable, Long> dao = getHelper().getDao(TestTable.class);
TestTable hoge = new TestTable(1L, "a", "A");
dao.create(hoge);
TestTable table = dao.queryForId(1L);
System.out.println(table);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
##動作結果
I/System.out(1935): MyDatabaseHelper.onCreate()
I/System.out(1935): TestTable [id=1, code=hoge, value=HOGE]
##説明
- データベースとマッピングするクラス(
TestTable
)は、@DatabaseTable
でアノテートする。- カラムとマッピングさせるフィールドは、
@DatabaseField
でアノテートする。 - フィールドの Getter/Setter は不要(private でも構わない)。
- 引数なしのコンストラクタが必須(private でも構わない)。
- カラムとマッピングさせるフィールドは、
- データベースアクセスには、
OrmLiteSqliteOpenHelper
を継承したクラスを作成する。-
TableUtils.createTable()
を使うと、クラスに設定したアノテーションの情報をもとにテーブルを自動生成してくれる。
-
- アクティビティは
OrmLiteBaseActivity
を継承して作成する。- 型引数に、作成した
OrmLiteSqliteOpenHelper
のサブクラスを渡す。 -
getHelper().getDao(Class)
で、デーブルごとの Dao を取得する。
- 型引数に、作成した
###Dao 生成はコストが高い
Dao の生成はコストが高いらしく、1度生成したらできる限り使い回したほうが良いらしい。
Building a DAO can be an expensive operation and for devices with limited resources (like mobile apps), DAOs should be reused if at all possible.
2.4 Setting Up the DAOs | ORMLite Documentation: 2. How to Use
#OrmLiteBaseActivity を継承しない方法
package com.example.ormlitesample;
import java.sql.SQLException;
import android.app.Activity;
import android.os.Bundle;
import com.j256.ormlite.android.apptools.OpenHelperManager;
import com.j256.ormlite.dao.Dao;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
MyDatabaseHelper helper = OpenHelperManager.getHelper(this, MyDatabaseHelper.class);
Dao<TestTable, Long> dao = helper.getDao(TestTable.class);
TestTable hoge = new TestTable(1L, "hoge", "HOGE");
dao.create(hoge);
TestTable table = dao.queryForId(1L);
System.out.println(table);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
OpenHelperManager.releaseHelper();
}
}
-
OpenHelperManager.getHelper()
メソッドで、ヘルパークラスを取得できる。 - この場合、アクティビティが削除されるときに
OpenHelperManager.releaseHelper()
を呼ばなければならない。
#SQLException をスローしない Dao を取得する
package com.example.ormlitesample;
import android.os.Bundle;
import com.j256.ormlite.android.apptools.OrmLiteBaseActivity;
import com.j256.ormlite.dao.RuntimeExceptionDao;
public class MainActivity extends OrmLiteBaseActivity<MyDatabaseHelper> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RuntimeExceptionDao<TestTable, Long> dao = getHelper().getRuntimeExceptionDao(TestTable.class);
TestTable table = dao.queryForId(1L);
System.out.println(table);
}
}
-
getRuntimeExceptionDao(Class)
で、SQLException
をスローしない Dao を取得できる。
#アノテーションで設定する
##@DatabaseTable
###tableName
- テーブル名を指定する。
- 省略した場合は、クラス名を小文字にしたものが使用される。
全部書いてると大変なので、主要そうなのだけ。
その他の属性は ORMLite Documentation: 2. How to Use を参照。
###columnName
- カラム名を指定する。
###dataType
- データベースに保存するときの型を指定する。
- Persisted Data Types
###defaultValue
- レコードを登録するときの初期値を設定する。
###width
- カラムのサイズを指定する。
- 主に文字列型のカラムのサイズを指定するのに使用する。
- 0 の場合はデータベースのデフォルトのサイズが使用される。
###canBeNull
- NULL 可項目にするかどうかを boolean で指定する。
- デフォルトは true。
###id
- プライマリキーであることを指定する。
- クラス内で、1つのフィールドにのみ設定できる。
- Dao の **ForId() などのメソッドは、id = true の項目が使用される。
###generatedId
- プライマリキーの値がデータベースによって自動生成されることを指定する。
- クラス内で、1つのフィールドにのみ設定できる。
- id 属性とは排他的に指定する。
###foreign
- true を指定すると、そのフィールドが他のテーブルの ID を参照していることを指定できる。
- Java クラス上は、参照するテーブルクラスをフィールドとして持つようにする。
package com.example.ormlitesample;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
@DatabaseTable
public class ForeignTable {
@DatabaseField(generatedId=true)
private Long id;
@DatabaseField
private String value;
}
package com.example.ormlitesample;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
@DatabaseTable
public class TestTable {
@DatabaseField(generatedId=true)
private Long id;
@DatabaseField(foreign=true)
private ForeignTable foreign;
}
###useGetSet
- true を設定すると、値の取得は Getter/Setter を通じて行われるようになる。
- デフォルトは、リフレクションを使ってフィールドを直接参照する。
###unknownEnumName
- DB から読み込んだレコードに enum の列挙子に存在しない値が存在した場合、どの列挙子にマッピングするかを文字列で指定する。
- デフォルトは、存在しない列挙子を読み込んだ場合、 SQLException がスローされる。
###persisted
- false を設定すると、そのフィールドはデータベースに保存されなくなる。
###unique
- true を設定すると、その項目にユニークキー制約が設定される。
- 複数項目でユニークキーを設定する場合は、
uniqueCombo
を使う。
###uniqueCombo
- true を設定した複数の項目でユニークキー制約を設定する。
###uniqueIndexName
- 名前を指定してユニークキー制約を設定する。
- 同じ名前を設定したフィールドで1つのユニークキー制約を設定できる。
#QueryBuilder を使って検索する
##基本
package com.example.ormlitesample;
import java.sql.SQLException;
import java.util.List;
import android.os.Bundle;
import com.j256.ormlite.android.apptools.OrmLiteBaseActivity;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.PreparedQuery;
import com.j256.ormlite.stmt.QueryBuilder;
public class MainActivity extends OrmLiteBaseActivity<MyDatabaseHelper> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Dao<TestTable, Long> dao = getHelper().getDao(TestTable.class);
QueryBuilder<TestTable, Long> queryBuilder = dao.queryBuilder();
PreparedQuery<TestTable> preparedQuery = queryBuilder.where().eq("value", "hoge").prepare();
List<TestTable> list = dao.query(preparedQuery);
System.out.println(list);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
-
Dao.queryBuilder()
でビルダーを取得する。 - ビルダーで検索条件を設定し、最後に
prepare()
メソッドでPreparedQuery
のインスタンスを取得する。 - 取得した
PreparedQuery
をDao.query()
に渡す。
##AND, OR 条件を使う
###普通に連結する
QueryBuilder<TestTable, Long> builder = dao.queryBuilder();
builder.where().eq("value", "hoge")
.or()
.eq("value", "fuga");
WHERE value = 'hoge'
OR value = 'fuga'
-
and()
またはor()
メソッドで AND, OR を指定できる。
###グループ化する
QueryBuilder<TestTable, Long> builder = dao.queryBuilder();
Where<TestTable, Long> where = builder.where();
where.or(
where.and(
where.eq("code", "hoge"),
where.eq("value", "HOGE")
),
where.and(
where.eq("code", "piyo"),
where.eq("value", "PIYO")
)
);
PreparedQuery<TestTable> query = builder.prepare();
WHERE (code = 'hoge' AND value = 'HOGE')
OR (code = 'piyo' AND valud = 'PIYO')
-
Where
オブジェクトを使って、複雑な条件式を作ることができる(難読化しそうで恐い)。
その他にも distinct()
とか orderBy()
とか、様々なメソッドがビルダーに用意されている。
3.4 QueryBuilder Capabilities | ORMLite Documentation: 3. Custom Statement Builder この辺を見れば、だいたい使い方は分かる気がする。
#生 SQL を実行する
package com.example.ormlitesample;
import java.sql.SQLException;
import android.os.Bundle;
import com.j256.ormlite.android.apptools.OrmLiteBaseActivity;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.GenericRawResults;
import com.j256.ormlite.field.DataType;
public class MainActivity extends OrmLiteBaseActivity<MyDatabaseHelper> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Dao<TestTable, Long> dao = getHelper().getDao(TestTable.class);
String sql = "SELECT * FROM testtable WHERE value = ?";
DataType[] dataTypes = {DataType.LONG_OBJ, DataType.STRING};
GenericRawResults<Object[]> result = dao.queryRaw(sql, dataTypes, "HOGE");
for (Object[] row : result) {
for (Object column : row) {
System.out.println("class=" + column.getClass() + ", value=" + column);
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
I/System.out(12073): class=class java.lang.Long, value=1
I/System.out(12073): class=class java.lang.String, value=HOGE
-
Dao.queryRow()
という名前のメソッドがいくつかオーバーロードされており、これらは生の SQL を実行できる。 - COUNT を取りたいときとかは、
queryRawValue()
という long 値を返すメソッドが用意されている。
##生 SQL を書いたけど、結局 Java クラスにマッピングできるように SELECT している場合
複雑な JOIN とかしたけど、結局 SELECT しているのは1つのテーブルの項目だけで、そのままクラスにマッピングして欲しいときとかは、以下のようにするといいと思う。
package com.example.ormlitesample;
import java.sql.SQLException;
import android.os.Bundle;
import com.j256.ormlite.android.apptools.OrmLiteBaseActivity;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.GenericRawResults;
import com.j256.ormlite.dao.RawRowMapper;
public class MainActivity extends OrmLiteBaseActivity<MyDatabaseHelper> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Dao<TestTable, Long> dao = getHelper().getDao(TestTable.class);
String sql = "SELECT * FROM testtable";
RawRowMapper<TestTable> mapper = dao.getRawRowMapper();
GenericRawResults<TestTable> result = dao.queryRaw(sql, mapper);
for (TestTable row : result) {
System.out.println(row);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
-
queryRow()
の第二引数に、 dao から取得しておいた TestTable クラス用のRawRowMapper
を渡す。
#RoboGuice と連携する
package com.example.ormlitesample;
import javax.inject.Inject;
import roboguice.activity.event.OnCreateEvent;
import roboguice.activity.event.OnDestroyEvent;
import roboguice.event.Observes;
import roboguice.inject.ContextSingleton;
import android.app.Activity;
import com.j256.ormlite.android.apptools.OpenHelperManager;
import com.j256.ormlite.dao.RuntimeExceptionDao;
@ContextSingleton
public class MyDatabaseHelperProvider {
@Inject
private Activity activity;
private MyDatabaseHelper helper;
private RuntimeExceptionDao<TestTable, Long> testTableDao;
public void onActivityCreate(@Observes OnCreateEvent event) {
this.helper = OpenHelperManager.getHelper(this.activity, MyDatabaseHelper.class);
}
public void onActivityDestroy(@Observes OnDestroyEvent event) {
OpenHelperManager.releaseHelper();
}
public RuntimeExceptionDao<TestTable, Long> getTestTableDao() {
if (this.testTableDao == null) {
this.testTableDao = this.helper.getRuntimeExceptionDao(TestTable.class);
}
return this.testTableDao;
}
}
package com.example.ormlitesample;
import java.util.List;
import javax.inject.Inject;
import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import android.os.Bundle;
import com.j256.ormlite.dao.RuntimeExceptionDao;
@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {
@Inject
private MyDatabaseHelperProvider provider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RuntimeExceptionDao<TestTable, Long> dao = this.provider.getTestTableDao();
List<TestTable> list = dao.queryForAll();
for (TestTable t : list) {
System.out.println(t);
}
}
}
-
MyDatabaseHelper
を供給するクラスを@ContextSingleton
スコープで定義する。 -
@Observes
を使うことで、onDestory()
時のリリース漏れが無いようにしている(これで使い回しがしやすくなる気がする)。
#参考