diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index b30d9ec..d84a995 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -117,13 +117,9 @@ public function loadMultiple(array $ids = NULL) {
     // database when all requested entities are loaded from cache.
     $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
     // Try to load entities from the static cache, if the entity type supports
-    // static caching.
+    // static caching. This will remove ID's that were loaded from $ids.
     if ($this->cache && $ids) {
       $entities += $this->cacheGet($ids);
-      // If any entities were loaded, remove them from the ids still to load.
-      if ($passed_ids) {
-        $ids = array_keys(array_diff_key($passed_ids, $entities));
-      }
     }
 
     // Load any remaining entities from the database. This is the case if $ids
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
index f303f79..ef99943 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
@@ -109,17 +109,24 @@ public function resetCache(array $ids = NULL) {
   /**
    * Gets entities from the static cache.
    *
-   * @param $ids
-   *   If not empty, return entities that match these IDs.
+   * @param array &$ids
+   *   If not empty, return entities that match these IDs. ID's that were found
+   *   will be removed from the list.
    *
-   * @return
+   * @return \Drupal\Core\Entity\EntityInterface[]
    *   Array of entities from the entity cache.
    */
-  protected function cacheGet($ids) {
+  protected function cacheGet(&$ids) {
     $entities = array();
     // Load any available entities from the internal cache.
     if ($this->cache && !empty($this->entityCache)) {
-      $entities += array_intersect_key($this->entityCache, array_flip($ids));
+      foreach ($ids as $index => $id) {
+        if (isset($this->entityCache[$id])) {
+          $entities[$id] = $this->entityCache[$id];
+          // Remove the ID from the list
+          unset($ids[$index]);
+        }
+      }
     }
     return $entities;
   }
@@ -127,7 +134,7 @@ protected function cacheGet($ids) {
   /**
    * Stores entities in the static entity cache.
    *
-   * @param $entities
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
    *   Entities to store in the cache.
    */
   protected function cacheSet($entities) {
diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
index d82c8cf..e898eae 100644
--- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
@@ -7,11 +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\PrepareCacheInterface;
 use Drupal\Core\Language\Language;
-use Drupal\Component\Utility\NestedArray;
-use Drupal\Component\Uuid\Uuid;
 use Drupal\field\FieldInfo;
 use Drupal\field\FieldUpdateForbiddenException;
 use Drupal\field\FieldInterface;
@@ -83,6 +83,13 @@ class FieldableDatabaseStorageController extends FieldableEntityStorageControlle
   protected $fieldInfo;
 
   /**
+   * Cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cacheBackend;
+
+  /**
    * The entity bundle key.
    *
    * @var string|bool
@@ -104,7 +111,8 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_type,
       $entity_info,
       $container->get('database'),
-      $container->get('field.info')
+      $container->get('field.info'),
+      $container->get('cache.entity')
     );
   }
 
@@ -119,12 +127,15 @@ public static function createInstance(ContainerInterface $container, $entity_typ
    *   The database connection to be used.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
+  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info, CacheBackendInterface $cache) {
     parent::__construct($entity_type, $entity_info);
 
     $this->database = $database;
     $this->fieldInfo = $field_info;
+    $this->cacheBackend = $cache;
     $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
     $this->entityClass = $this->entityInfo['class'];
 
@@ -210,14 +221,10 @@ public function loadMultiple(array $ids = NULL) {
     // and we need to know if it's empty for this reason to avoid querying the
     // database when all requested entities are loaded from cache.
     $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
-    // Try to load entities from the static cache, if the entity type supports
-    // static caching.
+    // Try to load entities from the cache, if the entity type supports
+    // static caching. This will remove ID's that were loaded from $ids.
     if ($this->cache && $ids) {
       $entities += $this->cacheGet($ids);
-      // If any entities were loaded, remove them from the ids still to load.
-      if ($passed_ids) {
-        $ids = array_keys(array_diff_key($passed_ids, $entities));
-      }
     }
 
     // Load any remaining entities from the database. This is the case if $ids
@@ -556,18 +563,7 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
       $this->loadFieldItems($queried_entities, $load_revision ? static::FIELD_LOAD_REVISION : static::FIELD_LOAD_CURRENT);
     }
 
-    // Call hook_entity_load().
-    foreach (\Drupal::moduleHandler()->getImplementations('entity_load') as $module) {
-      $function = $module . '_entity_load';
-      $function($queried_entities, $this->entityType);
-    }
-    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
-    // always the queried entities, followed by additional arguments set in
-    // $this->hookLoadArguments.
-    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
-    foreach (\Drupal::moduleHandler()->getImplementations($this->entityType . '_load') as $module) {
-      call_user_func_array($module . '_' . $this->entityType . '_load', $args);
-    }
+    $this->invokeLoadHook($queried_entities);
   }
 
   /**
@@ -669,12 +665,12 @@ public function save(EntityInterface $entity) {
         if ($this->revisionDataTable) {
           $this->savePropertyData($entity, 'revision_data_table');
         }
-        $this->resetCache(array($entity->id()));
         $entity->setNewRevision(FALSE);
         $entity->postSave($this, TRUE);
         $this->invokeFieldMethod('update', $entity);
         $this->saveFieldItems($entity, TRUE);
         $this->invokeHook('update', $entity);
+        $this->resetCache(array($entity->id()));
         if ($this->dataTable) {
           $this->invokeTranslationHooks($entity);
         }
@@ -876,9 +872,23 @@ 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.
+   * @param int $age
+   *   EntityStorageControllerInterface::FIELD_LOAD_CURRENT to load the most
+   *   recent revision for all fields, or
+   *   EntityStorageControllerInterface::FIELD_LOAD_REVISION to load the version
+   *   indicated by each entity.
    */
-  protected function doLoadFieldItems($entities, $age) {
+  protected function loadFieldItems(array $entities, $age) {
+    if (empty($entities) || empty($this->entityInfo['fieldable'])) {
+      return;
+    }
     $load_current = $age == static::FIELD_LOAD_CURRENT;
 
     // Collect entities ids, bundles and languages.
@@ -945,9 +955,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();
@@ -1030,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->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
       $field = $instance->getField();
       $table_name = static::_fieldTableName($field);
@@ -1047,9 +1065,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->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
@@ -1525,4 +1546,158 @@ static public function _fieldColumnName(FieldInterface $field, $column) {
     return in_array($column, Field::getReservedColumns()) ? $column : $field->getName() . '_' . $column;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function cacheGet(&$ids) {
+    // The parent implementation provides the static cache, so first attempt
+    // to load entities from it.
+    $entities = parent::cacheGet($ids);
+    // @todo: Rename field_cache?
+    if ($this->entityInfo['field_cache'] && $ids) {
+      // Now attempt to load the remaining ID's (those that were loaded have been
+      // removed from $ids already) from the persistent cache.
+      // 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.
+        // Put them in a temporary variable so that we can call the load hooks
+        // for those.
+        // @todo: Avoid this, which requires that all cached data must
+        //   be in defined fields or find a way to extract non-defined values
+        //   too.
+        $entities_from_cache = array();
+        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_from_cache[$id] = new $this->entityClass($values, $this->entityType, $bundle, $translations);
+            // Already put the loaded entity into the cache. This will prevent
+            // cacheSet() from unnecessarily writing them back into the cache
+            // backend. Do not call that method directly to prevent that.
+            $this->entityCache[$id] = $entities_from_cache[$id];
+            unset($ids[$index]);
+          }
+        }
+
+        // Call the load hooks on the entities loaded from the persistent cache.
+        if (!empty($entities_from_cache)) {
+          $this->invokeLoadHook($entities_from_cache);
+          foreach ($entities_from_cache as $id => $entity) {
+            $entities[$id] = $entity;
+          }
+        }
+      }
+    }
+    return $entities;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function cacheSet($entities) {
+    if ($this->entityInfo['field_cache']) {
+      foreach ($entities as $id => $entity) {
+        // Skip entities that are already in the static cache.
+        if (isset($this->entityCache[$id])) {
+          continue;
+        }
+
+        $data = array(
+          'id' => $entity->id(),
+          'bundle' => $entity->bundle(),
+          'translations' => array_keys($entity->getTranslationLanguages()),
+          'values' => array(),
+        );
+        $default_langcode = $entity->getUntranslated()->language()->id;
+        foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+          $translation = $entity->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()) {
+              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['values'][$field_name][$langcode][$delta] = $item->getCacheData();
+                }
+                else {
+                  $data['values'][$field_name][$langcode][$delta] = $item->getValue();
+                }
+              }
+            }
+          }
+        }
+        $this->cacheBackend->set($this->buildCacheId($id), $data, CacheBackendInterface::CACHE_PERMANENT, array($this->entityType . '_values' => TRUE));
+      }
+    }
+    parent::cacheSet($entities);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetCache(array $ids = NULL) {
+    parent::resetCache($ids);
+    if ($this->entityInfo['field_cache']) {
+      if ($ids) {
+        $cids = array();
+        foreach ($ids as $id) {
+          $cids[] = $this->buildCacheId($id);
+        }
+        $this->cacheBackend->deleteMultiple($cids);
+      }
+      else {
+        $this->cacheBackend->deleteTags(array($this->entityType . '_values' => TRUE));
+      }
+    }
+  }
+
+  /**
+   * Invokes the entity load hooks on the given entities.
+   *
+   * @param array $entities
+   *   List of entities to invoke the hook for.
+   */
+  protected function invokeLoadHook($entities) {
+    // Call hook_entity_load().
+    foreach (\Drupal::moduleHandler()
+               ->getImplementations('entity_load') as $module) {
+      $function = $module . '_entity_load';
+      $function($entities, $this->entityType);
+    }
+    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
+    // always the queried entities, followed by additional arguments set in
+    // $this->hookLoadArguments.
+    $args = array_merge(array($entities), $this->hookLoadArguments);
+    foreach (\Drupal::moduleHandler()
+               ->getImplementations($this->entityType . '_load') as $module) {
+      call_user_func_array($module . '_' . $this->entityType . '_load', $args);
+    }
+  }
+
+  /**
+   * 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->entityType}:$id";
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
index 74b5bf3..e0ef41f 100644
--- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
@@ -7,213 +7,12 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Core\Field\PrepareCacheInterface;
 use Drupal\field\FieldInterface;
 use Drupal\field\FieldInstanceInterface;
-use Drupal\Core\Field\ConfigFieldItemListInterface;
-use Symfony\Component\DependencyInjection\Container;
 
 abstract class FieldableEntityStorageControllerBase extends EntityStorageControllerBase implements FieldableEntityStorageControllerInterface {
 
   /**
-   * 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.
-   * @param int $age
-   *   EntityStorageControllerInterface::FIELD_LOAD_CURRENT to load the most
-   *   recent revision for all fields, or
-   *   EntityStorageControllerInterface::FIELD_LOAD_REVISION to load the version
-   *   indicated by each entity.
-   */
-  protected function loadFieldItems(array $entities, $age) {
-    if (empty($entities)) {
-      return;
-    }
-
-    // Only the most current revision of non-deleted fields for cacheable entity
-    // types can be cached.
-    $load_current = $age == static::FIELD_LOAD_CURRENT;
-    $info = entity_get_info($this->entityType);
-    $use_cache = $load_current && $info['field_cache'];
-
-    // 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->entityType}:$id";
-      }
-      $cache = cache('field')->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->entityType}:$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 controller 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 instanceof ConfigFieldItemListInterface && !$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->entityType}:$id";
-          cache('field')->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_info = $entity->entityInfo();
-      if ($entity_info['field_cache']) {
-        cache('field')->delete('field:' . $entity->entityType() . ':' . $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_info = $entity->entityInfo();
-    if ($entity_info['field_cache']) {
-      cache('field')->delete('field:' . $entity->entityType() . ':' . $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
-   *   EntityStorageControllerInterface::FIELD_LOAD_CURRENT to load the most
-   *   recent revision for all fields, or
-   *   EntityStorageControllerInterface::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(FieldInterface $field) { }
diff --git a/core/lib/Drupal/Core/Field/ConfigEntityReferenceItemBase.php b/core/lib/Drupal/Core/Field/ConfigEntityReferenceItemBase.php
index bbd1747..8be7439 100644
--- a/core/lib/Drupal/Core/Field/ConfigEntityReferenceItemBase.php
+++ b/core/lib/Drupal/Core/Field/ConfigEntityReferenceItemBase.php
@@ -89,7 +89,7 @@ public static function schema(FieldInterface $field) {
   public function isEmpty() {
     // Avoid loading the entity by first checking the 'target_id'.
     $target_id = $this->target_id;
-    if (!empty($target_id)) {
+    if ($target_id !== NULL && $target_id !== '') {
       return FALSE;
     }
     // Allow auto-create entities.
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
index 5ebec96..bf4c27c 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
@@ -19,21 +19,19 @@
 class CustomBlockStorageController extends FieldableDatabaseStorageController {
 
   /**
-   * Overrides \Drupal\Core\Entity\DatabaseStorageController::attachLoad().
+   * {@ineheritdoc}
    */
-  protected function attachLoad(&$blocks, $load_revision = FALSE) {
-    // Create an array of block types for passing as a load argument.
-    // Note that blocks at this point are still \StdClass objects returned from
-    // the database.
-    foreach ($blocks as $entity) {
-      $types[$entity->type] = $entity->type;
+  protected function invokeLoadHook($entities) {
+    // Besides the list of custom blocks, pass one additional argument to
+    // hook_custom_block_load(), containing a list of block types that were loaded.
+    $typed_nodes = array();
+    foreach ($entities as $id => $node) {
+      $typed_nodes[$node->bundle()][$id] = $entities[$id];
     }
+    $argument = array_keys($typed_nodes);
+    $this->hookLoadArguments = array($argument);
 
-    // Besides the list of blocks, pass one additional argument to
-    // hook_custom_block_load(), containing a list of block types that were
-    // loaded.
-    $this->hookLoadArguments = array($types);
-    parent::attachLoad($blocks, $load_revision);
+    parent::invokeLoadHook($entities);
   }
 
 }
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
index 8f77373..814429e 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
@@ -235,7 +235,7 @@ function testFieldAttachPrepareViewMultiple() {
   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.
@@ -269,15 +269,15 @@ function testFieldAttachCache() {
     $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(cache('field')->get($cid), 'Cached: no cache entry on insert');
 
     // Load, and check that a cache entry is present with the expected values.
     $controller = $this->container->get('entity.manager')->getStorageController($entity->entityType());
     $controller->resetCache();
     $controller->load($entity->id());
-    $cache = cache('field')->get($cid);
-    $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load');
+    $cache = cache('entity')->get($cid);
+    $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());
@@ -287,13 +287,13 @@ function testFieldAttachCache() {
     ));
     $entity->{$this->field_name_2} = $values;
     $entity->save();
-    $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on update');
+    $this->assertFalse(cache('entity')->get($cid), 'Cached: no cache entry on update');
 
     // Load, and check that a cache entry is present with the expected values.
     $controller->resetCache();
     $controller->load($entity->id());
-    $cache = cache('field')->get($cid);
-    $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load');
+    $cache = cache('entity')->get($cid);
+    $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.
     $entity = entity_create($entity_type, array(
@@ -309,12 +309,12 @@ function testFieldAttachCache() {
     // Load, and check that a cache entry is present with the expected values.
     $controller->resetCache();
     $controller->load($entity->id());
-    $cache = cache('field')->get($cid);
-    $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load');
+    $cache = cache('entity')->get($cid);
+    $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();
-    $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry after delete');
+    $this->assertFalse(cache('entity')->get($cid), 'Cached: no cache entry after delete');
   }
 
   /**
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
index d24042a..9751712 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
@@ -82,6 +82,7 @@ function testDisableFilterModule() {
       'filters[filter_test_replace][status]' => 1,
     );
     $this->drupalPostForm('admin/config/content/formats/manage/' . $format_id, $edit, t('Save configuration'));
+    \Drupal::entityManager()->getStorageController('node')->resetCache(array($node->id()));
 
     // Verify that filter_test_replace filter replaced the content.
     $this->drupalGet('node/' . $node->id());
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 0c3a623..ff2a4d8 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -30,46 +30,22 @@ public function create(array $values) {
   }
 
   /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageController::attachLoad().
+   * {@ineheritdoc}
    */
-  protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
-    $queried_entities = $this->mapFromStorageRecords($queried_entities, $load_revision);
-
-    // Create an array of nodes for each content type and pass this to the
-    // object type specific callback. To preserve backward-compatibility we
-    // pass on BC decorators to node-specific hooks, while we pass on the
-    // regular entity objects else.
-    $typed_nodes = array();
-    foreach ($queried_entities as $id => $node) {
-      $typed_nodes[$node->bundle()][$id] = $queried_entities[$id];
-    }
-
-    if ($load_revision) {
-      $this->loadFieldItems($queried_entities, static::FIELD_LOAD_REVISION);
-    }
-    else {
-      $this->loadFieldItems($queried_entities, static::FIELD_LOAD_CURRENT);
-    }
-
+  protected function invokeLoadHook($entities) {
     // Besides the list of nodes, pass one additional argument to
     // hook_node_load(), containing a list of node types that were loaded.
+    $typed_nodes = array();
+    foreach ($entities as $id => $node) {
+      $typed_nodes[$node->bundle()][$id] = $entities[$id];
+    }
     $argument = array_keys($typed_nodes);
     $this->hookLoadArguments = array($argument);
 
-    // Call hook_entity_load().
-    foreach (\Drupal::moduleHandler()->getImplementations('entity_load') as $module) {
-      $function = $module . '_entity_load';
-      $function($queried_entities, $this->entityType);
-    }
-    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
-    // always the queried entities, followed by additional arguments set in
-    // $this->hookLoadArguments.
-    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
-    foreach (\Drupal::moduleHandler()->getImplementations($this->entityType . '_load') as $module) {
-      call_user_func_array($module . '_' . $this->entityType . '_load', $args);
-    }
+    parent::invokeLoadHook($entities);
   }
 
+
   /**
    * Overrides Drupal\Core\Entity\DatabaseStorageController::postDelete().
    */
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 d581147..b9ed58f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
@@ -61,11 +61,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/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
index 49d9f78..43e26f8 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 = cache('field')->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 = 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',
         ),
       ),
     );
-    cache('field')->set("field:$entity_type:" . $entity->id(), $data);
-    $entity = entity_load($entity_type, $entity->id(), TRUE);
+    \Drupal::entityManager()->getStorageController($entity_type)->resetCache();
+    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/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index 48e8206..53d8e48 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.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\Password\PasswordInterface;
 use Drupal\Core\Database\Connection;
@@ -49,15 +50,15 @@ class UserStorageController extends FieldableDatabaseStorageController implement
    *   The database connection to be used.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
-   * @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($entity_type, $entity_info, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) {
-    parent::__construct($entity_type, $entity_info, $database, $field_info, $uuid_service);
+  public function __construct($entity_type, $entity_info, Connection $database, FieldInfo $field_info, CacheBackendInterface $cache, PasswordInterface $password, UserDataInterface $user_data) {
+    parent::__construct($entity_type, $entity_info, $database, $field_info, $cache);
 
     $this->password = $password;
     $this->userData = $user_data;
@@ -72,7 +73,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_info,
       $container->get('database'),
       $container->get('field.info'),
-      $container->get('uuid'),
+      $container->get('cache.entity'),
       $container->get('password'),
       $container->get('user.data')
     );
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 79e9cab..92ed7c4 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -978,6 +978,7 @@ function user_login_finalize(UserInterface $account) {
     ->fields(array('login' => $user->getLastLoginTime()))
     ->condition('uid', $user->id())
     ->execute();
+  \Drupal::entityManager()->getStorageController('user')->resetCache(array($account->id()));
 
   // Regenerate the session ID to prevent against session fixation attacks.
   // This is called before hook_user in case one of those functions fails
