Second level cache

Hi guys,

I believe, We are coming to an end.. ;)
https://github.com/doctrine/doctrine2/pull/580, rebased and reopened to make easier the final review.


### Cache strategies

    * READ_ONLY (DEFAULT)   : ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks.
    * NONSTRICT_READ_WRITE  : Nonstrict Read Write Cache doesn’t employ any locks but can do reads, inserts , updates and deletes.
    * READ_WRITE            : Read Write cache employs locks the entity before update/delete.


### classes / interfaces

* **Region** : 
    Defines a contract for accessing a entity/collection data cache. (Doesn’t employ any locks)

* **ConcurrentRegion** : 
    Defines contract for concurrently managed data region. (Locks the data before update/delete.)

* **TimestampRegion** :
     Holds timestamps of the most recent entities/tables updates. These timestamps are used as part of the cache for  persister queries (findAll, findBy, ...).

* **CacheKey / EntityCacheKey / CollectionCacheKey / QueryCacheKey / TimestampCacheKey**:
    Defines entity / collection key to be stored in the cache region.


* **EntityHydrator / CollectionHidrator**
    Build cache entries and rebuild entities/colection from cache

* **CacheFactory**
    Factory from second level cache components


### Collection Caching

The most common use case is to cache entities. But we can also cache relationships. 
A “collection cache” caches the primary keys of entities that are members of a collection (OneToMany/ManyToMany). 
and each element will be cached into its region.

Only identifiers will be cached for collection. When a collection is read from the second level cache it will create proxies based on the cached identifiers, if the application needs to access an element, Doctrine will go to the cache to load the element data.

### Query Cache

The query cache does not cache the state of the actual entities in the result set; 
it caches only identifier values for an individual query.
So the query cache should always be used in conjunction with the second-level cache.

### Persister Query ```(findAll, findBy, ...).```

As well as ```Query Cache``` all persister queries store only identifier values for an individual query.
All persister use a single timestamps cache region keeps track of the last update for each persister,
When a query is loaded from cache, the timestamp region is checked for the last update for that persister.
Using the last update timestamps as part of the query key invalidate the cache key when an update occurs. 


### OPERATIONS


#### INSERT :

    *************************************************************************************************
                        | READ-ONLY             | NONSTRICT-READ-WRITE      | READ-WRITE            |
    -------------------------------------------------------------------------------------------------
    pre-insert          |                       |                           |                       |
    -------------------------------------------------------------------------------------------------
    on-insert           |                       |                           |                       |
    -------------------------------------------------------------------------------------------------
    after-transaction   | add item to the cache | add item to the cache     | add item to the cache |
    -------------------------------------------------------------------------------------------------


#### UPDATE :

    *************************************************************************************************
                        | READ-ONLY             | NONSTRICT-READ-WRITE      | READ-WRITE            |
    -------------------------------------------------------------------------------------------------
    pre-update          |                       |                           | lock item             |
    -------------------------------------------------------------------------------------------------
    on-update           | throws exception      |                           |                       |
    -------------------------------------------------------------------------------------------------
    after-transaction   |                       |  update item cache        | remove item cache     |
    -------------------------------------------------------------------------------------------------


#### DELETE : 

    *************************************************************************************************
                        | READ-ONLY             | NONSTRICT-READ-WRITE      | READ-WRITE            |
    -------------------------------------------------------------------------------------------------
    pre-remove          |                       |                           |                       |
    -------------------------------------------------------------------------------------------------
    on-remove           |                       |                           | lock item             |
    -------------------------------------------------------------------------------------------------
    after-transaction   | remove item cache     |  remove item cache        | remove item cache     |
    -------------------------------------------------------------------------------------------------


#### USAGE : 
```php
<?php

/**
 * @Entity
 * @Cache("NONSTRICT_READ_WRITE")
 */
class State
{
    /**
     * @Id
     * @GeneratedValue
     * @Column(type="integer")
     */
    protected $id;
    /**
     * @Column
     */
    protected $name;
    /**
     * @Cache()
     * @ManyToOne(targetEntity="Country")
     * @JoinColumn(name="country_id", referencedColumnName="id")
     */
    protected $country;
    /**
     * @Cache()
     * @OneToMany(targetEntity="City", mappedBy="state")
     */
    protected $cities;
}
```

```php
<?php

$em->persist(new State($name, $country));
$em->flush();                                // Put into cache

$em->clear();                                // Clear entity manager

$state   = $em->find('Entity\State', 1);     // Retreive item from cache
$country = $state->getCountry();             // Retreive item from cache
$cities  = $state->getCities();              // Load from database and put into cache

$state->setName("New Name");
$em->persist($state);
$em->flush();                                // Update item cache

$em->clear();                                // Clear entity manager

$em->find('Entity\State', 1)->getCities();   // Retreive from cache


$em->getCache()->containsEntity('Entity\State', $state->getId())  // Check if the cache exists
$em->getCache()->evictEntity('Entity\State', $state->getId());    // Remove an entity from cache
$em->getCache()->evictEntityRegion('Entity\State');               // Remove all entities from cache

$em->getCache()->containsCollection('Entity\State', 'cities', $state->getId());   // Check if the cache exists        
$em->getCache()->evictCollection('Entity\State', 'cities', $state->getId());      // Remove an entity collection from cache
$em->getCache()->evictCollectionRegion('Entity\State', 'cities');                 // Remove all collections from cache

```


```php
<?php

    // Execute database query, store query cache and entity cache into 2lc
    $result = $em->createQuery('SELECT s FROM Entity\State s ORDER BY s.name')
        ->setCacheable(true)
        ->getResult();

```


```php
<?php

// load from database and store cache query key hashing the query + parameters + last timestamp cache region..
$entities   = $em->getRepository('Entity\Country')->findAll();

// load from cache..
$entities   = $em->getRepository('Entity\Country')->findAll();

// update the timestamp cache region for Country
$em->persist(new Country('zombieland'));
$em->flush();
$em->clear();  

// Reload the query from database.
// The timestamp cache region update invalidates the query key
$entities   = $em->getRepository('Entity\Country')->findAll();
```