8

私はこの問題と多くの時間をかけて戦っていますが、公式には、一部のカスタムクエリ(QueryオブジェクトのuseResultCache(true))しかキャッシュできないことがわかりました。ただし、アプリケーション内のすべてのクエリをいくつかのテーブルにキャッシュする必要があります。EntityManagerのfind*メソッドはどうですか?...

誰かが私がエレガントな解決策を見つけるのを手伝ってもらえますか?

4

1 に答える 1

12

これはまだサポートされていないため、最終的にはサービスレイヤーまたは拡張リポジトリで処理する必要があります。

探しているのは、Hibernateのような第2レベルのキャッシュです。これにより、基本的に、redis、riak、mongodbなどのキー値ストアをプラグインして、操作が単純なフェッチ操作である場合に物事を高速化できます。

https://github.com/doctrine/doctrine2/pull/580に進行中のプルリクエストがあります。これはおそらくDoctrineORM2.5に到達するので、それを確認してください。

use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\ArrayCache;

class MyBaseRepo extends EntityRepository
{
    public function __construct(EntityManager $entityManager)
    {
        parent::__construct($entityManager);
        $cache       = $em->getConfiguration()->getHydrationCache();
        $this->cache = $cache ?: new ArrayCache();
    }

    public function find($id)
    {
        if (!$object = $this->tryFetchFromCache($id)) {
            $object = parent::find($id);
            $this->cache->save($this->generateKey($id), $object);
        }

        return $object;
    }

    protected function tryFetchFromCache($id)
    {
        if (!$object = $this->cache->fetch($this->generateCacheKey($id))) {
            return null;
        }

        return $this->getEntityManager()->merge($object);
    }

    public function generateCacheKey($id) { /* ... */ }
}

アプリをブートストラップするときに、これを構成内のORMのベースリポジトリにすることができます。

$configuration = new \Doctrine\ORM\Configuration();

$configuration->setDefaultRepositoryClassName('My\MyBaseRepo');

これにより、エンティティのいずれかで更新/保存が発生したときに、キャッシュエントリを削除する必要があります。

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;

class IssueUpdateSubscriber implements EventSubscriber
{
    public function onFlush(OnFlushEventArgs $args)
    {
        $em  = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        if (!$cache = $em->getConfiguration()->getHydrationCache()) {
            return;
        }

        $busted = array_merge(
            $uow->getScheduledEntityUpdates(),
            $uow->getScheduledEntityDeletions(),
        );

        foreach ($busted as $entityToClear) {
            $className  = get_class($entityToClear);
            $metadata   = $em->getClassMetadata($className);
            $repository = $em->getRepository($className);
            $id         = $metadata->getIdentifierValues($entityToClear);

            $cache->delete($repository->generateCacheKey($id));
        }
    }

    public function getSubscribedEvents()
    {
        return array(Events::onFlush);
    }
}

この実装は、特定のエンティティのデータベースへのすべてのアクセスをインターセプトするわけではないことに注意してください。プロキシによって引き起こされる遅延読み込みの初期化をインターセプトせず、非常に壊れやすいため、それをサポートするためにいくつかの適切な統合テストを設定してください。

于 2013-03-29T11:51:14.993 に答える