さらに、Oracleのドキュメントでは、SQliteと比較してパフォーマンス指標が大幅に優れていることが示されたため、アプリケーションでは(携帯電話を超えないで)このデータストレージ形式を選択しました。
データベースの操作の中心となるクラスは、シングルトンテンプレートに従って作成され、次のようになります。
public class DatabaseConfig { private static DatabaseConfig ourInstance; private Environment envmnt; private EntityStore store; public static DatabaseConfig getInstance() { if (ourInstance == null) throw new IllegalArgumentException("You need initialize database config previously!"); return ourInstance; } public static void init(File envDir) { ourInstance = new DatabaseConfig(envDir); } private DatabaseConfig(File envDir) { EnvironmentConfig envConfig = new EnvironmentConfig(); StoreConfig storeConfig = new StoreConfig(); envConfig.setTransactional(true); envConfig.setAllowCreate(true); storeConfig.setAllowCreate(true); storeConfig.setTransactional(true); envmnt = new Environment(envDir, envConfig); try { store = new EntityStore(envmnt, "autocalc", storeConfig); } catch (IncompatibleClassException e) { //todo: . } } public static void shutdown() { if (ourInstance != null) { ourInstance.close(); } } private void close() { store.close(); envmnt.close(); } public EntityStore getStore() { return store; } public Transaction startTransaction() { return envmnt.beginTransaction(null, null); } }
このクラスの問題は非常に散発的であり、エンティティにアクセスする前に初期化する必要がありますが、これは忘れてしまう可能性があります。 さらに、トランザクションの作成/終了の問題が発生しました。 トランザクションは、あるクラスで開き、別のクラスで閉じます。これは、開発の観点からは最適に見えません。 これまでのところ、この「間違い」は私が「美しく」修正することができませんでした。 これは、保存されたエンティティの次の識別子の値を取得するためにトランザクションが使用されるという事実に照らして特に曲がって見えます。
上位レベルでは、DataAccessデータアクセスクラスが作成されています。
public class FuelItemDA { private PrimaryIndex<Long, FuelItem> prIndex; private SecondaryIndex<Long, Long, FuelItem> odometerIndex; private SecondaryIndex<Date, Long, FuelItem> dateIndex; private DatabaseConfig dbConfig; public FuelItemDA() { dbConfig = DatabaseConfig.getInstance(); prIndex = dbConfig.getStore().getPrimaryIndex( Long.class, FuelItem.class); odometerIndex = dbConfig.getStore().getSecondaryIndex( prIndex, Long.class, "odometer"); dateIndex = dbConfig.getStore().getSecondaryIndex( prIndex, Date.class, "operationDate"); } public void save(FuelItem item) { Transaction tx = dbConfig.startTransaction(); try { if (item.getId() == 0) { long id = dbConfig.getStore().getSequence("SPENT_ID").get(tx, 1); item.setId(id); } prIndex.put(tx, item); tx.commit(); } catch (Exception e) { e.printStackTrace(); if (tx != null) { tx.abort(); tx = null; } } } public FuelItem load(long id) { return prIndex.get(id); } public List<FuelItem> getItemsInDates(Date bDate, Date eDate) { List<FuelItem> result = new LinkedList<FuelItem>(); EntityCursor<FuelItem> cursor = dateIndex.entities(bDate, true, eDate, true); for (Iterator<FuelItem> iterator = cursor.iterator(); iterator.hasNext(); ) { FuelItem spentItem = iterator.next(); result.add(spentItem); } cursor.close(); return result; } public void removeFuelItem(long id) { try { prIndex.delete(id); } catch (DatabaseException e) { e.printStackTrace(); prIndex.delete(id); } } }
ここでは、インデックスの作成に注意を払う必要があります。インデックスは、検索とフィルタリングに使用されます。 つまり 別のフィールドセットでデータを検索およびフィルタリングする必要がある場合は、追加のインデックスを作成する必要があります。
Berkley DBを使用するもう1つの機能は、情報の保存に使用されるエンティティクラスの記述です。 計画どおり、Berkley DBはオブジェクトの階層を保存できました。
@Persistent(version = 1) public class SpentItem implements Item{ @PrimaryKey(sequence="SPENT_ID") private long id; @SecondaryKey(relate= Relationship.MANY_TO_ONE) private long odometer; @SecondaryKey(relate= Relationship.MANY_TO_ONE) private Date operationDate; private double sum; .... } @Entity(version = 1) public class FuelItem extends SpentItem { private double count; private double price; private boolean full; ..... }
エンティティクラスでは、情報は注釈を通じて送信されます。
- データベースに保存されるオブジェクトの構造のバージョンについて。 オブジェクトの構造が変更された場合、データ構造を以前のバージョンから現在のバージョンに変換するトランスレーターを作成する必要があります。 FuelItemDAコンストラクターのtry / catchブロックを使用して、移行の問題を解決しました。
- 主キーと二次キーは、インデックスの作成に使用され、DataAccessレベルで定義しました
個人的には、データストレージを整理するこのアプローチが気に入りました。 なぜなら 表示するには、データベースに格納されているデータはそれほど必要ではなく、論理的に処理されているため、オブジェクトの方が簡単です。
DatabaseConfigを初期化するためだけに残っており、問題はまったくありません。
public class Calc extends Activity { private void setup() throws DatabaseException { File envDir = new File(android.os.Environment.getExternalStorageDirectory(), "data"); envDir = new File(envDir, "autoexpence"); if (!envDir.exists()) { if (!envDir.mkdirs()) { Log.e("TravellerLog :: ", "Problem creating Image folder"); } } DatabaseConfig.init(envDir); } }
SQliteを使用する利点には、SQLの形式でデータにアクセスするための使い慣れた開発ツールが含まれます。
Berkley Dbを使用する利点には、オブジェクトに対する直接のCRUD操作が含まれます。これにより、その後のデータの論理的な作業が容易になります。 私にとっては、これは通常のデータ出力インターフェースよりも重要な役割を果たしました。
PS
ダウンロードリンク。 バージョンが必要です
Berkeley DB Java。 アーカイブ内には、Android用のライブラリがあります。