速いと噂のAndroid ORMライブラリDBFlowを使ってみた
Raizlabs社というところのDBFlowというORマッパライブラリを使ってみました。
とても新しいライブラリで、2014/9/7がfirst commitとなっています。
こちらのサイト様で紹介されています。
qiita.com
Raizlabs社のスピードテストでは非常に速いという結果が出ています。
github.com
こちらのサイトでもActive Android, Sprinkles, SugarORM, DBFlowの中では一番速いという結果が出ています。
www.raizlabs.com
そんなDBFlowを基本的なところだけ動かしてみました。
導入
android-aptプラグインを導入します。
トップレベルのbuild.gradleに以下のように記述します。
buildscript { repositories { // Required for this library, don't use mavenCentral () jcenter() } dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } }
projectレベルのbuild.gradleに以下のように記述します。
apply plugin: 'com.neenbedankt.android-apt' dependencies { apt 'com.raizlabs.android:DBFlow-Compiler:2.2.1' compile "com.raizlabs.android:DBFlow-Core:2.2.1" compile "com.raizlabs.android:DBFlow:2.2.1" }
※ここで注意事項ですが、ビルド時間の短縮のためオフラインモードを使って下さい。 Android StudioのPreferences-> Build,Executor,Deployment->Build Tools->Gradle->Offline Workをチェックします。
DBFlowのセットアップ
Application継承クラスでDBFlowの初期化をします。
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); FlowManager.init(this); } }
データベースを定義する
DBFlowではデータベースはテーブルが各自接続するためのプレースホルダーオブジェクトとなります。
データベース用のクラスを作成し、アノテーションでデータベース名とバージョンを指定します。
@Database(name = ColonyDatabase.NAME, version = ColonyDatabase.VERSION) public class ColonyDatabase { public static final String NAME = "Colonies"; public static final int VERSION = 1; }
テーブルを定義する
Modelクラスを継承したクラスを作成します。
標準的なテーブル用にModelクラスを拡張したBaseModelクラスが用意されています。
テーブルの定義には@Tableアノテーションをつけ、データベース名を指定します。
必ずprimary keyを定義します。
クラスとColumnのスコープはpackage privateかpublicで定義して下さい。
@Table(databaseName = ColonyDatabase.NAME) public class Queen extends BaseModel { @Column @PrimaryKey(autoincrement = true) long id; @Column String name; }
フィールドを定義する
テーブルのクラスに@Columnアノテーションをつけたフィールドを定義します。
@Column(name = "modify_date")のように指定した場合はmodify_dateが実際のテーブルのフィールド名になります。
@Column(defaultValue = "defaultValueStringhere")のようにデフォルトの値を指定することも出来ます。
@PrimaryKeyアノテーションはプライマリーキーとなります。
他にもFOREIGN KEYの貼り方などリファレンスを見て下さい。
(@NotNull付けるとNOT NULL ON CONFLICT FAILまで付けられる気がするv2.2.1
ので怖いから使いたくない)
(BLOBのフィールドのセットの仕方がわからない)
@Column @Unique @PrimaryKey() String postId; @Column(name = "modify_date") Date date; @Column // @NotNull String title; @Column String description; // @Column // com.raizlabs.android.dbflow.data.Blob testblob;
apt
テーブルの定義が終わったらコンパイルしてみます。/app/build/generated/source/aptフォルダの配下に生成されたコードが入っています。
別にここのコードを見なくても処理を書けますが、見ておくといいかもしれません。
Databaseの定義
ContentValuesとのバインド
Query
私もあまり色々試していないのですよくわかっていないのですが、
@Tableとして定義したクラスには
insert()
save()
delete()
update()
async()
などのメソッドが用意されています。
//データの追加 posts = new Posts(); posts.date = new Date(); posts.postId = "test02"; posts.title = "title2"; posts.description = "description2"; posts.save();
select文
こういうSelect文はAndroidで書くのは大変ですが下記のように簡単に記述できます。
SELECT * FROM Ant where type = 'worker' AND isMale = 0;
↓
// main thread retrieval List<Ant> devices = new Select().from(Ant.class) .where( Condition.column(Ant$Table.TYPE).eq("worker"), Condition.column(Ant$Table.ISMALE).eq(false)).queryList(); //大きなクエリには TransactionManagerを推奨 // Async Transaction Queue Retrieval (Recommended) TransactionManager.getInstance().addTransaction(new SelectListTransaction<>( new Select() .from(DeviceObject.class) .where( Condition.column(Ant$Table.TYPE).eq("worker"), Condition.column(Ant$Table.ISMALE).eq(false))), transactionListener);
バックアップも作ってくれるみたい
Easy back up of the database: backupEnabled() enables back up on the database by calling FlowManager.getDatabaseForTable(table).backupDB() Please Note: This creates a temporary third database in case of a failed backup.
サンプル
実際に動かしてみたサンプルはこちらです。
適当なデータを作って画面に表示して、クリックされたデータを削除します。
処理の最初で前回のデータを全件削除するようにしています。
プロジェクト全体はこちらです。
github.com
データベースの定義とテーブルの定義
package sakurafish.com.examdbflow; import com.raizlabs.android.dbflow.annotation.Database; @Database(name = DBSample.NAME, version = DBSample.VERSION) public class DBSample { public static final String NAME = "dbsample"; public static final int VERSION = 1; }
package sakurafish.com.examdbflow; import com.raizlabs.android.dbflow.annotation.Column; import com.raizlabs.android.dbflow.annotation.PrimaryKey; import com.raizlabs.android.dbflow.annotation.Table; import com.raizlabs.android.dbflow.annotation.Unique; import com.raizlabs.android.dbflow.structure.BaseModel; import java.util.Date; @Table(databaseName = DBSample.NAME) public class Posts extends BaseModel { @Column @Unique @PrimaryKey() String postId; @Column(name = "modify_date") Date date; @Column // @NotNull String title; @Column String description; // @Column // com.raizlabs.android.dbflow.data.Blob testblob; }
DBを使用しているコード
package sakurafish.com.examdbflow; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; public class MainActivity extends AppCompatActivity { final String TAG = MainActivity.class.getSimpleName(); private List<Posts> mDatas; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); deleteData(); addData(); selectData(); initLayout(); } private void deleteData() { //全件削除 Log.d(TAG, "count 削除前:" + new Delete().from(Posts.class).count()); new Delete().from(Posts.class).query(); Log.d(TAG, "count 削除後:" + new Delete().from(Posts.class).count()); } private void addData() { Posts posts = new Posts(); posts.date = new Date(); posts.postId = "test01"; posts.title = "title1"; posts.description = "description1"; posts.save(); posts = new Posts(); posts.date = new Date(); posts.postId = "test02"; posts.title = "title2"; posts.description = "description2"; posts.save(); posts = new Posts(); posts.date = new Date(); posts.postId = "test03"; posts.title = "title3"; posts.description = "description3"; posts.save(); posts = new Posts(); posts.date = new Date(); posts.postId = "test04"; posts.title = "title4"; posts.description = "description4"; posts.save(); posts = new Posts(); posts.date = new Date(); posts.postId = "test05"; posts.title = "title5"; posts.description = "description5"; posts.save(); } private void selectData() { //全件取得 mDatas = new Select().from(Posts.class).queryList(); } private void initLayout() { ListView listView = (ListView) findViewById(R.id.listview); final MyAdapter adapter = new MyAdapter(this, mDatas); listView.setAdapter(adapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(getApplicationContext(), "deleted! " + mDatas.get(position).postId, Toast.LENGTH_LONG).show(); //1件削除 mDatas.get(position).delete(); mDatas.remove(position); adapter.swapItems(mDatas); } }); } public class MyAdapter extends BaseAdapter { private Context mContext; private List<Posts> mDatas; public MyAdapter(@NonNull Context context, @Nullable List<Posts> datas) { super(); mContext = context; mDatas = datas; } public void swapItems(@NonNull final List<Posts> datas) { mDatas = datas; notifyDataSetChanged(); } @Override public int getCount() { if (mDatas == null) { return 0; } return mDatas.size(); } @Override public Object getItem(int position) { return mDatas.get(position); } @Override public long getItemId(int position) { return position; } class ViewHolder { TextView date; TextView postId; TextView title; TextView description; } SimpleDateFormat incomingFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); SimpleDateFormat outgoingFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm", java.util.Locale.getDefault()); @Override public View getView(final int position, View convertView, final ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); convertView = inflater.inflate(R.layout.list_row, null); holder = new ViewHolder(); holder.date = (TextView) convertView.findViewById(R.id.textView_date); holder.postId = (TextView) convertView.findViewById(R.id.textView_post_id); holder.title = (TextView) convertView.findViewById(R.id.textView_title); holder.description = (TextView) convertView.findViewById(R.id.textView_description); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } Posts data = (Posts) getItem(position); String date = outgoingFormat.format(data.date); holder.date.setText(date); holder.postId.setText(data.postId); holder.title.setText(data.title); holder.description.setText(data.description); return convertView; } } }