diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 1895cfd..da433dd 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Core\Entity\Plugin\DataType\EntityReference;
+use Drupal\Core\Field\PrepareCacheInterface;
 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Session\AccountInterface;
@@ -156,7 +157,14 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans
     foreach ($this->getEntityType()->getKeys() as $key => $field_name) {
       if (isset($this->values[$field_name])) {
         if (is_array($this->values[$field_name]) && isset($this->values[$field_name][Language::LANGCODE_DEFAULT])) {
-          $this->entityKeys[$key] = $this->values[$field_name][Language::LANGCODE_DEFAULT];
+          if (is_array($this->values[$field_name][Language::LANGCODE_DEFAULT])) {
+            if (isset($this->values[$field_name][Language::LANGCODE_DEFAULT][0]['value'])) {
+              $this->entityKeys[$key] = $this->values[$field_name][Language::LANGCODE_DEFAULT][0]['value'];
+            }
+          }
+          else {
+            $this->entityKeys[$key] = $this->values[$field_name][Language::LANGCODE_DEFAULT];
+          }
         }
       }
     }
@@ -1036,6 +1044,45 @@ protected function getEntityKey($key) {
   /**
    * {@inheritdoc}
    */
+  public function getCacheData() {
+    // Start with all the values, so that the data will also include values
+    // which are not defined as fields.
+    // @todo: Remove support for this when all entity values are fields.
+    $data = $this->values;
+    $default_langcode = $this->getUntranslated()->language()->id;
+    foreach ($this->getTranslationLanguages() as $langcode => $language) {
+      $translation = $this->getTranslation($langcode);
+      // Make sure the default language is valid.
+      if ($default_langcode == $langcode) {
+        $langcode = Language::LANGCODE_DEFAULT;
+      }
+      foreach ($translation as $field_name => $items) {
+        if (!$items->isEmpty()) {
+          // Remove existing values for defined fields, so that their
+          // structure is standardized.
+          if (isset($data[$field_name][$langcode])) {
+            unset($data[$field_name][$langcode]);
+          }
+          foreach ($items as $delta => $item) {
+            // If the field item needs to be prepare the cache data, call
+            // the corresponding method, otherwise use the values as cache
+            // data.
+            if ($item instanceof PrepareCacheInterface) {
+              $data[$field_name][$langcode][$delta] = $item->getCacheData();
+            }
+            else {
+              $data[$field_name][$langcode][$delta] = $item->getValue();
+            }
+          }
+        }
+      }
+    }
+    return $data;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
     return array();
   }
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
index da6890e..cd63b65 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
@@ -7,9 +7,11 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\Query\QueryInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\PrepareCacheInterface;
 use Drupal\Core\Language\Language;
 use Drupal\field\FieldConfigUpdateForbiddenException;
 use Drupal\field\FieldConfigInterface;
@@ -72,13 +74,21 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase {
   protected $entityManager;
 
   /**
+   * Cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cacheBackend;
+
+  /**
    * {@inheritdoc}
    */
   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
     return new static(
       $entity_type,
       $container->get('database'),
-      $container->get('entity.manager')
+      $container->get('entity.manager'),
+      $container->get('cache.entity')
     );
   }
 
@@ -91,12 +101,15 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
    *   The database connection to be used.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager) {
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache) {
     parent::__construct($entity_type);
 
     $this->database = $database;
     $this->entityManager = $entity_manager;
+    $this->cacheBackend = $cache;
 
     // Check if the entity type supports UUIDs.
     $this->uuidKey = $this->entityType->getKey('uuid');
@@ -121,13 +134,167 @@ public function __construct(EntityTypeInterface $entity_type, Connection $databa
    * {@inheritdoc}
    */
   protected function doLoadMultiple(array $ids = NULL) {
-    // Build and execute the query.
-    $records = $this
-      ->buildQuery($ids)
-      ->execute()
-      ->fetchAllAssoc($this->idKey);
+    // Attempt to load entities from the persistent cache. This will remove IDs
+    // that were loaded from $ids.
+    $entities_from_cache = $this->getFromPersistentCache($ids);
 
-    return $this->mapFromStorageRecords($records);
+    // Load any remaining entities from the database.
+    $entities_from_storage = $this->getFromStorage($ids);
+    $this->setPersistentCache($entities_from_storage);
+
+    return $entities_from_cache + $entities_from_storage;
+  }
+
+  /**
+   * Gets entities from the storage.
+   *
+   * @param array|null $ids
+   *   If not empty, return entities that match these IDs. Return all entities
+   *   when NULL.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityInterface[]
+   *   Array of entities from the storage.
+   */
+  protected function getFromStorage(array $ids = NULL) {
+    $entities = array();
+
+    if ($ids === NULL || $ids) {
+      // Build and execute the query.
+      $query_result = $this->buildQuery($ids)->execute();
+      $records = $query_result->fetchAllAssoc($this->idKey);
+
+      // Map the loaded records into entity objects and according fields.
+      if ($records) {
+        $entities = $this->mapFromStorageRecords($records);
+
+        // Call hook_entity_storage_load().
+        foreach ($this->moduleHandler()->getImplementations('entity_storage_load') as $module) {
+          $function = $module . '_entity_storage_load';
+          $function($entities, $this->entityTypeId);
+        }
+        // Call hook_TYPE_storage_load().
+        foreach ($this->moduleHandler()->getImplementations($this->entityTypeId . '_storage_load') as $module) {
+          $function = $module . '_' . $this->entityTypeId . '_storage_load';
+          $function($entities);
+        }
+      }
+    }
+
+    return $entities;
+  }
+
+  /**
+   * Gets entities from the persistent cache.
+   *
+   * @param array &$ids
+   *   If not empty, return entities that match these IDs. IDs that were found
+   *   will be removed from the list.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityInterface[]
+   *   Array of entities from the entity cache.
+   */
+  protected function getFromPersistentCache(&$ids) {
+    if (!$this->entityType->isPersistentlyCacheable() || empty($ids)) {
+      return array();
+    }
+    $entities = array();
+    // Build the list of cache entries to retrieve.
+    $cids = array();
+    foreach ($ids as $id) {
+      $cids[] = $this->buildCacheId($id);
+    }
+    if ($cache = $this->cacheBackend->getMultiple($cids)) {
+      // Create entity objects based on the loaded values from the cache.
+      foreach ($ids as $index => $id) {
+        $cid = $this->buildCacheId($id);
+        if (isset($cache[$cid])) {
+          $values = $cache[$cid]->data['values'];
+          $translations = $cache[$cid]->data['translations'];
+          $bundle = $this->bundleKey ? $cache[$cid]->data['bundle'] : FALSE;
+          $entities[$id] = new $this->entityClass($values, $this->entityTypeId, $bundle, $translations);
+          unset($ids[$index]);
+        }
+      }
+    }
+    return $entities;
+  }
+
+  /**
+   * Stores entities in the persistent entity cache.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
+   *   Entities to store in the cache.
+   */
+  protected function setPersistentCache($entities) {
+    if (!$this->entityType->isPersistentlyCacheable()) {
+      return;
+    }
+
+    foreach ($entities as $id => $entity) {
+      $data = array(
+        'id' => $entity->id(),
+        'bundle' => $entity->bundle(),
+        'translations' => array_keys($entity->getTranslationLanguages()),
+        'values' => $entity->getCacheData(),
+      );
+      $this->cacheBackend->set($this->buildCacheId($id), $data, CacheBackendInterface::CACHE_PERMANENT, array($this->entityTypeId . '_values' => TRUE));
+    }
+  }
+
+  /**
+   * Invokes hook_entity_load_uncached().
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
+   *   List of entities, keyed on the entity ID.
+   */
+  protected function invokeLoadUncachedHook(array &$entities) {
+    if (!empty($entities)) {
+      // Call hook_entity_load_uncached().
+      foreach ($this->moduleHandler()->getImplementations('entity_load_uncached') as $module) {
+        $function = $module . '_entity_load_uncached';
+        $function($entities, $this->entityTypeId);
+      }
+      // Call hook_TYPE_load_uncached().
+      foreach ($this->moduleHandler()->getImplementations($this->entityTypeId . '_load_uncached') as $module) {
+        $function = $module . '_' . $this->entityTypeId . '_load_uncached';
+        $function($entities);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetCache(array $ids = NULL) {
+    if ($ids) {
+      $cids = array();
+      foreach ($ids as $id) {
+        unset($this->entities[$id]);
+        $cids[] = $this->buildCacheId($id);
+      }
+      if ($this->entityType->isPersistentlyCacheable()) {
+        $this->cacheBackend->deleteMultiple($cids);
+      }
+    }
+    else {
+      $this->entities = array();
+      if ($this->entityType->isPersistentlyCacheable()) {
+        $this->cacheBackend->deleteTags(array($this->entityTypeId . '_values' => TRUE));
+      }
+    }
+  }
+
+  /**
+   * Returns the cache ID for the passed in entity ID.
+   *
+   * @param int $id
+   *   Entity ID for which the cache ID should be built.
+   *
+   * @return string
+   *   Cache ID that can be passed to the cache backend.
+   */
+  protected function buildCacheId($id) {
+    return "values:{$this->entityTypeId}:$id";
   }
 
   /**
@@ -697,9 +864,26 @@ public function getQueryServiceName() {
   }
 
   /**
-   * {@inheritdoc}
+   * Loads values of configurable fields for a group of entities.
+   *
+   * Loads all fields for each entity object in a group of a single entity type.
+   * The loaded field values are added directly to the entity objects.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
+   *   An array of entities keyed by entity ID.
    */
-  protected function doLoadFieldItems($entities, $age) {
+  protected function loadFieldItems(array $entities) {
+    if (empty($entities) || !$this->entityType->isFieldable()) {
+      return;
+    }
+
+    $age = static::FIELD_LOAD_CURRENT;
+    foreach ($entities as $entity) {
+      if (!$entity->isDefaultRevision()) {
+        $age = static::FIELD_LOAD_REVISION;
+        break;
+      }
+    }
     $load_current = $age == static::FIELD_LOAD_CURRENT;
 
     // Collect entities ids, bundles and languages.
@@ -768,9 +952,14 @@ protected function doLoadFieldItems($entities, $age) {
   }
 
   /**
-   * {@inheritdoc}
+   * Saves values of configurable fields for an entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   * @param bool $update
+   *   TRUE if the entity is being updated, FALSE if it is being inserted.
    */
-  protected function doSaveFieldItems(EntityInterface $entity, $update) {
+  protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
     $vid = $entity->getRevisionId();
     $id = $entity->id();
     $bundle = $entity->bundle();
@@ -856,9 +1045,12 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) {
   }
 
   /**
-   * {@inheritdoc}
+   * Deletes values of configurable fields for all revisions of an entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
    */
-  protected function doDeleteFieldItems(EntityInterface $entity) {
+  protected function deleteFieldItems(EntityInterface $entity) {
     foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $instance) {
       if (!($instance instanceof FieldInstanceConfigInterface)) {
         continue;
@@ -876,9 +1068,12 @@ protected function doDeleteFieldItems(EntityInterface $entity) {
   }
 
   /**
-   * {@inheritdoc}
+   * Deletes values of configurable fields for a single revision of an entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity. It must have a revision ID.
    */
-  protected function doDeleteFieldItemsRevision(EntityInterface $entity) {
+  protected function deleteFieldItemsRevision(EntityInterface $entity) {
     $vid = $entity->getRevisionId();
     if (isset($vid)) {
       foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $instance) {
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
index 654fc5e..b5f51ce 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Field\PrepareCacheInterface;
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 
@@ -26,7 +27,7 @@
  * @see \Drupal\Core\TypedData\TypedDataManager
  * @see \Drupal\Core\Field\FieldItemListInterface
  */
-interface ContentEntityInterface extends EntityInterface, RevisionableInterface, TranslatableInterface, ComplexDataInterface {
+interface ContentEntityInterface extends EntityInterface, RevisionableInterface, TranslatableInterface, ComplexDataInterface, PrepareCacheInterface {
 
   /**
    * Marks the translation identified by the given language code as existing.
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index b40f73a..049902a 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -8,7 +8,6 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Component\Utility\String;
-use Drupal\Core\Field\PrepareCacheInterface;
 use Drupal\field\FieldConfigInterface;
 use Drupal\field\FieldInstanceConfigInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -75,206 +74,6 @@ protected function doCreate(array $values) {
   }
 
   /**
-   * Loads values of configurable fields for a group of entities.
-   *
-   * Loads all fields for each entity object in a group of a single entity type.
-   * The loaded field values are added directly to the entity objects.
-   *
-   * This method is a wrapper that handles the field data cache. Subclasses
-   * need to implement the doLoadFieldItems() method with the actual storage
-   * logic.
-   *
-   * @param array $entities
-   *   An array of entities keyed by entity ID.
-   */
-  protected function loadFieldItems(array $entities) {
-    if (empty($entities)) {
-      return;
-    }
-
-    $age = static::FIELD_LOAD_CURRENT;
-    foreach ($entities as $entity) {
-      if (!$entity->isDefaultRevision()) {
-        $age = static::FIELD_LOAD_REVISION;
-        break;
-      }
-    }
-
-    // Only the most current revision of non-deleted fields for cacheable entity
-    // types can be cached.
-    $load_current = $age == static::FIELD_LOAD_CURRENT;
-    $use_cache = $load_current && $this->entityType->isFieldDataCacheable();
-
-    // Assume all entities will need to be queried. Entities found in the cache
-    // will be removed from the list.
-    $queried_entities = $entities;
-
-    // Fetch available entities from cache, if applicable.
-    if ($use_cache) {
-      // Build the list of cache entries to retrieve.
-      $cids = array();
-      foreach ($entities as $id => $entity) {
-        $cids[] = "field:{$this->entityTypeId}:$id";
-      }
-      $cache = \Drupal::cache('entity')->getMultiple($cids);
-      // Put the cached field values back into the entities and remove them from
-      // the list of entities to query.
-      foreach ($entities as $id => $entity) {
-        $cid = "field:{$this->entityTypeId}:$id";
-        if (isset($cache[$cid])) {
-          unset($queried_entities[$id]);
-          foreach ($cache[$cid]->data as $langcode => $values) {
-            $translation = $entity->getTranslation($langcode);
-            // We do not need to worry about field translatability here, the
-            // translation object will manage that automatically.
-            foreach ($values as $field_name => $items) {
-              $translation->$field_name = $items;
-            }
-          }
-        }
-      }
-    }
-
-    // Fetch other entities from their storage location.
-    if ($queried_entities) {
-      // Let the storage actually load the values.
-      $this->doLoadFieldItems($queried_entities, $age);
-
-      // Build cache data.
-      // @todo: Improve this logic to avoid instantiating field objects once
-      // the field logic is improved to not do that anyway.
-      if ($use_cache) {
-        foreach ($queried_entities as $id => $entity) {
-          $data = array();
-          foreach ($entity->getTranslationLanguages() as $langcode => $language) {
-            $translation = $entity->getTranslation($langcode);
-            foreach ($translation as $field_name => $items) {
-              if ($items->getFieldDefinition() instanceof FieldInstanceConfigInterface && !$items->isEmpty()) {
-                foreach ($items as $delta => $item) {
-                  // If the field item needs to prepare the cache data, call the
-                  // corresponding method, otherwise use the values as cache
-                  // data.
-                  if ($item instanceof PrepareCacheInterface) {
-                    $data[$langcode][$field_name][$delta] = $item->getCacheData();
-                  }
-                  else {
-                    $data[$langcode][$field_name][$delta] = $item->getValue();
-                  }
-                }
-              }
-            }
-          }
-          $cid = "field:{$this->entityTypeId}:$id";
-          \Drupal::cache('entity')->set($cid, $data);
-        }
-      }
-    }
-  }
-
-  /**
-   * Saves values of configurable fields for an entity.
-   *
-   * This method is a wrapper that handles the field data cache. Subclasses
-   * need to implement the doSaveFieldItems() method with the actual storage
-   * logic.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity.
-   * @param bool $update
-   *   TRUE if the entity is being updated, FALSE if it is being inserted.
-   */
-  protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
-    $this->doSaveFieldItems($entity, $update);
-
-    if ($update) {
-      $entity_type = $entity->getEntityType();
-      if ($entity_type->isFieldDataCacheable()) {
-        \Drupal::cache('entity')->delete('field:' . $entity->getEntityTypeId() . ':' . $entity->id());
-      }
-    }
-  }
-
-  /**
-   * Deletes values of configurable fields for all revisions of an entity.
-   *
-   * This method is a wrapper that handles the field data cache. Subclasses
-   * need to implement the doDeleteFieldItems() method with the actual storage
-   * logic.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity.
-   */
-  protected function deleteFieldItems(EntityInterface $entity) {
-    $this->doDeleteFieldItems($entity);
-
-    $entity_type = $entity->getEntityType();
-    if ($entity_type->isFieldDataCacheable()) {
-      \Drupal::cache('entity')->delete('field:' . $entity->getEntityTypeId() . ':' . $entity->id());
-    }
-  }
-
-  /**
-   * Deletes values of configurable fields for a single revision of an entity.
-   *
-   * This method is a wrapper that handles the field data cache. Subclasses
-   * need to implement the doDeleteFieldItemsRevision() method with the actual
-   * storage logic.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity. It must have a revision ID attribute.
-   */
-  protected function deleteFieldItemsRevision(EntityInterface $entity) {
-    $this->doDeleteFieldItemsRevision($entity);
-  }
-
-  /**
-   * Loads values of configurable fields for a group of entities.
-   *
-   * This is the method that holds the actual storage logic.
-   *
-   * @param array $entities
-   *   An array of entities keyed by entity ID.
-   * @param int $age
-   *   EntityStorageInterface::FIELD_LOAD_CURRENT to load the most
-   *   recent revision for all fields, or
-   *   EntityStorageInterface::FIELD_LOAD_REVISION to load the version
-   *   indicated by each entity.
-   */
-  abstract protected function doLoadFieldItems($entities, $age);
-
-  /**
-   * Saves values of configurable fields for an entity.
-   *
-   * This is the method that holds the actual storage logic.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity.
-   * @param bool $update
-   *   TRUE if the entity is being updated, FALSE if it is being inserted.
-   */
-  abstract protected function doSaveFieldItems(EntityInterface $entity, $update);
-
-  /**
-   * Deletes values of configurable fields for all revisions of an entity.
-   *
-   * This is the method that holds the actual storage logic.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity.
-   */
-  abstract protected function doDeleteFieldItems(EntityInterface $entity);
-
-  /**
-   * Deletes values of configurable fields for a single revision of an entity.
-   *
-   * This is the method that holds the actual storage logic.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity.
-   */
-  abstract protected function doDeleteFieldItemsRevision(EntityInterface $entity);
-
-  /**
    * {@inheritdoc}
    */
   public function onFieldCreate(FieldConfigInterface $field) { }
diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index 4382665..1c2a3b0 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -38,7 +38,7 @@ class EntityType implements EntityTypeInterface {
    *
    * @var bool
    */
-  protected $field_cache;
+  protected $persistent_cache;
 
   /**
    * An array of entity keys.
@@ -249,8 +249,8 @@ public function isRenderCacheable() {
   /**
    * {@inheritdoc}
    */
-  public function isFieldDataCacheable() {
-    return isset($this->field_cache) ? $this->field_cache: TRUE;
+  public function isPersistentlyCacheable() {
+    return isset($this->persistent_cache) ? $this->persistent_cache: TRUE;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
index a6e39aa..d92e00b 100644
--- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -153,7 +153,7 @@ public function isRenderCacheable();
    *
    * @return bool
    */
-  public function isFieldDataCacheable();
+  public function isPersistentlyCacheable();
 
   /**
    * Sets the name of the entity type class.
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 18f0a57..ab564d0 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -855,11 +855,11 @@ function comment_translation_configuration_element_submit($form, &$form_state) {
 }
 
 /**
- * Implements hook_entity_load().
+ * Implements hook_entity_storage_load().
  *
  * @see \Drupal\comment\Plugin\Field\FieldType\CommentItem::propertyDefinitions()
  */
-function comment_entity_load($entities, $entity_type) {
+function comment_entity_storage_load($entities, $entity_type) {
   // Comments can only be attached to content entities, so skip others.
   if (!\Drupal::entityManager()->getDefinition($entity_type)->isSubclassOf('Drupal\Core\Entity\ContentEntityInterface')) {
     return;
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStatistics.php b/core/modules/comment/lib/Drupal/comment/CommentStatistics.php
index 48505c4..c42cdc4 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStatistics.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStatistics.php
@@ -243,6 +243,10 @@ public function update(CommentInterface $comment) {
         ->condition('field_id', $comment->getFieldId())
         ->execute();
     }
+
+    // Reset the cache of the commented entity so that when the entity is loaded
+    // the next time, the statistics will be loaded again.
+    $this->entityManager->getStorage($comment->getCommentedEntityTypeId())->resetCache(array($comment->getCommentedEntityId()));
   }
 
 }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorage.php b/core/modules/comment/lib/Drupal/comment/CommentStorage.php
index 88a1bd4..5c6c288 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorage.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorage.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\comment;
 
+use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
@@ -38,11 +39,13 @@ class CommentStorage extends ContentEntityDatabaseStorage implements CommentStor
    *   The database connection to be used.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
    * @param \Drupal\comment\CommentStatisticsInterface $comment_statistics
    *   The comment statistics service.
    */
-  public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, CommentStatisticsInterface $comment_statistics) {
-    parent::__construct($entity_info, $database, $entity_manager);
+  public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, CommentStatisticsInterface $comment_statistics) {
+    parent::__construct($entity_info, $database, $entity_manager, $cache);
     $this->statistics = $comment_statistics;
   }
 
@@ -54,6 +57,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $entity_info,
       $container->get('database'),
       $container->get('entity.manager'),
+      $container->get('cache.entity'),
       $container->get('comment.statistics')
     );
   }
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 502d4e4..70d1eb2 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -501,9 +501,9 @@ function content_translation_language_fallback_candidates_entity_view_alter(&$ca
 }
 
 /**
- * Implements hook_entity_load().
+ * Implements hook_entity_storage_load().
  */
-function content_translation_entity_load(array $entities, $entity_type) {
+function content_translation_entity_storage_load(array $entities, $entity_type) {
   $enabled_entities = array();
 
   if (content_translation_enabled($entity_type)) {
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index bdde395..a479698 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -292,7 +292,9 @@ function field_modules_uninstalled($modules) {
  * Clears the field info and field data caches.
  */
 function field_cache_clear() {
-  \Drupal::cache('entity')->deleteAll();
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type) {
+    \Drupal::entityManager()->getStorage($entity_type->id())->resetCache();
+  }
   field_info_cache_clear();
 }
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
index 39f6e28..f2ce7fa 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
@@ -180,12 +180,12 @@ function testEntityDisplayViewMultiple() {
   function testFieldAttachCache() {
     // Initialize random values and a test entity.
     $entity_init = entity_create('entity_test', array('type' => $this->instance->bundle));
-    $langcode = Language::LANGCODE_NOT_SPECIFIED;
+    $langcode = Language::LANGCODE_DEFAULT;
     $values = $this->_generateTestFieldValues($this->field->getCardinality());
 
     // Non-cacheable entity type.
     $entity_type = 'entity_test';
-    $cid = "field:$entity_type:" . $entity_init->id();
+    $cid = "values:$entity_type:" . $entity_init->id();
 
     // Check that no initial cache entry is present.
     $this->assertFalse(\Drupal::cache('entity')->get($cid), 'Non-cached: no initial cache entry');
@@ -194,7 +194,7 @@ function testFieldAttachCache() {
     $entity = clone($entity_init);
     $entity->{$this->field_name}->setValue($values);
     $entity = $this->entitySaveReload($entity);
-    $cid = "field:$entity_type:" . $entity->id();
+    $cid = "values:$entity_type:" . $entity->id();
     $this->assertFalse(\Drupal::cache('entity')->get($cid), 'Non-cached: no cache entry on insert and load');
 
     // Cacheable entity type.
@@ -207,14 +207,14 @@ function testFieldAttachCache() {
     ));
 
     // Check that no initial cache entry is present.
-    $cid = "field:$entity_type:" . $entity->id();
+    $cid = "values:$entity_type:" . $entity->id();
     $this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no initial cache entry');
 
     // Save, and check that no cache entry is present.
     $entity = clone($entity_init);
     $entity->{$this->field_name_2} = $values;
     $entity->save();
-    $cid = "field:$entity_type:" . $entity->id();
+    $cid = "values:$entity_type:" . $entity->id();
 
     $this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no cache entry on insert');
     // Load, and check that a cache entry is present with the expected values.
@@ -222,7 +222,7 @@ function testFieldAttachCache() {
     $controller->resetCache();
     $controller->load($entity->id());
     $cache = \Drupal::cache('entity')->get($cid);
-    $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load');
+    $this->assertEqual($cache->data['values'][$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Update with different values, and check that the cache entry is wiped.
     $values = $this->_generateTestFieldValues($this->field_2->getCardinality());
@@ -234,7 +234,7 @@ function testFieldAttachCache() {
     $controller->resetCache();
     $controller->load($entity->id());
     $cache = \Drupal::cache('entity')->get($cid);
-    $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load');
+    $this->assertEqual($cache->data['values'][$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Create a new revision, and check that the cache entry is wiped.
     $values = $this->_generateTestFieldValues($this->field_2->getCardinality());
@@ -247,7 +247,7 @@ function testFieldAttachCache() {
     $controller->resetCache();
     $controller->load($entity->id());
     $cache = \Drupal::cache('entity')->get($cid);
-    $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load');
+    $this->assertEqual($cache->data['values'][$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Delete, and check that the cache entry is wiped.
     $entity->delete();
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 7580d9b..35b529e 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -1902,15 +1902,14 @@ function file_get_file_references(File $file, FieldDefinitionInterface $field =
           $file_fields[$entity_type_id][$bundle] = array();
           // This contains the possible field names.
           foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) {
-            $field_type = $field_definition->getType();
             // If this is the first time this field type is seen, check
             // whether it references files.
-            if (!isset($field_columns[$field_type])) {
-              $field_columns[$field_type] = file_field_find_file_reference_column($field_definition);
+            if (!isset($field_columns[$field_definition->getType()])) {
+              $field_columns[$field_definition->getType()] = file_field_find_file_reference_column($field_definition);
             }
             // If the field type does reference files then record it.
-            if ($field_columns[$field_type]) {
-              $file_fields[$entity_type_id][$bundle][$field_name] = $field_columns[$field_type];
+            if ($field_columns[$field_definition->getType()]) {
+              $file_fields[$entity_type_id][$bundle][$field_name] = $field_columns[$field_definition->getType()];
             }
           }
         }
diff --git a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
index afb0114..9ce60a5 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
@@ -31,6 +31,7 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
+    node_access_test_add_field(entity_load('node_type', 'article'));
     node_access_rebuild();
     \Drupal::state()->set('node_access_test.private', TRUE);
   }
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 1b9b8f8..2d55ba9 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -383,9 +383,9 @@ function forum_node_predelete(EntityInterface $node) {
 }
 
 /**
- * Implements hook_node_load().
+ * Implements hook_node_storage_load().
  */
-function forum_node_load($nodes) {
+function forum_node_storage_load($nodes) {
   $node_vids = array();
   foreach ($nodes as $node) {
     if (\Drupal::service('forum_manager')->checkNodeType($node)) {
diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumNodeAccessTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumNodeAccessTest.php
index 99f8b25..5e7ff26 100644
--- a/core/modules/forum/lib/Drupal/forum/Tests/ForumNodeAccessTest.php
+++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumNodeAccessTest.php
@@ -32,6 +32,7 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
     node_access_rebuild();
+    node_access_test_add_field(entity_load('node_type', 'forum'));
     \Drupal::state()->set('node_access_test.private', TRUE);
   }
 
@@ -54,7 +55,7 @@ function testForumNodeAccess() {
     $edit = array(
       'title[0][value]' => $private_node_title,
       'body[0][value]' => $this->randomName(200),
-      'private' => TRUE,
+      'private[0][value]' => TRUE,
     );
     $this->drupalPostForm('node/add/forum', $edit, t('Save'), array('query' => array('forum_id' => 1)));
     $private_node = $this->drupalGetNodeByTitle($private_node_title);
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
index f227792..57f12b2 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
@@ -39,6 +39,8 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
+    node_access_test_add_field(entity_load('node_type', 'article'));
+
     node_access_rebuild();
     \Drupal::state()->set('node_access_test.private', TRUE);
   }
@@ -75,7 +77,7 @@ function testNodeAccessBasic() {
           'title[0][value]' => t('@private_public Article created by @user', array('@private_public' => $type, '@user' => $this->webUser->getUsername())),
         );
         if ($is_private) {
-          $edit['private'] = TRUE;
+          $edit['private[0][value]'] = TRUE;
           $edit['body[0][value]'] = 'private node';
           $edit['field_tags'] = 'private';
         }
@@ -85,14 +87,13 @@ function testNodeAccessBasic() {
         }
 
         $this->drupalPostForm('node/add/article', $edit, t('Save'));
-        $nid = db_query('SELECT nid FROM {node_field_data} WHERE title = :title', array(':title' => $edit['title[0][value]']))->fetchField();
-        $private_status = db_query('SELECT private FROM {node_access_test} where nid = :nid', array(':nid' => $nid))->fetchField();
-        $this->assertTrue($is_private == $private_status, 'The private status of the node was properly set in the node_access_test table.');
+        $node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
+        $this->assertEqual($is_private, (int)$node->private->value, 'The private status of the node was properly set in the node_access_test table.');
         if ($is_private) {
-          $private_nodes[] = $nid;
+          $private_nodes[] = $node->id();
         }
-        $titles[$nid] = $edit['title[0][value]'];
-        $this->nodesByUser[$this->webUser->id()][$nid] = $is_private;
+        $titles[$node->id()] = $edit['title[0][value]'];
+        $this->nodesByUser[$this->webUser->id()][$node->id()] = $is_private;
       }
     }
     $this->publicTid = db_query('SELECT tid FROM {taxonomy_term_data} WHERE name = :name', array(':name' => 'public'))->fetchField();
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareCombinationTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareCombinationTest.php
index 4ea9fe8..46fdbbb 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareCombinationTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareCombinationTest.php
@@ -53,6 +53,8 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
+    node_access_test_add_field(entity_load('node_type', 'page'));
+
     // Create the 'private' field, which allows the node to be marked as private
     // (restricted access) in a given translation.
     $field_private = entity_create('field_config', array(
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php
index feeae45..1de38ec 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php
@@ -32,6 +32,8 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
+    node_access_test_add_field(entity_load('node_type', 'page'));
+
     // After enabling a node access module, the access table has to be rebuild.
     node_access_rebuild();
 
diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.install b/core/modules/node/tests/modules/node_access_test/node_access_test.install
deleted file mode 100644
index 6b3ef5d..0000000
--- a/core/modules/node/tests/modules/node_access_test/node_access_test.install
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * @file
- * Install, update and uninstall functions for the node_access_test module.
- */
-
-/**
- * Implements hook_schema().
- */
-function node_access_test_schema() {
-  $schema['node_access_test'] = array(
-    'description' => 'The base table for node_access_test.',
-    'fields' => array(
-      'nid' => array(
-        'description' => 'The {node}.nid this record affects.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'private' => array(
-        'description' => 'Boolean indicating whether the node is private (visible to administrator) or not (visible to non-administrators).',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-    ),
-    'indexes' => array(
-      'nid' => array('nid'),
-    ),
-    'primary key' => array('nid'),
-    'foreign keys' => array(
-      'versioned_node' => array(
-        'table' => 'node',
-        'columns' => array('nid' => 'nid'),
-      ),
-    ),
-  );
-
-  return $schema;
-}
\ No newline at end of file
diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module
index 0d9494e..873921b 100644
--- a/core/modules/node/tests/modules/node_access_test/node_access_test.module
+++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module
@@ -9,8 +9,7 @@
  * a special 'node test view' permission.
  */
 
-use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Field\FieldDefinition;
+use Drupal\node\NodeTypeInterface;
 use Drupal\node\NodeInterface;
 
 /**
@@ -79,74 +78,32 @@ function node_access_test_permission() {
 }
 
 /**
- * Implements hook_entity_base_field_info().
+ * Adds the private field to a node type
+ *
+ * @param \Drupal\node\NodeTypeInterface $type
+ *   A node type object.
  */
-function node_access_test_entity_base_field_info(EntityTypeInterface $entity_type) {
-  if ($entity_type->id() === 'node') {
-    $fields['private'] = FieldDefinition::create('boolean')
-      ->setLabel(t('Private'))
-      ->setComputed(TRUE);
-
-    return $fields;
-  }
-}
-
-/**
- * Implements hook_form_BASE_FORM_ID_alter().
- */
-function node_access_test_form_node_form_alter(&$form, $form_state) {
-  // Only show this checkbox for NodeAccessBaseTableTestCase.
-  if (\Drupal::state()->get('node_access_test.private')) {
-    $node = $form_state['controller']->getEntity($form_state);
-    $form['private'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Private'),
-      '#description' => t('Check here if this content should be set private and only shown to privileged users.'),
-      '#default_value' => $node->private->value,
-    );
-  }
-}
-
-/**
- * Implements hook_node_load().
- */
-function node_access_test_node_load($nodes) {
-  $result = db_query('SELECT nid, private FROM {node_access_test} WHERE nid IN(:nids)', array(':nids' => array_keys($nodes)));
-  foreach ($result as $record) {
-    $nodes[$record->nid]->private = $record->private;
-  }
-}
-
-/**
- * Implements hook_node_predelete().
- */
-
-function node_access_test_node_predelete(NodeInterface $node) {
-  db_delete('node_access_test')->condition('nid', $node->id())->execute();
-}
-
-/**
- * Implements hook_node_insert().
- */
-function node_access_test_node_insert(NodeInterface $node) {
-  _node_access_test_node_write($node);
-}
-
-/**
- * Implements hook_nodeapi_update().
- */
-function node_access_test_node_update(NodeInterface $node) {
-  _node_access_test_node_write($node);
-}
-
-/**
- * Helper for node insert/update.
- */
-function _node_access_test_node_write(NodeInterface $node) {
-  db_merge('node_access_test')
-    ->key('nid', $node->id())
-    ->fields(array('private' => (int) $node->private->value))
-    ->execute();
+function node_access_test_add_field(NodeTypeInterface $type) {
+  $field = entity_create('field_config', array(
+    'name' => 'private',
+    'entity_type' => 'node',
+    'type' => 'integer',
+  ));
+  $field->save();
+  $instance = entity_create('field_instance_config', array(
+    'field_name' => 'private',
+    'entity_type' => 'node',
+    'bundle' => $type->id(),
+    'label' => 'Private',
+  ));
+  $instance->save();
+
+  // Assign widget settings for the 'default' form mode.
+  entity_get_form_display('node', $type->id(), 'default')
+    ->setComponent('private', array(
+      'type' => 'number',
+    ))
+    ->save();
 }
 
 /**
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index 8ea735b..f03b28f 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -224,9 +224,9 @@ function rdf_entity_prepare_view($entity_type, array $entities, array $displays)
 }
 
 /**
- * Implements hook_comment_load().
+ * Implements hook_comment_storage_load().
  */
-function rdf_comment_load($comments) {
+function rdf_comment_storage_load($comments) {
   foreach ($comments as $comment) {
     // Pages with many comments can show poor performance. This information
     // isn't needed until rdf_preprocess_comment() is called, but set it here
diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php
index 50b9f6a..c10b90d 100644
--- a/core/modules/system/entity.api.php
+++ b/core/modules/system/entity.api.php
@@ -269,18 +269,55 @@ function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
  * This is a generic load hook called for all entity types loaded via the
  * entity API.
  *
- * @param array $entities
+ * The hook will not be invoked for entities loaded from the cache. Use
+ * hook_entity_load_uncached() if necessary.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface[] $entities
  *   The entities keyed by entity ID.
  * @param string $entity_type_id
  *   The type of entities being loaded (i.e. node, user, comment).
  */
-function hook_entity_load($entities, $entity_type_id) {
+function hook_entity_load(array $entities, $entity_type_id) {
   foreach ($entities as $entity) {
     $entity->foo = mymodule_add_something($entity);
   }
 }
 
 /**
+ * Act on entities when loaded from the storage or cache.
+ *
+ * Only use this hook if the result must not be cached.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface[] $entities
+ *   The entities keyed by entity ID.
+ * @param string $entity_type
+ *   The type of entities being loaded (i.e. node, user, comment).
+ *
+ * @see hook_entity_load_uncached()
+ */
+function hook_entity_load_uncached(array $entities, $entity_type) {
+  foreach ($entities as $entity) {
+    $entity->foo = mymodule_add_something_uncached($entity);
+  }
+}
+
+/**
+ * Act on entities of the given type when loaded from the storage or cache.
+ *
+ * Only use this hook if the result must not be cached.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface[] $entities
+ *   The entities keyed by entity ID.
+ *
+ * @see hook_entity_load_uncached()
+ */
+function hook_ENTITY_TYPE_load_uncached(array $entities) {
+  foreach ($entities as $entity) {
+    $entity->foo = mymodule_add_something_uncached($entity);
+  }
+}
+
+/**
  * Act on an entity before it is about to be created or updated.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
index cd92191..b533d7c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
@@ -83,11 +83,7 @@ function testUpdateAccess() {
     $this->assertResponse(200);
 
     // Access the update page as user 1.
-    $user1 = user_load(1);
-    $user1->pass_raw = user_password();
-    $user1->pass = $this->container->get('password')->hash(trim($user1->pass_raw));
-    db_query("UPDATE {users} SET pass = :pass WHERE uid = :uid", array(':pass' => $user1->getPassword(), ':uid' => $user1->id()));
-    $this->drupalLogin($user1);
+    $this->drupalLogin($this->root_user);
     $this->drupalGet($this->update_url, array('external' => TRUE));
     $this->assertResponse(200);
   }
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
index 01e6872..7b7efaf 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
@@ -32,7 +32,7 @@
  *   },
  *   base_table = "entity_test",
  *   fieldable = TRUE,
- *   field_cache = FALSE,
+ *   persistent_cache = FALSE,
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestCache.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestCache.php
index 07d0e75..ab5fdab 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestCache.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestCache.php
@@ -22,7 +22,6 @@
  *   },
  *   base_table = "entity_test",
  *   fieldable = TRUE,
- *   field_cache = TRUE,
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
index 481711f..59d4574 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
@@ -13,7 +13,7 @@
  * @ContentEntityType(
  *   id = "entity_test_label_callback",
  *   label = @Translation("Entity test label callback"),
- *   field_cache = FALSE,
+ *   persistent_cache = FALSE,
  *   base_table = "entity_test",
  *   revision_table = "entity_test_revision",
  *   label_callback = "entity_test_label_callback",
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestNoLabel.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestNoLabel.php
index 6dc35d4..5ad3ba7 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestNoLabel.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestNoLabel.php
@@ -13,7 +13,7 @@
  * @ContentEntityType(
  *   id = "entity_test_no_label",
  *   label = @Translation("Entity Test without label"),
- *   field_cache = FALSE,
+ *   persistent_cache = FALSE,
  *   base_table = "entity_test",
  *   entity_keys = {
  *     "id" = "id",
diff --git a/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
index 9b93095..d431776 100644
--- a/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
@@ -126,38 +126,36 @@ function testProcessedCache() {
     // Load the entity and check that the field cache contains the expected
     // data.
     $entity = entity_load($entity_type, $entity->id());
-    $cache = \Drupal::cache('entity')->get("field:$entity_type:" . $entity->id());
-    $this->assertEqual($cache->data, array(
-      Language::LANGCODE_NOT_SPECIFIED => array(
-        'summary_field' => array(
-          0 => array(
-            'value' => $value,
-            'summary' => $summary,
-            'format' => 'plain_text',
-            'processed' => $value,
-            'summary_processed' => $summary,
-          ),
+    $cache = \Drupal::cache('entity')->get("values:$entity_type:" . $entity->id());
+    $this->assertEqual($cache->data['values']['summary_field'], array(
+      Language::LANGCODE_DEFAULT => array(
+        0 => array(
+          'value' => $value,
+          'summary' => $summary,
+          'format' => 'plain_text',
+          'processed' => $value,
+          'summary_processed' => $summary,
         ),
       ),
     ));
 
     // Inject fake processed values into the cache to make sure that these are
     // used as-is and not re-calculated when the entity is loaded.
-    $data = array(
-      Language::LANGCODE_NOT_SPECIFIED => array(
-        'summary_field' => array(
-          0 => array(
-            'value' => $value,
-            'summary' => $summary,
-            'format' => 'plain_text',
-            'processed' => 'Cached processed value',
-            'summary_processed' => 'Cached summary processed value',
-          ),
+    $data = $cache->data;
+    $data['values']['summary_field'] = array(
+      Language::LANGCODE_DEFAULT => array(
+        0 => array(
+          'value' => $value,
+          'summary' => $summary,
+          'format' => 'plain_text',
+          'processed' => 'Cached processed value',
+          'summary_processed' => 'Cached summary processed value',
         ),
       ),
     );
-    \Drupal::cache('entity')->set("field:$entity_type:" . $entity->id(), $data);
-    $entity = entity_load($entity_type, $entity->id(), TRUE);
+    \Drupal::entityManager()->getStorage($entity_type)->resetCache();
+    \Drupal::cache('entity')->set("values:$entity_type:" . $entity->id(), $data);
+    $entity = entity_load($entity_type, $entity->id());
     $this->assertEqual($entity->summary_field->processed, 'Cached processed value');
     $this->assertEqual($entity->summary_field->summary_processed, 'Cached summary processed value');
 
diff --git a/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerNodeAccessTest.php b/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerNodeAccessTest.php
index c50eed2..ed5f58e 100644
--- a/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerNodeAccessTest.php
+++ b/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerNodeAccessTest.php
@@ -34,6 +34,7 @@ public function setUp() {
     parent::setUp();
     node_access_rebuild();
     $this->drupalCreateContentType(array('type' => 'page'));
+    node_access_test_add_field(entity_load('node_type', 'page'));
     $this->container->get('comment.manager')->addDefaultField('node', 'page', 'comment', CommentItemInterface::OPEN);
     \Drupal::state()->set('node_access_test.private', TRUE);
   }
diff --git a/core/modules/user/lib/Drupal/user/UserStorage.php b/core/modules/user/lib/Drupal/user/UserStorage.php
index d5d91ca..6414ea7 100644
--- a/core/modules/user/lib/Drupal/user/UserStorage.php
+++ b/core/modules/user/lib/Drupal/user/UserStorage.php
@@ -8,6 +8,7 @@
 namespace Drupal\user;
 
 use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
@@ -32,13 +33,6 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt
   protected $password;
 
   /**
-   * Provides the user data service object.
-   *
-   * @var \Drupal\user\UserDataInterface
-   */
-  protected $userData;
-
-  /**
    * Constructs a new UserStorage object.
    *
    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
@@ -47,18 +41,15 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt
    *   The database connection to be used.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
-   * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
-   *   The UUID Service.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
    * @param \Drupal\Core\Password\PasswordInterface $password
    *   The password hashing service.
-   * @param \Drupal\user\UserDataInterface $user_data
-   *   The user data service.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) {
-    parent::__construct($entity_type, $database, $entity_manager, $uuid_service);
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, PasswordInterface $password) {
+    parent::__construct($entity_type, $database, $entity_manager, $cache);
 
     $this->password = $password;
-    $this->userData = $user_data;
   }
 
   /**
@@ -69,9 +60,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $entity_type,
       $container->get('database'),
       $container->get('entity.manager'),
-      $container->get('uuid'),
-      $container->get('password'),
-      $container->get('user.data')
+      $container->get('cache.entity'),
+      $container->get('password')
     );
   }
 
@@ -152,6 +142,8 @@ public function updateLastLoginTimestamp(UserInterface $account) {
       ->fields(array('login' => $account->getLastLoginTime()))
       ->condition('uid', $account->id())
       ->execute();
+    // Ensure that the entity cache is cleared.
+    $this->resetCache(array($account->id()));
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
index 6e60480..ae44364 100644
--- a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
@@ -152,7 +152,8 @@ public function testCreate() {
     $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
       ->disableOriginalConstructor()
       ->getMock();
-    $entity_storage = new ContentEntityDatabaseStorage($entity_type, $connection, $entity_manager);
+    $cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $entity_storage = new ContentEntityDatabaseStorage($entity_type, $connection, $entity_manager, $cache);
 
     $entity = $entity_storage->create();
     $entity->expects($this->atLeastOnce())
