ActiveObjectsのキャッシュの仕組みを簡単に説明します

ActiveObjectsではエンティティを通して、DBテーブルのデータの取得や変更を行います。これらは、エンティティに対してgetter()メソッドやsetter()メソッド、save()メソッドを呼び出すことにより行います。
もし、これらのメソッドを呼ぶたびに毎回DBへのアクセスが行われれば、パフォーマンスはとても悪くなるでしょう。そこで、パフォーマンスのため、ActiveObjectsではテーブルのデータをメモリ中にキャッシュします。
今回は、このActiveObjectsのキャッシュの仕組みをを簡単に説明します。

キャッシュ関連のインタフェース

ActiveObjectsには、キャッシュに関する以下のインタフェースがあります。それぞれのインタフェースは、RAMで始まるクラスにより実装されます。これらのクラスは、メモリ中にキャッシュを行うキャッシュの実装です。

インタフェース 実装クラス 概要
Cache RAMCache CacheLayerの管理
CacheLayer RAMCacheLayer データのキャッシュ
RelationsCache RAMRelationsCache 関連のキャッシュ

以下で、これらのインタフェースの仕組みを説明します。ただし、今回はRelationsCacheについては説明しません。

CacheLayer - データのキャッシュ

エンティティ(テーブル)のデータは、CacheLayerインスタンスの中にキャッシュされます。エンティティとCacheLayerインスタンスは、一対一に対応します。
CacheLayerは、javaのMapに似たインタフェースで、列名をキーとしてテーブルのデータの格納や取得を行います。
エンティティのsetterメソッドを呼び出すと、setterに対応する列の名前をキーとしてCacherLayerインスタンスにデータが格納されます。
エンティティのgetterメソッドを呼びび出すと、getterに対応する列の名前をキーとしてCacheLayerからデータを取得し、その値が返されます。もし、CacheLayerインスタンス中にデータがなければ、DBにSELECT文が発行され、テーブルからデータが取得されます。テーブルからデータを取得した場合、そのデータをCacheLayerインスタンスに格納します。
エンティティのsave()メソッドを呼び出すと、DBに対してUPDATE文が発行されテーブルのデータが更新されます。実際にUPDATE文で更新されるのは、以前のsave()メソッドの呼び出し後にsetterメソッドで設定された値だけです。

Cache - CacheLayerの取りまとめ

EntityManagerはCacheインスタンスを一つだけ持ち、このCacheインスタンスがすべてのCacheLayerインスタンスを管理します。
EntityManager#find()メソッドを呼び出してエンティティを取得すると、そのエンティティに対応するCacheLayerインスタンスが作成されます。もし、同じエンティティに対応するCacheLayerインスタンスがすでにある場合は、そのCacheLayerインスタンスが再利用されます。
エンティティを参照しなくなると、CacheLayerインスタンスは勝手にガーベジコレクションされます。これを実現するために、RAMCacheではCacheLayerインスタンスをWeakHashMapに格納しています。

実装クラスに関して

現在のところ、キャッシュを実装するクラスは「RAM〜」という名前のクラスしかありません。先にも言ったとおり、これはメモリ(RAM)中にデータをキャッシュするクラス群です。
EntityManagerのコンストラクタでRAMCacheインスタンスが作成され、デフォルトではこのインスタンスを使ってキャッシュ処理が行われます。
デフォルトでと言いましたが、実はEntityManager#setCache()メソッドでCacheインスタンスの再設定ができます。このメソッドを使えば独自に実装したCacheクラスにキャッシュを行わせることができそうです。

さいごに

ActiveObjectsではDBのアクセスを意識しなくても使えます。しかし、DBのアクセスが必要以上に頻繁に行われれば、当然パフォーマンスが悪くなってしまいます。不要なDBアクセスは減らしたいものです。
今回は、ActiveObjectsに不要なDBアクセスをさせないために、キャッシュの仕組みについて調べました。
キャッシュの仕組みは大体わかったので、今後はパフォーマンスを悪くしないActiveObjectsの使い方を調べようと思います。