diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
index 07cf115..590ab9e 100644
--- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
@@ -7,11 +7,12 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\Field\PrepareCacheInterface;
 use Drupal\Core\Entity\Query\QueryInterface;
 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 +84,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 +112,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 +128,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'];
 
@@ -531,18 +543,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);
   }
 
   /**
@@ -1467,4 +1468,133 @@ static public function _fieldColumnName(FieldInterface $field, $column) {
     return in_array($column, Field::getReservedColumns()) ? $column : $field->getFieldName() . '_' . $column;
   }
 
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function cacheGet($ids) {
+    $entities = parent::cacheGet($ids);
+    if ($this->cache) {
+      // Build the list of cache entries to retrieve.
+      $cids = array();
+      foreach ($ids as $id) {
+        if (!isset($entities[$id])) {
+          $cids[] = "entity:{$this->entityType}:$id";
+        }
+      }
+      if ($cids && $cache = $this->cacheBackend->getMultiple($cids)) {
+        // Put the cached field values back into the entities and remove them from
+        // the list of entities to query.
+        $entities_from_cache = array();
+        foreach ($ids as $id) {
+          $cid = "entity:{$this->entityType}:$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->entityCache[$id] = $entities_from_cache[$id];
+          }
+        }
+
+        // Call the load hooks on the entities loaded from the persistent cache.
+        if (!empty($entities_from_cache)) {
+          $this->invokeLoadHook($entities_from_cache);
+          $entities = array_merge($entities, $entities_from_cache);
+        }
+
+      }
+      return $entities;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function cacheSet($entities) {
+    if ($this->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(),
+        );
+        foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+          $translation = $entity->getTranslation($langcode);
+
+          // Make sure the default language is valid.
+          if ($entity->getUntranslated()->language()->id == $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();
+                }
+              }
+            }
+          }
+        }
+        $cid = "entity:{$this->entityType}:$id";
+        $this->cacheBackend->set($cid, $data);
+      }
+    }
+    parent::cacheSet($entities);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetCache(array $ids = NULL) {
+    parent::resetCache($ids);
+    if ($ids) {
+      $cids = array();
+      foreach ($ids as $id) {
+        $cids[] = "entity:{$this->entityType}:$id";
+      }
+      $this->cacheBackend->deleteMultiple($cids);
+    }
+    else {
+      $this->cacheBackend->deleteAll();
+    }
+  }
+
+  /**
+   * 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);
+    }
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
index f16d863..700cdcd 100644
--- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
@@ -34,80 +34,12 @@
    *   indicated by each entity.
    */
   protected function loadFieldItems(array $entities, $age) {
-    if (empty($entities)) {
+    if (empty($entities) || empty($this->entityInfo['fieldable'])) {
       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);
-        }
-      }
-    }
+    // @todo: Drop these wrappers.
+    // Let the storage controller actually load the values.
+    $this->doLoadFieldItems($entities, $age);
   }
 
   /**
@@ -123,14 +55,8 @@ protected function loadFieldItems(array $entities, $age) {
    *   TRUE if the entity is being updated, FALSE if it is being inserted.
    */
   protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
+    // @todo: Drop these wrappers.
     $this->doSaveFieldItems($entity, $update);
-
-    if ($update) {
-      $entity_info = $entity->entityInfo();
-      if ($entity_info['field_cache']) {
-        cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
-      }
-    }
   }
 
   /**
@@ -144,12 +70,8 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
    *   The entity.
    */
   protected function deleteFieldItems(EntityInterface $entity) {
+    // @todo: Drop these wrappers.
     $this->doDeleteFieldItems($entity);
-
-    $entity_info = $entity->entityInfo();
-    if ($entity_info['field_cache']) {
-      cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
-    }
   }
 
   /**
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 2078f58..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 $id => $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/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/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index 48e8206..bb433b0 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')
     );
