diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index 2b53dfd88b02b3de55b22e8580d565c1243f6c7b..b30d9ec149e8155c895176daad7c94bce4b857fe 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -23,12 +23,9 @@
 /**
  * Defines a base entity controller class.
  *
- * Default implementation of Drupal\Core\Entity\EntityStorageControllerInterface.
- *
- * This class can be used as-is by most simple entity types. Entity types
- * requiring special handling can extend the class.
+ * This class only supports bare, non-content entities.
  */
-class DatabaseStorageController extends FieldableEntityStorageControllerBase {
+class DatabaseStorageController extends EntityStorageControllerBase {
 
   /**
    * The UUID service.
@@ -37,22 +34,6 @@ class DatabaseStorageController extends FieldableEntityStorageControllerBase {
    */
   protected $uuidService;
 
-  /**
-   * Name of entity's revision database table field, if it supports revisions.
-   *
-   * Has the value FALSE if this entity does not use revisions.
-   *
-   * @var string
-   */
-  protected $revisionKey;
-
-  /**
-   * The table that stores revisions, if the entity supports revisions.
-   *
-   * @var string
-   */
-  protected $revisionTable;
-
   /**
    * Whether this entity type should use the static cache.
    *
@@ -84,7 +65,6 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_type,
       $entity_info,
       $container->get('database'),
-      $container->get('field.info'),
       $container->get('uuid')
     );
   }
@@ -98,16 +78,13 @@ public static function createInstance(ContainerInterface $container, $entity_typ
    *   An array of entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   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.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service) {
+  public function __construct($entity_type, array $entity_info, Connection $database, UuidInterface $uuid_service) {
     parent::__construct($entity_type, $entity_info);
 
     $this->database = $database;
-    $this->fieldInfo = $field_info;
     $this->uuidService = $uuid_service;
 
     // Check if the entity type supports IDs.
@@ -125,15 +102,6 @@ public function __construct($entity_type, array $entity_info, Connection $databa
     else {
       $this->uuidKey = FALSE;
     }
-
-    // Check if the entity type supports revisions.
-    if (!empty($this->entityInfo['entity_keys']['revision'])) {
-      $this->revisionKey = $this->entityInfo['entity_keys']['revision'];
-      $this->revisionTable = $this->entityInfo['revision_table'];
-    }
-    else {
-      $this->revisionKey = FALSE;
-    }
   }
 
   /**
@@ -212,50 +180,21 @@ public function load($id) {
   }
 
   /**
-   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::loadRevision().
+   * {@inheritdoc}
    */
   public function loadRevision($revision_id) {
-    // Build and execute the query.
-    $query_result = $this->buildQuery(array(), $revision_id)->execute();
-
-    if (!empty($this->entityInfo['class'])) {
-      // We provide the necessary arguments for PDO to create objects of the
-      // specified entity class.
-      // @see \Drupal\Core\Entity\EntityInterface::__construct()
-      $query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
-    }
-    $queried_entities = $query_result->fetchAllAssoc($this->idKey);
-
-    // Pass the loaded entities from the database through $this->attachLoad(),
-    // which attaches fields (if supported by the entity type) and calls the
-    // entity type specific load callback, for example hook_node_load().
-    if (!empty($queried_entities)) {
-      $this->attachLoad($queried_entities, $revision_id);
-    }
-    return reset($queried_entities);
+    throw new \Exception('Database storage controller does not support revisions.');
   }
 
   /**
-   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::deleteRevision().
+   * {@inheritdoc}
    */
   public function deleteRevision($revision_id) {
-    if ($revision = $this->loadRevision($revision_id)) {
-      // Prevent deletion if this is the default revision.
-      if ($revision->isDefaultRevision()) {
-        throw new EntityStorageException('Default revision can not be deleted');
-      }
-
-      $this->database->delete($this->revisionTable)
-        ->condition($this->revisionKey, $revision->getRevisionId())
-        ->execute();
-      $this->invokeFieldMethod('deleteRevision', $revision);
-      $this->deleteFieldItemsRevision($revision);
-      $this->invokeHook('revision_delete', $revision);
-    }
+    throw new \Exception('Database storage controller does not support revisions.');
   }
 
   /**
-   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::loadByProperties().
+   * {@inheritdoc}
    */
   public function loadByProperties(array $values = array()) {
     // Build a query to fetch the entity IDs.
@@ -283,19 +222,8 @@ protected function buildPropertyQuery(QueryInterface $entity_query, array $value
   /**
    * Builds the query to load the entity.
    *
-   * This has full revision support. For entities requiring special queries,
-   * the class can be extended, and the default query can be constructed by
-   * calling parent::buildQuery(). This is usually necessary when the object
-   * being loaded needs to be augmented with additional data from another
-   * table, such as loading node type into comments or vocabulary machine name
-   * into terms, however it can also support $conditions on different tables.
-   * See Drupal\comment\CommentStorageController::buildQuery() for an example.
-   *
    * @param array|null $ids
    *   An array of entity IDs, or NULL to load all entities.
-   * @param $revision_id
-   *   The ID of the revision to load, or FALSE if this query is asking for the
-   *   most current revision(s).
    *
    * @return SelectQuery
    *   A SelectQuery object for loading the entity.
@@ -305,37 +233,8 @@ protected function buildQuery($ids, $revision_id = FALSE) {
 
     $query->addTag($this->entityType . '_load_multiple');
 
-    if ($revision_id) {
-      $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id));
-    }
-    elseif ($this->revisionKey) {
-      $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
-    }
-
     // Add fields from the {entity} table.
     $entity_fields = drupal_schema_fields_sql($this->entityInfo['base_table']);
-
-    if ($this->revisionKey) {
-      // Add all fields from the {entity_revision} table.
-      $entity_revision_fields = drupal_map_assoc(drupal_schema_fields_sql($this->entityInfo['revision_table']));
-      // The ID field is provided by entity, so remove it.
-      unset($entity_revision_fields[$this->idKey]);
-
-      // Remove all fields from the base table that are also fields by the same
-      // name in the revision table.
-      $entity_field_keys = array_flip($entity_fields);
-      foreach ($entity_revision_fields as $name) {
-        if (isset($entity_field_keys[$name])) {
-          unset($entity_fields[$entity_field_keys[$name]]);
-        }
-      }
-      $query->fields('revision', $entity_revision_fields);
-
-      // Compare revision ID of the base and revision table, if equal then this
-      // is the default revision.
-      $query->addExpression('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, 'isDefaultRevision');
-    }
-
     $query->fields('base', $entity_fields);
 
     if ($ids) {
@@ -348,41 +247,21 @@ protected function buildQuery($ids, $revision_id = FALSE) {
   /**
    * Attaches data to entities upon loading.
    *
-   * This will attach fields, if the entity is fieldable. It calls
-   * hook_entity_load() for modules which need to add data to all entities.
-   * It also calls hook_TYPE_load() on the loaded entities. For example
-   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
-   * expects special parameters apart from the queried entities, you can set
-   * $this->hookLoadArguments prior to calling the method.
-   * See Drupal\node\NodeStorageController::attachLoad() for an example.
-   *
    * @param $queried_entities
    *   Associative array of query results, keyed on the entity ID.
    * @param $load_revision
    *   (optional) TRUE if the revision should be loaded, defaults to FALSE.
    */
   protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
-    // Attach field values.
-    if ($this->entityInfo['fieldable']) {
-      $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);
-    }
   }
 
   /**
-   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::create().
+   * {@inheritdoc}
    */
   public function create(array $values) {
     $entity_class = $this->entityInfo['class'];
@@ -404,7 +283,7 @@ public function create(array $values) {
   }
 
   /**
-   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::delete().
+   * {@inheritdoc}
    */
   public function delete(array $entities) {
     if (!$entities) {
@@ -425,19 +304,11 @@ public function delete(array $entities) {
         ->condition($this->idKey, $ids, 'IN')
         ->execute();
 
-      if ($this->revisionKey) {
-        $this->database->delete($this->revisionTable)
-          ->condition($this->idKey, $ids, 'IN')
-          ->execute();
-      }
-
       // Reset the cache as soon as the changes have been applied.
       $this->resetCache($ids);
 
       $entity_class::postDelete($this, $entities);
       foreach ($entities as $entity) {
-        $this->invokeFieldMethod('delete', $entity);
-        $this->deleteFieldItems($entity);
         $this->invokeHook('delete', $entity);
       }
       // Ignore slave server temporarily.
@@ -451,7 +322,7 @@ public function delete(array $entities) {
   }
 
   /**
-   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::save().
+   * {@inheritdoc}
    */
   public function save(EntityInterface $entity) {
     $transaction = $this->database->startTransaction();
@@ -462,39 +333,21 @@ public function save(EntityInterface $entity) {
       }
 
       $entity->preSave($this);
-      $this->invokeFieldMethod('preSave', $entity);
       $this->invokeHook('presave', $entity);
 
       if (!$entity->isNew()) {
-        if ($entity->isDefaultRevision()) {
-          $return = drupal_write_record($this->entityInfo['base_table'], $entity, $this->idKey);
-        }
-        else {
-          // @todo, should a different value be returned when saving an entity
-          // with $isDefaultRevision = FALSE?
-          $return = FALSE;
-        }
-        if ($this->revisionKey) {
-          $this->saveRevision($entity);
-        }
+        $return = drupal_write_record($this->entityInfo['base_table'], $entity, $this->idKey);
         $this->resetCache(array($entity->id()));
         $entity->postSave($this, TRUE);
-        $this->invokeFieldMethod('update', $entity);
-        $this->saveFieldItems($entity, TRUE);
         $this->invokeHook('update', $entity);
       }
       else {
         $return = drupal_write_record($this->entityInfo['base_table'], $entity);
-        if ($this->revisionKey) {
-          $this->saveRevision($entity);
-        }
         // Reset general caches, but keep caches specific to certain entities.
         $this->resetCache(array());
 
         $entity->enforceIsNew(FALSE);
         $entity->postSave($this, FALSE);
-        $this->invokeFieldMethod('insert', $entity);
-        $this->saveFieldItems($entity, FALSE);
         $this->invokeHook('insert', $entity);
       }
 
@@ -511,46 +364,6 @@ public function save(EntityInterface $entity) {
     }
   }
 
-  /**
-   * Saves an entity revision.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   */
-  protected function saveRevision(EntityInterface $entity) {
-    // Convert the entity into an array as it might not have the same properties
-    // as the entity, it is just a raw structure.
-    $record = (array) $entity;
-
-    // When saving a new revision, set any existing revision ID to NULL so as to
-    // ensure that a new revision will actually be created.
-    if ($entity->isNewRevision() && $record[$this->revisionKey]) {
-      $record[$this->revisionKey] = NULL;
-    }
-
-    // Cast to object as preSaveRevision() expects one to be compatible with the
-    // upcoming NG storage controller.
-    $record = (object) $record;
-    $entity->preSaveRevision($this, $record);
-    $record = (array) $record;
-
-    if ($entity->isNewRevision()) {
-      drupal_write_record($this->revisionTable, $record);
-      if ($entity->isDefaultRevision()) {
-        $this->database->update($this->entityInfo['base_table'])
-          ->fields(array($this->revisionKey => $record[$this->revisionKey]))
-          ->condition($this->idKey, $entity->id())
-          ->execute();
-      }
-      $entity->setNewRevision(FALSE);
-    }
-    else {
-      drupal_write_record($this->revisionTable, $record, $this->revisionKey);
-    }
-    // Make sure to update the new revision key for the entity.
-    $entity->{$this->revisionKey} = $record[$this->revisionKey];
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -558,645 +371,4 @@ public function getQueryServiceName() {
     return 'entity.query.sql';
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  protected function doLoadFieldItems($entities, $age) {
-    $load_current = $age == static::FIELD_LOAD_CURRENT;
-
-    // Collect entities ids and bundles.
-    $bundles = array();
-    $ids = array();
-    foreach ($entities as $key => $entity) {
-      $bundles[$entity->bundle()] = TRUE;
-      $ids[] = $load_current ? $key : $entity->getRevisionId();
-    }
-
-    // Collect impacted fields.
-    $fields = array();
-    foreach ($bundles as $bundle => $v) {
-      foreach ($this->fieldInfo->getBundleInstances($this->entityType, $bundle) as $field_name => $instance) {
-        $fields[$field_name] = $instance->getField();
-      }
-    }
-
-    // Load field data.
-    $all_langcodes = array_keys(language_list());
-    foreach ($fields as $field_name => $field) {
-      $table = $load_current ? static::_fieldTableName($field) : static::_fieldRevisionTableName($field);
-
-      // If the field is translatable ensure that only values having valid
-      // languages are retrieved. Since we are loading values for multiple
-      // entities, we cannot limit the query to the available translations.
-      $langcodes = $field->isFieldTranslatable() ? $all_langcodes : array(Language::LANGCODE_NOT_SPECIFIED);
-      $results = $this->database->select($table, 't')
-        ->fields('t')
-        ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
-        ->condition('deleted', 0)
-        ->condition('langcode', $langcodes, 'IN')
-        ->orderBy('delta')
-        ->execute();
-
-      $delta_count = array();
-      foreach ($results as $row) {
-        if (!isset($delta_count[$row->entity_id][$row->langcode])) {
-          $delta_count[$row->entity_id][$row->langcode] = 0;
-        }
-
-        if ($field->getFieldCardinality() == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field->getFieldCardinality()) {
-          $item = array();
-          // For each column declared by the field, populate the item from the
-          // prefixed database column.
-          foreach ($field->getColumns() as $column => $attributes) {
-            $column_name = static::_fieldColumnName($field, $column);
-            // Unserialize the value if specified in the column schema.
-            $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name;
-          }
-
-          // Add the item to the field values for the entity.
-          $entities[$row->entity_id]->getTranslation($row->langcode)->{$field_name}[$delta_count[$row->entity_id][$row->langcode]] = $item;
-          $delta_count[$row->entity_id][$row->langcode]++;
-        }
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function doSaveFieldItems(EntityInterface $entity, $update) {
-    $vid = $entity->getRevisionId();
-    $id = $entity->id();
-    $bundle = $entity->bundle();
-    $entity_type = $entity->entityType();
-    if (!isset($vid)) {
-      $vid = $id;
-    }
-
-    foreach ($this->fieldInfo->getBundleInstances($entity_type, $bundle) as $field_name => $instance) {
-      $field = $instance->getField();
-      $table_name = static::_fieldTableName($field);
-      $revision_name = static::_fieldRevisionTableName($field);
-
-      // Delete and insert, rather than update, in case a value was added.
-      if ($update) {
-        // Only overwrite the field's base table if saving the default revision
-        // of an entity.
-        if ($entity->isDefaultRevision()) {
-          $this->database->delete($table_name)
-            ->condition('entity_id', $id)
-            ->execute();
-        }
-        $this->database->delete($revision_name)
-          ->condition('entity_id', $id)
-          ->condition('revision_id', $vid)
-          ->execute();
-      }
-
-      // Prepare the multi-insert query.
-      $do_insert = FALSE;
-      $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
-      foreach ($field->getColumns() as $column => $attributes) {
-        $columns[] = static::_fieldColumnName($field, $column);
-      }
-      $query = $this->database->insert($table_name)->fields($columns);
-      $revision_query = $this->database->insert($revision_name)->fields($columns);
-
-      $langcodes = $field->isFieldTranslatable() ? array_keys($entity->getTranslationLanguages()) : array(Language::LANGCODE_NOT_SPECIFIED);
-      foreach ($langcodes as $langcode) {
-        $delta_count = 0;
-        $items = $entity->getTranslation($langcode)->get($field_name);
-        $items->filterEmptyValues();
-        foreach ($items as $delta => $item) {
-          // We now know we have someting to insert.
-          $do_insert = TRUE;
-          $record = array(
-            'entity_id' => $id,
-            'revision_id' => $vid,
-            'bundle' => $bundle,
-            'delta' => $delta,
-            'langcode' => $langcode,
-          );
-          foreach ($field->getColumns() as $column => $attributes) {
-            $column_name = static::_fieldColumnName($field, $column);
-            // Serialize the value if specified in the column schema.
-            $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column;
-          }
-          $query->values($record);
-          $revision_query->values($record);
-
-          if ($field->getFieldCardinality() != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field->getFieldCardinality()) {
-            break;
-          }
-        }
-      }
-
-      // Execute the query if we have values to insert.
-      if ($do_insert) {
-        // Only overwrite the field's base table if saving the default revision
-        // of an entity.
-        if ($entity->isDefaultRevision()) {
-          $query->execute();
-        }
-        $revision_query->execute();
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function doDeleteFieldItems(EntityInterface $entity) {
-    foreach ($this->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
-      $field = $instance->getField();
-      $table_name = static::_fieldTableName($field);
-      $revision_name = static::_fieldRevisionTableName($field);
-      $this->database->delete($table_name)
-        ->condition('entity_id', $entity->id())
-        ->execute();
-      $this->database->delete($revision_name)
-        ->condition('entity_id', $entity->id())
-        ->execute();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function doDeleteFieldItemsRevision(EntityInterface $entity) {
-    $vid = $entity->getRevisionId();
-    if (isset($vid)) {
-      foreach ($this->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
-        $revision_name = static::_fieldRevisionTableName($instance->getField());
-        $this->database->delete($revision_name)
-          ->condition('entity_id', $entity->id())
-          ->condition('revision_id', $vid)
-          ->execute();
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldCreate(FieldInterface $field) {
-    $schema = $this->_fieldSqlSchema($field);
-    foreach ($schema as $name => $table) {
-      $this->database->schema()->createTable($name, $table);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldUpdate(FieldInterface $field) {
-    $original = $field->original;
-
-    if (!$field->hasData()) {
-      // There is no data. Re-create the tables completely.
-
-      if ($this->database->supportsTransactionalDDL()) {
-        // If the database supports transactional DDL, we can go ahead and rely
-        // on it. If not, we will have to rollback manually if something fails.
-        $transaction = $this->database->startTransaction();
-      }
-
-      try {
-        $original_schema = $this->_fieldSqlSchema($original);
-        foreach ($original_schema as $name => $table) {
-          $this->database->schema()->dropTable($name, $table);
-        }
-        $schema = $this->_fieldSqlSchema($field);
-        foreach ($schema as $name => $table) {
-          $this->database->schema()->createTable($name, $table);
-        }
-      }
-      catch (\Exception $e) {
-        if ($this->database->supportsTransactionalDDL()) {
-          $transaction->rollback();
-        }
-        else {
-          // Recreate tables.
-          $original_schema = $this->_fieldSqlSchema($original);
-          foreach ($original_schema as $name => $table) {
-            if (!$this->database->schema()->tableExists($name)) {
-              $this->database->schema()->createTable($name, $table);
-            }
-          }
-        }
-        throw $e;
-      }
-    }
-    else {
-      if ($field->getColumns() != $original->getColumns()) {
-        throw new FieldUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data.");
-      }
-      // There is data, so there are no column changes. Drop all the prior
-      // indexes and create all the new ones, except for all the priors that
-      // exist unchanged.
-      $table = static::_fieldTableName($original);
-      $revision_table = static::_fieldRevisionTableName($original);
-
-      $schema = $field->getSchema();
-      $original_schema = $original->getSchema();
-
-      foreach ($original_schema['indexes'] as $name => $columns) {
-        if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) {
-          $real_name = static::_fieldIndexName($field, $name);
-          $this->database->schema()->dropIndex($table, $real_name);
-          $this->database->schema()->dropIndex($revision_table, $real_name);
-        }
-      }
-      $table = static::_fieldTableName($field);
-      $revision_table = static::_fieldRevisionTableName($field);
-      foreach ($schema['indexes'] as $name => $columns) {
-        if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) {
-          $real_name = static::_fieldIndexName($field, $name);
-          $real_columns = array();
-          foreach ($columns as $column_name) {
-            // Indexes can be specified as either a column name or an array with
-            // column name and length. Allow for either case.
-            if (is_array($column_name)) {
-              $real_columns[] = array(
-                static::_fieldColumnName($field, $column_name[0]),
-                $column_name[1],
-              );
-            }
-            else {
-              $real_columns[] = static::_fieldColumnName($field, $column_name);
-            }
-          }
-          $this->database->schema()->addIndex($table, $real_name, $real_columns);
-          $this->database->schema()->addIndex($revision_table, $real_name, $real_columns);
-        }
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldDelete(FieldInterface $field) {
-    // Mark all data associated with the field for deletion.
-    $field->deleted = FALSE;
-    $table = static::_fieldTableName($field);
-    $revision_table = static::_fieldRevisionTableName($field);
-    $this->database->update($table)
-      ->fields(array('deleted' => 1))
-      ->execute();
-
-    // Move the table to a unique name while the table contents are being
-    // deleted.
-    $field->deleted = TRUE;
-    $new_table = static::_fieldTableName($field);
-    $revision_new_table = static::_fieldRevisionTableName($field);
-    $this->database->schema()->renameTable($table, $new_table);
-    $this->database->schema()->renameTable($revision_table, $revision_new_table);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onInstanceDelete(FieldInstanceInterface $instance) {
-    $field = $instance->getField();
-    $table_name = static::_fieldTableName($field);
-    $revision_name = static::_fieldRevisionTableName($field);
-    $this->database->update($table_name)
-      ->fields(array('deleted' => 1))
-      ->condition('bundle', $instance->bundle)
-      ->execute();
-    $this->database->update($revision_name)
-      ->fields(array('deleted' => 1))
-      ->condition('bundle', $instance->bundle)
-      ->execute();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onBundleRename($bundle, $bundle_new) {
-    // We need to account for deleted or inactive fields and instances. The
-    // method runs before the instance definitions are updated, so we need to
-    // fetch them using the old bundle name.
-    $instances = field_read_instances(array('entity_type' => $this->entityType, 'bundle' => $bundle), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
-    foreach ($instances as $instance) {
-      $field = $instance->getField();
-      $table_name = static::_fieldTableName($field);
-      $revision_name = static::_fieldRevisionTableName($field);
-      $this->database->update($table_name)
-        ->fields(array('bundle' => $bundle_new))
-        ->condition('bundle', $bundle)
-        ->execute();
-      $this->database->update($revision_name)
-        ->fields(array('bundle' => $bundle_new))
-        ->condition('bundle', $bundle)
-        ->execute();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceInterface $instance) {
-    $field = $instance->getField();
-    $table_name = static::_fieldTableName($field);
-    $query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC))
-      ->condition('entity_id', $entity->id())
-      ->orderBy('delta');
-    foreach ($field->getColumns() as $column_name => $data) {
-      $query->addField('t', static::_fieldColumnName($field, $column_name), $column_name);
-    }
-    return $query->execute()->fetchAll();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function purgeFieldItems(EntityInterface $entity, FieldInstanceInterface $instance) {
-    $field = $instance->getField();
-    $table_name = static::_fieldTableName($field);
-    $revision_name = static::_fieldRevisionTableName($field);
-    $this->database->delete($table_name)
-      ->condition('entity_id', $entity->id())
-      ->execute();
-    $this->database->delete($revision_name)
-      ->condition('entity_id', $entity->id())
-      ->execute();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldPurge(FieldInterface $field) {
-    $table_name = static::_fieldTableName($field);
-    $revision_name = static::_fieldRevisionTableName($field);
-    $this->database->schema()->dropTable($table_name);
-    $this->database->schema()->dropTable($revision_name);
-  }
-
-  /**
-   * Gets the SQL table schema.
-   *
-   * @private Calling this function circumvents the entity system and is
-   * strongly discouraged. This function is not considered part of the public
-   * API and modules relying on it might break even in minor releases.
-   *
-   * @param \Drupal\field\FieldInterface $field
-   *   The field object
-   * @param array $schema
-   *   The field schema array. Mandatory for upgrades, omit otherwise.
-   *
-   * @return array
-   *   The same as a hook_schema() implementation for the data and the
-   *   revision tables.
-   *
-   * @see hook_schema()
-   */
-  public static function _fieldSqlSchema(FieldInterface $field, array $schema = NULL) {
-    if ($field->deleted) {
-      $description_current = "Data storage for deleted field {$field->uuid()} ({$field->entity_type}, {$field->getFieldName()}).";
-      $description_revision = "Revision archive storage for deleted field {$field->uuid()} ({$field->entity_type}, {$field->getFieldName()}).";
-    }
-    else {
-      $description_current = "Data storage for {$field->entity_type} field {$field->getFieldName()}.";
-      $description_revision = "Revision archive storage for {$field->entity_type} field {$field->getFieldName()}.";
-    }
-
-    $current = array(
-      'description' => $description_current,
-      'fields' => array(
-        'bundle' => array(
-          'type' => 'varchar',
-          'length' => 128,
-          'not null' => TRUE,
-          'default' => '',
-          'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
-        ),
-        'deleted' => array(
-          'type' => 'int',
-          'size' => 'tiny',
-          'not null' => TRUE,
-          'default' => 0,
-          'description' => 'A boolean indicating whether this data item has been deleted'
-        ),
-        'entity_id' => array(
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'description' => 'The entity id this data is attached to',
-        ),
-        'revision_id' => array(
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => FALSE,
-          'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
-        ),
-        'langcode' => array(
-          'type' => 'varchar',
-          'length' => 32,
-          'not null' => TRUE,
-          'default' => '',
-          'description' => 'The language code for this data item.',
-        ),
-        'delta' => array(
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'description' => 'The sequence number for this data item, used for multi-value fields',
-        ),
-      ),
-      'primary key' => array('entity_id', 'deleted', 'delta', 'langcode'),
-      'indexes' => array(
-        'bundle' => array('bundle'),
-        'deleted' => array('deleted'),
-        'entity_id' => array('entity_id'),
-        'revision_id' => array('revision_id'),
-        'langcode' => array('langcode'),
-      ),
-    );
-
-    if (!$schema) {
-      $schema = $field->getSchema();
-    }
-
-    // Add field columns.
-    foreach ($schema['columns'] as $column_name => $attributes) {
-      $real_name = static::_fieldColumnName($field, $column_name);
-      $current['fields'][$real_name] = $attributes;
-    }
-
-    // Add indexes.
-    foreach ($schema['indexes'] as $index_name => $columns) {
-      $real_name = static::_fieldIndexName($field, $index_name);
-      foreach ($columns as $column_name) {
-        // Indexes can be specified as either a column name or an array with
-        // column name and length. Allow for either case.
-        if (is_array($column_name)) {
-          $current['indexes'][$real_name][] = array(
-            static::_fieldColumnName($field, $column_name[0]),
-            $column_name[1],
-          );
-        }
-        else {
-          $current['indexes'][$real_name][] = static::_fieldColumnName($field, $column_name);
-        }
-      }
-    }
-
-    // Add foreign keys.
-    foreach ($schema['foreign keys'] as $specifier => $specification) {
-      $real_name = static::_fieldIndexName($field, $specifier);
-      $current['foreign keys'][$real_name]['table'] = $specification['table'];
-      foreach ($specification['columns'] as $column_name => $referenced) {
-        $sql_storage_column = static::_fieldColumnName($field, $column_name);
-        $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
-      }
-    }
-
-    // Construct the revision table.
-    $revision = $current;
-    $revision['description'] = $description_revision;
-    $revision['primary key'] = array('entity_id', 'revision_id', 'deleted', 'delta', 'langcode');
-    $revision['fields']['revision_id']['not null'] = TRUE;
-    $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
-
-    return array(
-      static::_fieldTableName($field) => $current,
-      static::_fieldRevisionTableName($field) => $revision,
-    );
-  }
-
-  /**
-   * Generates a table name for a field data table.
-   *
-   * @private Calling this function circumvents the entity system and is
-   * strongly discouraged. This function is not considered part of the public
-   * API and modules relying on it might break even in minor releases. Only
-   * call this function to write a query that \Drupal::entityQuery() does not
-   * support. Always call entity_load() before using the data found in the
-   * table.
-   *
-   * @param \Drupal\field\FieldInterface $field
-   *   The field object.
-   *
-   * @return string
-   *   A string containing the generated name for the database table.
-   *
-   */
-  static public function _fieldTableName(FieldInterface $field) {
-    if ($field->deleted) {
-      // When a field is a deleted, the table is renamed to
-      // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
-      // table names longer than 64 characters, we hash the uuid and return the
-      // first 10 characters so we end up with a short unique ID.
-      return "field_deleted_data_" . substr(hash('sha256', $field->uuid()), 0, 10);
-    }
-    else {
-      return static::_generateFieldTableName($field, FALSE);
-    }
-  }
-
-  /**
-   * Generates a table name for a field revision archive table.
-   *
-   * @private Calling this function circumvents the entity system and is
-   * strongly discouraged. This function is not considered part of the public
-   * API and modules relying on it might break even in minor releases. Only
-   * call this function to write a query that \Drupal::entityQuery() does not
-   * support. Always call entity_load() before using the data found in the
-   * table.
-   *
-   * @param \Drupal\field\FieldInterface $field
-   *   The field object.
-   *
-   * @return string
-   *   A string containing the generated name for the database table.
-   */
-  static public function _fieldRevisionTableName(FieldInterface $field) {
-    if ($field->deleted) {
-      // When a field is a deleted, the table is renamed to
-      // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with
-      // table names longer than 64 characters, we hash the uuid and return the
-      // first 10 characters so we end up with a short unique ID.
-      return "field_deleted_revision_" . substr(hash('sha256', $field->uuid()), 0, 10);
-    }
-    else {
-      return static::_generateFieldTableName($field, TRUE);
-    }
-  }
-
-  /**
-   * Generates a safe and unanbiguous field table name.
-   *
-   * The method accounts for a maximum table name length of 64 characters, and
-   * takes care of disambiguation.
-   *
-   * @param \Drupal\field\FieldInterface $field
-   *   The field object.
-   * @param bool $revision
-   *   TRUE for revision table, FALSE otherwise.
-   *
-   * @return string
-   *   The final table name.
-   */
-  static protected function _generateFieldTableName(FieldInterface $field, $revision) {
-    $separator = $revision ? '_revision__' : '__';
-    $table_name = $field->entity_type . $separator .  $field->name;
-    // Limit the string to 48 characters, keeping a 16 characters margin for db
-    // prefixes.
-    if (strlen($table_name) > 48) {
-      // Use a shorter separator, a truncated entity_type, and a hash of the
-      // field UUID.
-      $separator = $revision ? '_r__' : '__';
-      // Truncate to the same length for the current and revision tables.
-      $entity_type = substr($field->entity_type, 0, 34);
-      $field_hash = substr(hash('sha256', $field->uuid), 0, 10);
-      $table_name = $entity_type . $separator . $field_hash;
-    }
-    return $table_name;
-  }
-
-  /**
-   * Generates an index name for a field data table.
-   *
-   * @private Calling this function circumvents the entity system and is
-   * strongly discouraged. This function is not considered part of the public
-   * API and modules relying on it might break even in minor releases.
-   *
-   * @param \Drupal\field\FieldInterface $field
-   *   The field structure
-   * @param string $index
-   *   The name of the index.
-   *
-   * @return string
-   *   A string containing a generated index name for a field data table that is
-   *   unique among all other fields.
-   */
-  static public function _fieldIndexName(FieldInterface $field, $index) {
-    return $field->getFieldName() . '_' . $index;
-  }
-
-  /**
-   * Generates a column name for a field data table.
-   *
-   * @private Calling this function circumvents the entity system and is
-   * strongly discouraged. This function is not considered part of the public
-   * API and modules relying on it might break even in minor releases. Only
-   * call this function to write a query that \Drupal::entityQuery() does not
-   * support. Always call entity_load() before using the data found in the
-   * table.
-   *
-   * @param \Drupal\field\FieldInterface $field
-   *   The field object.
-   * @param string $column
-   *   The name of the column.
-   *
-   * @return string
-   *   A string containing a generated column name for a field data table that is
-   *   unique among all other fields.
-   */
-  static public function _fieldColumnName(FieldInterface $field, $column) {
-    return in_array($column, Field::getReservedColumns()) ? $column : $field->getFieldName() . '_' . $column;
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
deleted file mode 100644
index 3990d9ecc145851fe8a2341f3997e0658e94c944..0000000000000000000000000000000000000000
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ /dev/null
@@ -1,557 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\DatabaseStorageControllerNG.
- */
-
-namespace Drupal\Core\Entity;
-
-use Drupal\Core\Language\Language;
-use Drupal\field\FieldInfo;
-use Drupal\Core\Entity\Query\QueryInterface;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\DatabaseStorageController;
-use Drupal\Core\Entity\EntityStorageException;
-use Drupal\Component\Uuid\UuidInterface;
-use Drupal\Core\Database\Connection;
-
-/**
- * Implements Field API specific enhancements to the DatabaseStorageController class.
- *
- * @todo: Once all entity types have been converted, merge improvements into the
- * DatabaseStorageController class.
- *
- * @see \Drupal\Core\ContentEntityBase
- */
-class DatabaseStorageControllerNG extends DatabaseStorageController {
-
-  /**
-   * The entity class to use.
-   *
-   * @var string
-   */
-  protected $entityClass;
-
-  /**
-   * The entity bundle key.
-   *
-   * @var string|bool
-   */
-  protected $bundleKey;
-
-  /**
-   * The table that stores properties, if the entity has multilingual support.
-   *
-   * @var string
-   */
-  protected $dataTable;
-
-  /**
-   * The table that stores revision field data if the entity supports revisions.
-   *
-   * @var string
-   */
-  protected $revisionDataTable;
-
-  /**
-   * Overrides DatabaseStorageController::__construct().
-   */
-  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service) {
-    parent::__construct($entity_type,$entity_info, $database, $field_info, $uuid_service);
-    $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
-    $this->entityClass = $this->entityInfo['class'];
-
-    // Check if the entity type has a dedicated table for properties.
-    if (!empty($this->entityInfo['data_table'])) {
-      $this->dataTable = $this->entityInfo['data_table'];
-      // Entity types having both revision and translation support should always
-      // define a revision data table.
-      if ($this->revisionTable && !empty($this->entityInfo['revision_data_table'])) {
-        $this->revisionDataTable = $this->entityInfo['revision_data_table'];
-      }
-    }
-
-    // Work-a-round to let load() get stdClass storage records without having to
-    // override it. We map storage records to entities in
-    // DatabaseStorageControllerNG:: mapFromStorageRecords().
-    // @todo: Remove this once this is moved in the main controller.
-    unset($this->entityInfo['class']);
-  }
-
-  /**
-   * Overrides DatabaseStorageController::create().
-   *
-   * @param array $values
-   *   An array of values to set, keyed by field name. The value has to be
-   *   the plain value of an entity field, i.e. an array of field items.
-   *   If no numerically indexed array is given, the value will be set for the
-   *   first field item. For example, to set the first item of a 'name'
-   *   field one can pass:
-   *   @code
-   *     $values = array('name' => array(0 => array('value' => 'the name')));
-   *   @endcode
-   *   or
-   *   @code
-   *     $values = array('name' => array('value' => 'the name'));
-   *   @endcode
-   *   If the 'name' field is a defined as 'string_item' which supports
-   *   setting its value by a string, it's also possible to just pass the name
-   *   string:
-   *   @code
-   *     $values = array('name' => 'the name');
-   *   @endcode
-   *
-   * @return \Drupal\Core\Entity\EntityInterface
-   *   A new entity object.
-   */
-  public function create(array $values) {
-    $entity_class = $this->entityClass;
-    $entity_class::preCreate($this, $values);
-
-    // We have to determine the bundle first.
-    $bundle = FALSE;
-    if ($this->bundleKey) {
-      if (!isset($values[$this->bundleKey])) {
-        throw new EntityStorageException(format_string('Missing bundle for entity type @type', array('@type' => $this->entityType)));
-      }
-      $bundle = $values[$this->bundleKey];
-    }
-    $entity = new $this->entityClass(array(), $this->entityType, $bundle);
-
-    foreach ($entity as $name => $field) {
-      if (isset($values[$name])) {
-        $entity->$name = $values[$name];
-      }
-      elseif (!array_key_exists($name, $values)) {
-        $entity->get($name)->applyDefaultValue();
-      }
-      unset($values[$name]);
-    }
-
-    // Set any passed values for non-defined fields also.
-    foreach ($values as $name => $value) {
-      $entity->$name = $value;
-    }
-    $entity->postCreate($this);
-
-    // Modules might need to add or change the data initially held by the new
-    // entity object, for instance to fill-in default values.
-    $this->invokeHook('create', $entity);
-
-    return $entity;
-  }
-
-  /**
-   * Builds an entity query.
-   *
-   * @param \Drupal\Core\Entity\Query\QueryInterface $entity_query
-   *   EntityQuery instance.
-   * @param array $values
-   *   An associative array of properties of the entity, where the keys are the
-   *   property names and the values are the values those properties must have.
-   */
-  protected function buildPropertyQuery(QueryInterface $entity_query, array $values) {
-    if ($this->dataTable) {
-      // @todo We should not be using a condition to specify whether conditions
-      //   apply to the default language. See http://drupal.org/node/1866330.
-      // Default to the original entity language if not explicitly specified
-      // otherwise.
-      if (!array_key_exists('default_langcode', $values)) {
-        $values['default_langcode'] = 1;
-      }
-      // If the 'default_langcode' flag is explicitly not set, we do not care
-      // whether the queried values are in the original entity language or not.
-      elseif ($values['default_langcode'] === NULL) {
-        unset($values['default_langcode']);
-      }
-    }
-
-    parent::buildPropertyQuery($entity_query, $values);
-  }
-
-  /**
-   * Overrides DatabaseStorageController::attachLoad().
-   *
-   * Added mapping from storage records to entities.
-   */
-  protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
-    // Map the loaded stdclass records into entity objects and according fields.
-    $queried_entities = $this->mapFromStorageRecords($queried_entities, $load_revision);
-    parent::attachLoad($queried_entities, $load_revision);
-  }
-
-  /**
-   * Maps from storage records to entity objects.
-   *
-   * @param array $records
-   *   Associative array of query results, keyed on the entity ID.
-   * @param boolean $load_revision
-   *   (optional) TRUE if the revision should be loaded, defaults to FALSE.
-   *
-   * @return array
-   *   An array of entity objects implementing the EntityInterface.
-   */
-  protected function mapFromStorageRecords(array $records, $load_revision = FALSE) {
-    $entities = array();
-    foreach ($records as $id => $record) {
-      $entities[$id] = array();
-      foreach ($record as $name => $value) {
-        // Skip the item delta and item value levels but let the field assign
-        // the value as suiting. This avoids unnecessary array hierarchies and
-        // saves memory here.
-        $entities[$id][$name][Language::LANGCODE_DEFAULT] = $value;
-      }
-      // If we have no multilingual values we can instantiate entity objecs
-      // right now, otherwise we need to collect all the field values first.
-      if (!$this->dataTable) {
-        $bundle = $this->bundleKey ? $record->{$this->bundleKey} : FALSE;
-        // Turn the record into an entity class.
-        $entities[$id] = new $this->entityClass($entities[$id], $this->entityType, $bundle);
-      }
-    }
-    $this->attachPropertyData($entities, $load_revision);
-    return $entities;
-  }
-
-  /**
-   * Attaches property data in all languages for translatable properties.
-   *
-   * @param array &$entities
-   *   Associative array of entities, keyed on the entity ID.
-   * @param int $revision_id
-   *   (optional) The revision to be loaded. Defaults to FALSE.
-   */
-  protected function attachPropertyData(array &$entities, $revision_id = FALSE) {
-    if ($this->dataTable) {
-      // If a revision table is available, we need all the properties of the
-      // latest revision. Otherwise we fall back to the data table.
-      $table = $this->revisionDataTable ?: $this->dataTable;
-      $query = $this->database->select($table, 'data', array('fetch' => \PDO::FETCH_ASSOC))
-        ->fields('data')
-        ->condition($this->idKey, array_keys($entities))
-        ->orderBy('data.' . $this->idKey);
-
-      if ($this->revisionDataTable) {
-        if ($revision_id) {
-          $query->condition($this->revisionKey, $revision_id);
-        }
-        else {
-          // Get the revision IDs.
-          $revision_ids = array();
-          foreach ($entities as $values) {
-            $revision_ids[] = $values[$this->revisionKey];
-          }
-          $query->condition($this->revisionKey, $revision_ids);
-        }
-      }
-
-      $data = $query->execute();
-      $field_definition = \Drupal::entityManager()->getFieldDefinitions($this->entityType);
-      $translations = array();
-      if ($this->revisionDataTable) {
-        $data_fields = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo['revision_data_table']), drupal_schema_fields_sql($this->entityInfo['base_table'])));
-      }
-      else {
-        $data_fields = array_flip(drupal_schema_fields_sql($this->entityInfo['data_table']));
-      }
-
-      foreach ($data as $values) {
-        $id = $values[$this->idKey];
-
-        // Field values in default language are stored with
-        // Language::LANGCODE_DEFAULT as key.
-        $langcode = empty($values['default_langcode']) ? $values['langcode'] : Language::LANGCODE_DEFAULT;
-        $translations[$id][$langcode] = TRUE;
-
-        foreach ($field_definition as $name => $definition) {
-          if (isset($data_fields[$name])) {
-            $entities[$id][$name][$langcode] = $values[$name];
-          }
-        }
-      }
-
-      foreach ($entities as $id => $values) {
-        $bundle = $this->bundleKey ? $values[$this->bundleKey][Language::LANGCODE_DEFAULT] : FALSE;
-        // Turn the record into an entity class.
-        $entities[$id] = new $this->entityClass($values, $this->entityType, $bundle, array_keys($translations[$id]));
-      }
-    }
-  }
-
-  /**
-   * Overrides DatabaseStorageController::save().
-   *
-   * Added mapping from entities to storage records before saving.
-   */
-  public function save(EntityInterface $entity) {
-    $transaction = $this->database->startTransaction();
-    try {
-      // Sync the changes made in the fields array to the internal values array.
-      $entity->updateOriginalValues();
-
-      // Load the stored entity, if any.
-      if (!$entity->isNew() && !isset($entity->original)) {
-        $entity->original = entity_load_unchanged($this->entityType, $entity->id());
-      }
-
-      $entity->preSave($this);
-      $this->invokeFieldMethod('preSave', $entity);
-      $this->invokeHook('presave', $entity);
-
-      // Create the storage record to be saved.
-      $record = $this->mapToStorageRecord($entity);
-
-      if (!$entity->isNew()) {
-        if ($entity->isDefaultRevision()) {
-          $return = drupal_write_record($this->entityInfo['base_table'], $record, $this->idKey);
-        }
-        else {
-          // @todo, should a different value be returned when saving an entity
-          // with $isDefaultRevision = FALSE?
-          $return = FALSE;
-        }
-        if ($this->revisionKey) {
-          $record->{$this->revisionKey} = $this->saveRevision($entity);
-        }
-        if ($this->dataTable) {
-          $this->savePropertyData($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);
-        if ($this->dataTable) {
-          $this->invokeTranslationHooks($entity);
-        }
-      }
-      else {
-        // Ensure the entity is still seen as new after assigning it an id,
-        // while storing its data.
-        $entity->enforceIsNew();
-        $return = drupal_write_record($this->entityInfo['base_table'], $record);
-        $entity->{$this->idKey}->value = (string) $record->{$this->idKey};
-        if ($this->revisionKey) {
-          $entity->setNewRevision();
-          $record->{$this->revisionKey} = $this->saveRevision($entity);
-        }
-        if ($this->dataTable) {
-          $this->savePropertyData($entity);
-        }
-        if ($this->revisionDataTable) {
-          $this->savePropertyData($entity, 'revision_data_table');
-        }
-
-        // Reset general caches, but keep caches specific to certain entities.
-        $this->resetCache(array());
-
-        $entity->enforceIsNew(FALSE);
-        $entity->postSave($this, FALSE);
-        $this->invokeFieldMethod('insert', $entity);
-        $this->saveFieldItems($entity, FALSE);
-        $this->invokeHook('insert', $entity);
-      }
-
-      // Ignore slave server temporarily.
-      db_ignore_slave();
-      unset($entity->original);
-
-      return $return;
-    }
-    catch (\Exception $e) {
-      $transaction->rollback();
-      watchdog_exception($this->entityType, $e);
-      throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
-    }
-  }
-
-  /**
-   * Saves an entity revision.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   *
-   * @return integer
-   *   The revision id.
-   */
-  protected function saveRevision(EntityInterface $entity) {
-    $record = $this->mapToStorageRecord($entity, 'revision_table');
-
-    // When saving a new revision, set any existing revision ID to NULL so as
-    // to ensure that a new revision will actually be created.
-    if ($entity->isNewRevision() && isset($record->{$this->revisionKey})) {
-      $record->{$this->revisionKey} = NULL;
-    }
-
-    $entity->preSaveRevision($this, $record);
-
-    if ($entity->isNewRevision()) {
-      drupal_write_record($this->revisionTable, $record);
-      if ($entity->isDefaultRevision()) {
-        $this->database->update($this->entityInfo['base_table'])
-          ->fields(array($this->revisionKey => $record->{$this->revisionKey}))
-          ->condition($this->idKey, $record->{$this->idKey})
-          ->execute();
-      }
-    }
-    else {
-      drupal_write_record($this->revisionTable, $record, array($this->revisionKey));
-    }
-
-    // Make sure to update the new revision key for the entity.
-    $entity->{$this->revisionKey}->value = $record->{$this->revisionKey};
-
-    return $record->{$this->revisionKey};
-  }
-
-  /**
-   * Stores the entity property language-aware data.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   * @param string $table_key
-   *   (optional) The entity key identifying the target table. Defaults to
-   *   'data_table'.
-   */
-  protected function savePropertyData(EntityInterface $entity, $table_key = 'data_table') {
-    $table_name = $this->entityInfo[$table_key];
-    $revision = $table_key != 'data_table';
-
-    if (!$revision || !$entity->isNewRevision()) {
-      $key = $revision ? $this->revisionKey : $this->idKey;
-      $value = $revision ? $entity->getRevisionId() : $entity->id();
-      // Delete and insert to handle removed values.
-      $this->database->delete($table_name)
-        ->condition($key, $value)
-        ->execute();
-    }
-
-    $query = $this->database->insert($table_name);
-
-    foreach ($entity->getTranslationLanguages() as $langcode => $language) {
-      $translation = $entity->getTranslation($langcode);
-      $record = $this->mapToDataStorageRecord($translation, $table_key);
-      $values = (array) $record;
-      $query
-        ->fields(array_keys($values))
-        ->values($values);
-    }
-
-    $query->execute();
-  }
-
-  /**
-   * Maps from an entity object to the storage record.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   * @param string $table_key
-   *   (optional) The entity key identifying the target table. Defaults to
-   *   'base_table'.
-   *
-   * @return \stdClass
-   *   The record to store.
-   */
-  protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'base_table') {
-    $record = new \stdClass();
-    $definitions = $entity->getPropertyDefinitions();
-    $schema = drupal_get_schema($this->entityInfo[$table_key]);
-    $is_new = $entity->isNew();
-
-    foreach (drupal_schema_fields_sql($this->entityInfo[$table_key]) as $name) {
-      $info = $schema['fields'][$name];
-      $value = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL;
-      // If we are creating a new entity, we must not populate the record with
-      // NULL values otherwise defaults would not be applied.
-      if (isset($value) || !$is_new) {
-        $record->$name = drupal_schema_get_field_value($info, $value);
-      }
-    }
-
-    return $record;
-  }
-
-  /**
-   * Maps from an entity object to the storage record of the field data.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   * @param string $table_key
-   *   (optional) The entity key identifying the target table. Defaults to
-   *   'data_table'.
-   *
-   * @return \stdClass
-   *   The record to store.
-   */
-  protected function mapToDataStorageRecord(EntityInterface $entity, $table_key = 'data_table') {
-    $record = $this->mapToStorageRecord($entity, $table_key);
-    $record->langcode = $entity->language()->id;
-    $record->default_langcode = intval($record->langcode == $entity->getUntranslated()->language()->id);
-    return $record;
-  }
-
-  /**
-   * Overwrites \Drupal\Core\Entity\DatabaseStorageController::delete().
-   */
-  public function delete(array $entities) {
-    if (!$entities) {
-      // If no IDs or invalid IDs were passed, do nothing.
-      return;
-    }
-
-    $transaction = $this->database->startTransaction();
-    try {
-      $entity_class = $this->entityClass;
-      $entity_class::preDelete($this, $entities);
-
-      foreach ($entities as $entity) {
-        $this->invokeHook('predelete', $entity);
-      }
-      $ids = array_keys($entities);
-
-      $this->database->delete($this->entityInfo['base_table'])
-        ->condition($this->idKey, $ids)
-        ->execute();
-
-      if ($this->revisionKey) {
-        $this->database->delete($this->revisionTable)
-          ->condition($this->idKey, $ids)
-          ->execute();
-      }
-
-      if ($this->dataTable) {
-        $this->database->delete($this->dataTable)
-          ->condition($this->idKey, $ids)
-          ->execute();
-      }
-
-      if ($this->revisionDataTable) {
-        $this->database->delete($this->revisionDataTable)
-          ->condition($this->idKey, $ids)
-          ->execute();
-      }
-
-      // Reset the cache as soon as the changes have been applied.
-      $this->resetCache($ids);
-
-      $entity_class::postDelete($this, $entities);
-      foreach ($entities as $entity) {
-        $this->invokeFieldMethod('delete', $entity);
-        $this->deleteFieldItems($entity);
-        $this->invokeHook('delete', $entity);
-      }
-      // Ignore slave server temporarily.
-      db_ignore_slave();
-    }
-    catch (\Exception $e) {
-      $transaction->rollback();
-      watchdog_exception($this->entityType, $e);
-      throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
-    }
-  }
-}
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
index 475688e1029eea36132d5636274e18b413c39680..f303f791faeb6438c469748734ca16baafbb6a3e 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
@@ -136,23 +136,6 @@ protected function cacheSet($entities) {
     }
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function invokeFieldMethod($method, EntityInterface $entity) {
-    // Only act on content entities.
-    if (!($entity instanceof ContentEntityInterface)) {
-      return;
-    }
-
-    foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
-      $translation = $entity->getTranslation($langcode);
-      foreach ($translation as $field) {
-        $field->$method();
-      }
-    }
-  }
-
   /**
    * Invokes a hook on behalf of the entity.
    *
@@ -169,30 +152,4 @@ protected function invokeHook($hook, EntityInterface $entity) {
     module_invoke_all('entity_' . $hook, $entity, $this->entityType);
   }
 
-  /**
-   * Checks translation statuses and invoke the related hooks if needed.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity being saved.
-   */
-  protected function invokeTranslationHooks(EntityInterface $entity) {
-    // Only act on content entities.
-    if (!($entity instanceof ContentEntityInterface)) {
-      return;
-    }
-    $translations = $entity->getTranslationLanguages(FALSE);
-    $original_translations = $entity->original->getTranslationLanguages(FALSE);
-    $all_translations = array_keys($translations + $original_translations);
-
-    // Notify modules of translation insertion/deletion.
-    foreach ($all_translations as $langcode) {
-      if (isset($translations[$langcode]) && !isset($original_translations[$langcode])) {
-        $this->invokeHook('translation_insert', $entity->getTranslation($langcode));
-      }
-      elseif (!isset($translations[$langcode]) && isset($original_translations[$langcode])) {
-        $this->invokeHook('translation_delete', $entity->getTranslation($langcode));
-      }
-    }
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
index 63a38bfc2ced07c618c7126e9d781023fb15c767..0a275e9c19a0c4b607b537dfd34457261f15aa31 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
@@ -153,14 +153,4 @@ public function save(EntityInterface $entity);
    */
   public function getQueryServicename();
 
-  /**
-   * Invokes a method on the Field objects within an entity.
-   *
-   * @param string $method
-   *   The method name.
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   */
-  public function invokeFieldMethod($method, EntityInterface $entity);
-
 }
diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
new file mode 100644
index 0000000000000000000000000000000000000000..07cf1151e57d2faf9d0b809ecf36bdac33efd80e
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
@@ -0,0 +1,1470 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\DatabaseStorageController.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Core\Database\Connection;
+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;
+use Drupal\field\FieldInstanceInterface;
+use Drupal\field\Entity\Field;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Defines a base entity controller class.
+ *
+ * Default implementation of Drupal\Core\Entity\EntityStorageControllerInterface.
+ *
+ * This class can be used as-is by most simple entity types. Entity types
+ * requiring special handling can extend the class.
+ */
+class FieldableDatabaseStorageController extends FieldableEntityStorageControllerBase {
+
+  /**
+   * Name of entity's revision database table field, if it supports revisions.
+   *
+   * Has the value FALSE if this entity does not use revisions.
+   *
+   * @var string
+   */
+  protected $revisionKey = FALSE;
+
+  /**
+   * The table that stores revisions, if the entity supports revisions.
+   *
+   * @var string
+   */
+  protected $revisionTable;
+
+  /**
+   * The table that stores properties, if the entity has multilingual support.
+   *
+   * @var string
+   */
+  protected $dataTable;
+
+  /**
+   * The table that stores revision field data if the entity supports revisions.
+   *
+   * @var string
+   */
+  protected $revisionDataTable;
+
+  /**
+   * Whether this entity type should use the static cache.
+   *
+   * Set by entity info.
+   *
+   * @var boolean
+   */
+  protected $cache;
+
+  /**
+   * Active database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The field info object.
+   *
+   * @var \Drupal\field\FieldInfo
+   */
+  protected $fieldInfo;
+
+  /**
+   * The entity bundle key.
+   *
+   * @var string|bool
+   */
+  protected $bundleKey = FALSE;
+
+  /**
+   * Name of the entity class.
+   *
+   * @var string
+   */
+  protected $entityClass;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+    return new static(
+      $entity_type,
+      $entity_info,
+      $container->get('database'),
+      $container->get('field.info')
+    );
+  }
+
+  /**
+   * Constructs a DatabaseStorageController object.
+   *
+   * @param string $entity_type
+   *   The entity type for which the instance is created.
+   * @param array $entity_info
+   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection to be used.
+   * @param \Drupal\field\FieldInfo $field_info
+   *   The field info service.
+   */
+  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
+    parent::__construct($entity_type, $entity_info);
+
+    $this->database = $database;
+    $this->fieldInfo = $field_info;
+    $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
+    $this->entityClass = $this->entityInfo['class'];
+
+    // Check if the entity type supports IDs.
+    if (isset($this->entityInfo['entity_keys']['id'])) {
+      $this->idKey = $this->entityInfo['entity_keys']['id'];
+    }
+
+    // Check if the entity type supports UUIDs.
+    if (!empty($this->entityInfo['entity_keys']['uuid'])) {
+      $this->uuidKey = $this->entityInfo['entity_keys']['uuid'];
+    }
+    else {
+      $this->uuidKey = FALSE;
+    }
+
+    // Check if the entity type supports revisions.
+    if (!empty($this->entityInfo['entity_keys']['revision'])) {
+      $this->revisionKey = $this->entityInfo['entity_keys']['revision'];
+      $this->revisionTable = $this->entityInfo['revision_table'];
+    }
+
+    // Check if the entity type has a dedicated table for fields.
+    if (!empty($this->entityInfo['data_table'])) {
+      $this->dataTable = $this->entityInfo['data_table'];
+      // Entity types having both revision and translation support should always
+      // define a revision data table.
+      if ($this->revisionTable && !empty($this->entityInfo['revision_data_table'])) {
+        $this->revisionDataTable = $this->entityInfo['revision_data_table'];
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function create(array $values) {
+    $entity_class = $this->entityClass;
+    $entity_class::preCreate($this, $values);
+
+    // We have to determine the bundle first.
+    $bundle = FALSE;
+    if ($this->bundleKey) {
+      if (!isset($values[$this->bundleKey])) {
+        throw new EntityStorageException(format_string('Missing bundle for entity type @type', array('@type' => $this->entityType)));
+      }
+      $bundle = $values[$this->bundleKey];
+    }
+    $entity = new $entity_class(array(), $this->entityType, $bundle);
+
+    foreach ($entity as $name => $field) {
+      if (isset($values[$name])) {
+        $entity->$name = $values[$name];
+      }
+      elseif (!array_key_exists($name, $values)) {
+        $entity->get($name)->applyDefaultValue();
+      }
+      unset($values[$name]);
+    }
+
+    // Set any passed values for non-defined fields also.
+    foreach ($values as $name => $value) {
+      $entity->$name = $value;
+    }
+    $entity->postCreate($this);
+
+    // Modules might need to add or change the data initially held by the new
+    // entity object, for instance to fill-in default values.
+    $this->invokeHook('create', $entity);
+
+    return $entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadMultiple(array $ids = NULL) {
+    $entities = array();
+
+    // Create a new variable which is either a prepared version of the $ids
+    // array for later comparison with the entity cache, or FALSE if no $ids
+    // were passed. The $ids array is reduced as items are loaded from cache,
+    // 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.
+    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
+    // is set to NULL (so we load all entities) or if there are any ids left to
+    // load.
+    if ($ids === NULL || $ids) {
+      // Build and execute the query.
+      $query_result = $this->buildQuery($ids)->execute();
+      $queried_entities = $query_result->fetchAllAssoc($this->idKey);
+    }
+
+    // Pass all entities loaded from the database through $this->attachLoad(),
+    // which attaches fields (if supported by the entity type) and calls the
+    // entity type specific load callback, for example hook_node_load().
+    if (!empty($queried_entities)) {
+      $this->attachLoad($queried_entities);
+      $entities += $queried_entities;
+    }
+
+    if ($this->cache) {
+      // Add entities to the cache.
+      if (!empty($queried_entities)) {
+        $this->cacheSet($queried_entities);
+      }
+    }
+
+    // Ensure that the returned array is ordered the same as the original
+    // $ids array if this was passed in and remove any invalid ids.
+    if ($passed_ids) {
+      // Remove any invalid ids from the array.
+      $passed_ids = array_intersect_key($passed_ids, $entities);
+      foreach ($entities as $entity) {
+        $passed_ids[$entity->id()] = $entity;
+      }
+      $entities = $passed_ids;
+    }
+
+    return $entities;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function load($id) {
+    $entities = $this->loadMultiple(array($id));
+    return isset($entities[$id]) ? $entities[$id] : NULL;
+  }
+
+  /**
+   * Maps from storage records to entity objects.
+   *
+   * @param array $records
+   *   Associative array of query results, keyed on the entity ID.
+   * @param bool $load_revision
+   *   (optional) TRUE if the revision should be loaded, defaults to FALSE.
+   *
+   * @return array
+   *   An array of entity objects implementing the EntityInterface.
+   */
+  protected function mapFromStorageRecords(array $records, $load_revision = FALSE) {
+    $entities = array();
+    foreach ($records as $id => $record) {
+      $entities[$id] = array();
+      foreach ($record as $name => $value) {
+        // Skip the item delta and item value levels but let the field assign
+        // the value as suiting. This avoids unnecessary array hierarchies and
+        // saves memory here.
+        $entities[$id][$name][Language::LANGCODE_DEFAULT] = $value;
+      }
+      // If we have no multilingual values we can instantiate entity objecs
+      // right now, otherwise we need to collect all the field values first.
+      if (!$this->dataTable) {
+        $bundle = $this->bundleKey ? $record->{$this->bundleKey} : FALSE;
+        // Turn the record into an entity class.
+        $entities[$id] = new $this->entityClass($entities[$id], $this->entityType, $bundle);
+      }
+    }
+    $this->attachPropertyData($entities, $load_revision);
+    return $entities;
+  }
+
+  /**
+   * Attaches property data in all languages for translatable properties.
+   *
+   * @param array &$entities
+   *   Associative array of entities, keyed on the entity ID.
+   * @param int $revision_id
+   *   (optional) The revision to be loaded. Defaults to FALSE.
+   */
+  protected function attachPropertyData(array &$entities, $revision_id = FALSE) {
+    if ($this->dataTable) {
+      // If a revision table is available, we need all the properties of the
+      // latest revision. Otherwise we fall back to the data table.
+      $table = $this->revisionDataTable ?: $this->dataTable;
+      $query = $this->database->select($table, 'data', array('fetch' => \PDO::FETCH_ASSOC))
+        ->fields('data')
+        ->condition($this->idKey, array_keys($entities))
+        ->orderBy('data.' . $this->idKey);
+
+      if ($this->revisionDataTable) {
+        if ($revision_id) {
+          $query->condition($this->revisionKey, $revision_id);
+        }
+        else {
+          // Get the revision IDs.
+          $revision_ids = array();
+          foreach ($entities as $values) {
+            $revision_ids[] = $values[$this->revisionKey];
+          }
+          $query->condition($this->revisionKey, $revision_ids);
+        }
+      }
+
+      $data = $query->execute();
+      $field_definition = \Drupal::entityManager()->getFieldDefinitions($this->entityType);
+      $translations = array();
+      if ($this->revisionDataTable) {
+        $data_fields = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo['revision_data_table']), drupal_schema_fields_sql($this->entityInfo['base_table'])));
+      }
+      else {
+        $data_fields = array_flip(drupal_schema_fields_sql($this->entityInfo['data_table']));
+      }
+
+      foreach ($data as $values) {
+        $id = $values[$this->idKey];
+
+        // Field values in default language are stored with
+        // Language::LANGCODE_DEFAULT as key.
+        $langcode = empty($values['default_langcode']) ? $values['langcode'] : Language::LANGCODE_DEFAULT;
+        $translations[$id][$langcode] = TRUE;
+
+        foreach ($field_definition as $name => $definition) {
+          if (isset($data_fields[$name])) {
+            $entities[$id][$name][$langcode] = $values[$name];
+          }
+        }
+      }
+
+      foreach ($entities as $id => $values) {
+        $bundle = $this->bundleKey ? $values[$this->bundleKey][Language::LANGCODE_DEFAULT] : FALSE;
+        // Turn the record into an entity class.
+        $entities[$id] = new $this->entityClass($values, $this->entityType, $bundle, array_keys($translations[$id]));
+      }
+    }
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::loadRevision().
+   */
+  public function loadRevision($revision_id) {
+    // Build and execute the query.
+    $query_result = $this->buildQuery(array(), $revision_id)->execute();
+    $queried_entities = $query_result->fetchAllAssoc($this->idKey);
+
+    // Pass the loaded entities from the database through $this->attachLoad(),
+    // which attaches fields (if supported by the entity type) and calls the
+    // entity type specific load callback, for example hook_node_load().
+    if (!empty($queried_entities)) {
+      $this->attachLoad($queried_entities, $revision_id);
+    }
+    return reset($queried_entities);
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::deleteRevision().
+   */
+  public function deleteRevision($revision_id) {
+    if ($revision = $this->loadRevision($revision_id)) {
+      // Prevent deletion if this is the default revision.
+      if ($revision->isDefaultRevision()) {
+        throw new EntityStorageException('Default revision can not be deleted');
+      }
+
+      $this->database->delete($this->revisionTable)
+        ->condition($this->revisionKey, $revision->getRevisionId())
+        ->execute();
+      $this->invokeFieldMethod('deleteRevision', $revision);
+      $this->deleteFieldItemsRevision($revision);
+      $this->invokeHook('revision_delete', $revision);
+    }
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::loadByProperties().
+   */
+  public function loadByProperties(array $values = array()) {
+    // Build a query to fetch the entity IDs.
+    $entity_query = \Drupal::entityQuery($this->entityType);
+    $this->buildPropertyQuery($entity_query, $values);
+    $result = $entity_query->execute();
+    return $result ? $this->loadMultiple($result) : array();
+  }
+
+  /**
+   * Builds an entity query.
+   *
+   * @param \Drupal\Core\Entity\Query\QueryInterface $entity_query
+   *   EntityQuery instance.
+   * @param array $values
+   *   An associative array of properties of the entity, where the keys are the
+   *   property names and the values are the values those properties must have.
+   */
+  protected function buildPropertyQuery(QueryInterface $entity_query, array $values) {
+    if ($this->dataTable) {
+      // @todo We should not be using a condition to specify whether conditions
+      //   apply to the default language. See http://drupal.org/node/1866330.
+      // Default to the original entity language if not explicitly specified
+      // otherwise.
+      if (!array_key_exists('default_langcode', $values)) {
+        $values['default_langcode'] = 1;
+      }
+      // If the 'default_langcode' flag is explicitly not set, we do not care
+      // whether the queried values are in the original entity language or not.
+      elseif ($values['default_langcode'] === NULL) {
+        unset($values['default_langcode']);
+      }
+    }
+
+    foreach ($values as $name => $value) {
+      $entity_query->condition($name, $value);
+    }
+  }
+
+  /**
+   * Builds the query to load the entity.
+   *
+   * This has full revision support. For entities requiring special queries,
+   * the class can be extended, and the default query can be constructed by
+   * calling parent::buildQuery(). This is usually necessary when the object
+   * being loaded needs to be augmented with additional data from another
+   * table, such as loading node type into comments or vocabulary machine name
+   * into terms, however it can also support $conditions on different tables.
+   * See Drupal\comment\CommentStorageController::buildQuery() for an example.
+   *
+   * @param array|null $ids
+   *   An array of entity IDs, or NULL to load all entities.
+   * @param $revision_id
+   *   The ID of the revision to load, or FALSE if this query is asking for the
+   *   most current revision(s).
+   *
+   * @return SelectQuery
+   *   A SelectQuery object for loading the entity.
+   */
+  protected function buildQuery($ids, $revision_id = FALSE) {
+    $query = $this->database->select($this->entityInfo['base_table'], 'base');
+
+    $query->addTag($this->entityType . '_load_multiple');
+
+    if ($revision_id) {
+      $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id));
+    }
+    elseif ($this->revisionTable) {
+      $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
+    }
+
+    // Add fields from the {entity} table.
+    $entity_fields = drupal_schema_fields_sql($this->entityInfo['base_table']);
+
+    if ($this->revisionTable) {
+      // Add all fields from the {entity_revision} table.
+      $entity_revision_fields = drupal_map_assoc(drupal_schema_fields_sql($this->entityInfo['revision_table']));
+      // The ID field is provided by entity, so remove it.
+      unset($entity_revision_fields[$this->idKey]);
+
+      // Remove all fields from the base table that are also fields by the same
+      // name in the revision table.
+      $entity_field_keys = array_flip($entity_fields);
+      foreach ($entity_revision_fields as $name) {
+        if (isset($entity_field_keys[$name])) {
+          unset($entity_fields[$entity_field_keys[$name]]);
+        }
+      }
+      $query->fields('revision', $entity_revision_fields);
+
+      // Compare revision ID of the base and revision table, if equal then this
+      // is the default revision.
+      $query->addExpression('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, 'isDefaultRevision');
+    }
+
+    $query->fields('base', $entity_fields);
+
+    if ($ids) {
+      $query->condition("base.{$this->idKey}", $ids, 'IN');
+    }
+
+    return $query;
+  }
+
+  /**
+   * Attaches data to entities upon loading.
+   *
+   * This will attach fields, if the entity is fieldable. It calls
+   * hook_entity_load() for modules which need to add data to all entities.
+   * It also calls hook_TYPE_load() on the loaded entities. For example
+   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
+   * expects special parameters apart from the queried entities, you can set
+   * $this->hookLoadArguments prior to calling the method.
+   * See Drupal\node\NodeStorageController::attachLoad() for an example.
+   *
+   * @param $queried_entities
+   *   Associative array of query results, keyed on the entity ID.
+   * @param $load_revision
+   *   (optional) TRUE if the revision should be loaded, defaults to FALSE.
+   */
+  protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
+    // Map the loaded records into entity objects and according fields.
+    $queried_entities = $this->mapFromStorageRecords($queried_entities, $load_revision);
+
+    // Attach field values.
+    if ($this->entityInfo['fieldable']) {
+      $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);
+    }
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::delete().
+   */
+  public function delete(array $entities) {
+    if (!$entities) {
+      // If no IDs or invalid IDs were passed, do nothing.
+      return;
+    }
+
+    $transaction = $this->database->startTransaction();
+    try {
+      $entity_class = $this->entityClass;
+      $entity_class::preDelete($this, $entities);
+
+      foreach ($entities as $entity) {
+        $this->invokeHook('predelete', $entity);
+      }
+      $ids = array_keys($entities);
+
+      $this->database->delete($this->entityInfo['base_table'])
+        ->condition($this->idKey, $ids)
+        ->execute();
+
+      if ($this->revisionTable) {
+        $this->database->delete($this->revisionTable)
+          ->condition($this->idKey, $ids)
+          ->execute();
+      }
+
+      if ($this->dataTable) {
+        $this->database->delete($this->dataTable)
+          ->condition($this->idKey, $ids)
+          ->execute();
+      }
+
+      if ($this->revisionDataTable) {
+        $this->database->delete($this->revisionDataTable)
+          ->condition($this->idKey, $ids)
+          ->execute();
+      }
+
+      // Reset the cache as soon as the changes have been applied.
+      $this->resetCache($ids);
+
+      $entity_class::postDelete($this, $entities);
+      foreach ($entities as $entity) {
+        $this->invokeFieldMethod('delete', $entity);
+        $this->deleteFieldItems($entity);
+        $this->invokeHook('delete', $entity);
+      }
+      // Ignore slave server temporarily.
+      db_ignore_slave();
+    }
+    catch (\Exception $e) {
+      $transaction->rollback();
+      watchdog_exception($this->entityType, $e);
+      throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(EntityInterface $entity) {
+    $transaction = $this->database->startTransaction();
+    try {
+      // Sync the changes made in the fields array to the internal values array.
+      $entity->updateOriginalValues();
+
+      // Load the stored entity, if any.
+      if (!$entity->isNew() && !isset($entity->original)) {
+        $entity->original = entity_load_unchanged($this->entityType, $entity->id());
+      }
+
+      $entity->preSave($this);
+      $this->invokeFieldMethod('preSave', $entity);
+      $this->invokeHook('presave', $entity);
+
+      // Create the storage record to be saved.
+      $record = $this->mapToStorageRecord($entity);
+
+      if (!$entity->isNew()) {
+        if ($entity->isDefaultRevision()) {
+          $return = drupal_write_record($this->entityInfo['base_table'], $record, $this->idKey);
+        }
+        else {
+          // @todo, should a different value be returned when saving an entity
+          // with $isDefaultRevision = FALSE?
+          $return = FALSE;
+        }
+        if ($this->revisionTable) {
+          $record->{$this->revisionKey} = $this->saveRevision($entity);
+        }
+        if ($this->dataTable) {
+          $this->savePropertyData($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);
+        if ($this->dataTable) {
+          $this->invokeTranslationHooks($entity);
+        }
+      }
+      else {
+        // Ensure the entity is still seen as new after assigning it an id,
+        // while storing its data.
+        $entity->enforceIsNew();
+        $return = drupal_write_record($this->entityInfo['base_table'], $record);
+        $entity->{$this->idKey}->value = (string) $record->{$this->idKey};
+        if ($this->revisionTable) {
+          $entity->setNewRevision();
+          $record->{$this->revisionKey} = $this->saveRevision($entity);
+        }
+        if ($this->dataTable) {
+          $this->savePropertyData($entity);
+        }
+        if ($this->revisionDataTable) {
+          $this->savePropertyData($entity, 'revision_data_table');
+        }
+
+        // Reset general caches, but keep caches specific to certain entities.
+        $this->resetCache(array());
+
+        $entity->enforceIsNew(FALSE);
+        $entity->postSave($this, FALSE);
+        $this->invokeFieldMethod('insert', $entity);
+        $this->saveFieldItems($entity, FALSE);
+        $this->invokeHook('insert', $entity);
+      }
+
+      // Ignore slave server temporarily.
+      db_ignore_slave();
+      unset($entity->original);
+
+      return $return;
+    }
+    catch (\Exception $e) {
+      $transaction->rollback();
+      watchdog_exception($this->entityType, $e);
+      throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
+    }
+  }
+
+  /**
+   * Stores the entity property language-aware data.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   * @param string $table_key
+   *   (optional) The entity key identifying the target table. Defaults to
+   *   'data_table'.
+   */
+  protected function savePropertyData(EntityInterface $entity, $table_key = 'data_table') {
+    $table_name = $this->entityInfo[$table_key];
+    $revision = $table_key != 'data_table';
+
+    if (!$revision || !$entity->isNewRevision()) {
+      $key = $revision ? $this->revisionKey : $this->idKey;
+      $value = $revision ? $entity->getRevisionId() : $entity->id();
+      // Delete and insert to handle removed values.
+      $this->database->delete($table_name)
+        ->condition($key, $value)
+        ->execute();
+    }
+
+    $query = $this->database->insert($table_name);
+
+    foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+      $translation = $entity->getTranslation($langcode);
+      $record = $this->mapToDataStorageRecord($translation, $table_key);
+      $values = (array) $record;
+      $query
+        ->fields(array_keys($values))
+        ->values($values);
+    }
+
+    $query->execute();
+  }
+
+  /**
+   * Maps from an entity object to the storage record.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   * @param string $table_key
+   *   (optional) The entity key identifying the target table. Defaults to
+   *   'base_table'.
+   *
+   * @return \stdClass
+   *   The record to store.
+   */
+  protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'base_table') {
+    $record = new \stdClass();
+    $definitions = $entity->getPropertyDefinitions();
+    $schema = drupal_get_schema($this->entityInfo[$table_key]);
+    $is_new = $entity->isNew();
+
+    foreach (drupal_schema_fields_sql($this->entityInfo[$table_key]) as $name) {
+      $info = $schema['fields'][$name];
+      $value = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL;
+      // If we are creating a new entity, we must not populate the record with
+      // NULL values otherwise defaults would not be applied.
+      if (isset($value) || !$is_new) {
+        $record->$name = drupal_schema_get_field_value($info, $value);
+      }
+    }
+
+    return $record;
+  }
+
+  /**
+   * Maps from an entity object to the storage record of the field data.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   * @param string $table_key
+   *   (optional) The entity key identifying the target table. Defaults to
+   *   'data_table'.
+   *
+   * @return \stdClass
+   *   The record to store.
+   */
+  protected function mapToDataStorageRecord(EntityInterface $entity, $table_key = 'data_table') {
+    $record = $this->mapToStorageRecord($entity, $table_key);
+    $record->langcode = $entity->language()->id;
+    $record->default_langcode = intval($record->langcode == $entity->getUntranslated()->language()->id);
+    return $record;
+  }
+
+  /**
+   * Saves an entity revision.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   *
+   * @return int
+   *   The revision id.
+   */
+  protected function saveRevision(EntityInterface $entity) {
+    $record = $this->mapToStorageRecord($entity, 'revision_table');
+
+    // When saving a new revision, set any existing revision ID to NULL so as to
+    // ensure that a new revision will actually be created.
+    if ($entity->isNewRevision() && isset($record->{$this->revisionKey})) {
+      $record->{$this->revisionKey} = NULL;
+    }
+
+    $entity->preSaveRevision($this, $record);
+
+    if ($entity->isNewRevision()) {
+      drupal_write_record($this->revisionTable, $record);
+      if ($entity->isDefaultRevision()) {
+        $this->database->update($this->entityInfo['base_table'])
+          ->fields(array($this->revisionKey => $record->{$this->revisionKey}))
+          ->condition($this->idKey, $record->{$this->idKey})
+          ->execute();
+      }
+    }
+    else {
+      drupal_write_record($this->revisionTable, $record, $this->revisionKey);
+    }
+
+    // Make sure to update the new revision key for the entity.
+    $entity->{$this->revisionKey}->value = $record->{$this->revisionKey};
+
+    return $record->{$this->revisionKey};
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQueryServiceName() {
+    return 'entity.query.sql';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doLoadFieldItems($entities, $age) {
+    $load_current = $age == static::FIELD_LOAD_CURRENT;
+
+    // Collect entities ids and bundles.
+    $bundles = array();
+    $ids = array();
+    foreach ($entities as $key => $entity) {
+      $bundles[$entity->bundle()] = TRUE;
+      $ids[] = $load_current ? $key : $entity->getRevisionId();
+    }
+
+    // Collect impacted fields.
+    $fields = array();
+    foreach ($bundles as $bundle => $v) {
+      foreach ($this->fieldInfo->getBundleInstances($this->entityType, $bundle) as $field_name => $instance) {
+        $fields[$field_name] = $instance->getField();
+      }
+    }
+
+    // Load field data.
+    $all_langcodes = array_keys(language_list());
+    foreach ($fields as $field_name => $field) {
+      $table = $load_current ? static::_fieldTableName($field) : static::_fieldRevisionTableName($field);
+
+      // If the field is translatable ensure that only values having valid
+      // languages are retrieved. Since we are loading values for multiple
+      // entities, we cannot limit the query to the available translations.
+      $langcodes = $field->isFieldTranslatable() ? $all_langcodes : array(Language::LANGCODE_NOT_SPECIFIED);
+      $results = $this->database->select($table, 't')
+        ->fields('t')
+        ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
+        ->condition('deleted', 0)
+        ->condition('langcode', $langcodes, 'IN')
+        ->orderBy('delta')
+        ->execute();
+
+      $delta_count = array();
+      foreach ($results as $row) {
+        if (!isset($delta_count[$row->entity_id][$row->langcode])) {
+          $delta_count[$row->entity_id][$row->langcode] = 0;
+        }
+
+        if ($field->getFieldCardinality() == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field->getFieldCardinality()) {
+          $item = array();
+          // For each column declared by the field, populate the item from the
+          // prefixed database column.
+          foreach ($field->getColumns() as $column => $attributes) {
+            $column_name = static::_fieldColumnName($field, $column);
+            // Unserialize the value if specified in the column schema.
+            $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name;
+          }
+
+          // Add the item to the field values for the entity.
+          $entities[$row->entity_id]->getTranslation($row->langcode)->{$field_name}[$delta_count[$row->entity_id][$row->langcode]] = $item;
+          $delta_count[$row->entity_id][$row->langcode]++;
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doSaveFieldItems(EntityInterface $entity, $update) {
+    $vid = $entity->getRevisionId();
+    $id = $entity->id();
+    $bundle = $entity->bundle();
+    $entity_type = $entity->entityType();
+    if (!isset($vid)) {
+      $vid = $id;
+    }
+
+    foreach ($this->fieldInfo->getBundleInstances($entity_type, $bundle) as $field_name => $instance) {
+      $field = $instance->getField();
+      $table_name = static::_fieldTableName($field);
+      $revision_name = static::_fieldRevisionTableName($field);
+
+      // Delete and insert, rather than update, in case a value was added.
+      if ($update) {
+        // Only overwrite the field's base table if saving the default revision
+        // of an entity.
+        if ($entity->isDefaultRevision()) {
+          $this->database->delete($table_name)
+            ->condition('entity_id', $id)
+            ->execute();
+        }
+        $this->database->delete($revision_name)
+          ->condition('entity_id', $id)
+          ->condition('revision_id', $vid)
+          ->execute();
+      }
+
+      // Prepare the multi-insert query.
+      $do_insert = FALSE;
+      $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
+      foreach ($field->getColumns() as $column => $attributes) {
+        $columns[] = static::_fieldColumnName($field, $column);
+      }
+      $query = $this->database->insert($table_name)->fields($columns);
+      $revision_query = $this->database->insert($revision_name)->fields($columns);
+
+      $langcodes = $field->isFieldTranslatable() ? array_keys($entity->getTranslationLanguages()) : array(Language::LANGCODE_NOT_SPECIFIED);
+      foreach ($langcodes as $langcode) {
+        $delta_count = 0;
+        $items = $entity->getTranslation($langcode)->get($field_name);
+        $items->filterEmptyValues();
+        foreach ($items as $delta => $item) {
+          // We now know we have someting to insert.
+          $do_insert = TRUE;
+          $record = array(
+            'entity_id' => $id,
+            'revision_id' => $vid,
+            'bundle' => $bundle,
+            'delta' => $delta,
+            'langcode' => $langcode,
+          );
+          foreach ($field->getColumns() as $column => $attributes) {
+            $column_name = static::_fieldColumnName($field, $column);
+            // Serialize the value if specified in the column schema.
+            $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column;
+          }
+          $query->values($record);
+          $revision_query->values($record);
+
+          if ($field->getFieldCardinality() != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field->getFieldCardinality()) {
+            break;
+          }
+        }
+      }
+
+      // Execute the query if we have values to insert.
+      if ($do_insert) {
+        // Only overwrite the field's base table if saving the default revision
+        // of an entity.
+        if ($entity->isDefaultRevision()) {
+          $query->execute();
+        }
+        $revision_query->execute();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doDeleteFieldItems(EntityInterface $entity) {
+    foreach ($this->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
+      $field = $instance->getField();
+      $table_name = static::_fieldTableName($field);
+      $revision_name = static::_fieldRevisionTableName($field);
+      $this->database->delete($table_name)
+        ->condition('entity_id', $entity->id())
+        ->execute();
+      $this->database->delete($revision_name)
+        ->condition('entity_id', $entity->id())
+        ->execute();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doDeleteFieldItemsRevision(EntityInterface $entity) {
+    $vid = $entity->getRevisionId();
+    if (isset($vid)) {
+      foreach ($this->fieldInfo->getBundleInstances($entity->entityType(), $entity->bundle()) as $instance) {
+        $revision_name = static::_fieldRevisionTableName($instance->getField());
+        $this->database->delete($revision_name)
+          ->condition('entity_id', $entity->id())
+          ->condition('revision_id', $vid)
+          ->execute();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onFieldCreate(FieldInterface $field) {
+    $schema = $this->_fieldSqlSchema($field);
+    foreach ($schema as $name => $table) {
+      $this->database->schema()->createTable($name, $table);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onFieldUpdate(FieldInterface $field) {
+    $original = $field->original;
+
+    if (!$field->hasData()) {
+      // There is no data. Re-create the tables completely.
+
+      if ($this->database->supportsTransactionalDDL()) {
+        // If the database supports transactional DDL, we can go ahead and rely
+        // on it. If not, we will have to rollback manually if something fails.
+        $transaction = $this->database->startTransaction();
+      }
+
+      try {
+        $original_schema = $this->_fieldSqlSchema($original);
+        foreach ($original_schema as $name => $table) {
+          $this->database->schema()->dropTable($name, $table);
+        }
+        $schema = $this->_fieldSqlSchema($field);
+        foreach ($schema as $name => $table) {
+          $this->database->schema()->createTable($name, $table);
+        }
+      }
+      catch (\Exception $e) {
+        if ($this->database->supportsTransactionalDDL()) {
+          $transaction->rollback();
+        }
+        else {
+          // Recreate tables.
+          $original_schema = $this->_fieldSqlSchema($original);
+          foreach ($original_schema as $name => $table) {
+            if (!$this->database->schema()->tableExists($name)) {
+              $this->database->schema()->createTable($name, $table);
+            }
+          }
+        }
+        throw $e;
+      }
+    }
+    else {
+      if ($field->getColumns() != $original->getColumns()) {
+        throw new FieldUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data.");
+      }
+      // There is data, so there are no column changes. Drop all the prior
+      // indexes and create all the new ones, except for all the priors that
+      // exist unchanged.
+      $table = static::_fieldTableName($original);
+      $revision_table = static::_fieldRevisionTableName($original);
+
+      $schema = $field->getSchema();
+      $original_schema = $original->getSchema();
+
+      foreach ($original_schema['indexes'] as $name => $columns) {
+        if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) {
+          $real_name = static::_fieldIndexName($field, $name);
+          $this->database->schema()->dropIndex($table, $real_name);
+          $this->database->schema()->dropIndex($revision_table, $real_name);
+        }
+      }
+      $table = static::_fieldTableName($field);
+      $revision_table = static::_fieldRevisionTableName($field);
+      foreach ($schema['indexes'] as $name => $columns) {
+        if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) {
+          $real_name = static::_fieldIndexName($field, $name);
+          $real_columns = array();
+          foreach ($columns as $column_name) {
+            // Indexes can be specified as either a column name or an array with
+            // column name and length. Allow for either case.
+            if (is_array($column_name)) {
+              $real_columns[] = array(
+                static::_fieldColumnName($field, $column_name[0]),
+                $column_name[1],
+              );
+            }
+            else {
+              $real_columns[] = static::_fieldColumnName($field, $column_name);
+            }
+          }
+          $this->database->schema()->addIndex($table, $real_name, $real_columns);
+          $this->database->schema()->addIndex($revision_table, $real_name, $real_columns);
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onFieldDelete(FieldInterface $field) {
+    // Mark all data associated with the field for deletion.
+    $field->deleted = FALSE;
+    $table = static::_fieldTableName($field);
+    $revision_table = static::_fieldRevisionTableName($field);
+    $this->database->update($table)
+      ->fields(array('deleted' => 1))
+      ->execute();
+
+    // Move the table to a unique name while the table contents are being
+    // deleted.
+    $field->deleted = TRUE;
+    $new_table = static::_fieldTableName($field);
+    $revision_new_table = static::_fieldRevisionTableName($field);
+    $this->database->schema()->renameTable($table, $new_table);
+    $this->database->schema()->renameTable($revision_table, $revision_new_table);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onInstanceDelete(FieldInstanceInterface $instance) {
+    $field = $instance->getField();
+    $table_name = static::_fieldTableName($field);
+    $revision_name = static::_fieldRevisionTableName($field);
+    $this->database->update($table_name)
+      ->fields(array('deleted' => 1))
+      ->condition('bundle', $instance->bundle)
+      ->execute();
+    $this->database->update($revision_name)
+      ->fields(array('deleted' => 1))
+      ->condition('bundle', $instance->bundle)
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onBundleRename($bundle, $bundle_new) {
+    // We need to account for deleted or inactive fields and instances. The
+    // method runs before the instance definitions are updated, so we need to
+    // fetch them using the old bundle name.
+    $instances = field_read_instances(array('entity_type' => $this->entityType, 'bundle' => $bundle), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
+    foreach ($instances as $instance) {
+      $field = $instance->getField();
+      $table_name = static::_fieldTableName($field);
+      $revision_name = static::_fieldRevisionTableName($field);
+      $this->database->update($table_name)
+        ->fields(array('bundle' => $bundle_new))
+        ->condition('bundle', $bundle)
+        ->execute();
+      $this->database->update($revision_name)
+        ->fields(array('bundle' => $bundle_new))
+        ->condition('bundle', $bundle)
+        ->execute();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceInterface $instance) {
+    $field = $instance->getField();
+    $table_name = static::_fieldTableName($field);
+    $query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC))
+      ->condition('entity_id', $entity->id())
+      ->orderBy('delta');
+    foreach ($field->getColumns() as $column_name => $data) {
+      $query->addField('t', static::_fieldColumnName($field, $column_name), $column_name);
+    }
+    return $query->execute()->fetchAll();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function purgeFieldItems(EntityInterface $entity, FieldInstanceInterface $instance) {
+    $field = $instance->getField();
+    $table_name = static::_fieldTableName($field);
+    $revision_name = static::_fieldRevisionTableName($field);
+    $this->database->delete($table_name)
+      ->condition('entity_id', $entity->id())
+      ->execute();
+    $this->database->delete($revision_name)
+      ->condition('entity_id', $entity->id())
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onFieldPurge(FieldInterface $field) {
+    $table_name = static::_fieldTableName($field);
+    $revision_name = static::_fieldRevisionTableName($field);
+    $this->database->schema()->dropTable($table_name);
+    $this->database->schema()->dropTable($revision_name);
+  }
+
+  /**
+   * Gets the SQL table schema.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field object
+   * @param array $schema
+   *   The field schema array. Mandatory for upgrades, omit otherwise.
+   *
+   * @return array
+   *   The same as a hook_schema() implementation for the data and the
+   *   revision tables.
+   *
+   * @see hook_schema()
+   */
+  public static function _fieldSqlSchema(FieldInterface $field, array $schema = NULL) {
+    if ($field->deleted) {
+      $description_current = "Data storage for deleted field {$field->uuid()} ({$field->entity_type}, {$field->getFieldName()}).";
+      $description_revision = "Revision archive storage for deleted field {$field->uuid()} ({$field->entity_type}, {$field->getFieldName()}).";
+    }
+    else {
+      $description_current = "Data storage for {$field->entity_type} field {$field->getFieldName()}.";
+      $description_revision = "Revision archive storage for {$field->entity_type} field {$field->getFieldName()}.";
+    }
+
+    $current = array(
+      'description' => $description_current,
+      'fields' => array(
+        'bundle' => array(
+          'type' => 'varchar',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+          'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
+        ),
+        'deleted' => array(
+          'type' => 'int',
+          'size' => 'tiny',
+          'not null' => TRUE,
+          'default' => 0,
+          'description' => 'A boolean indicating whether this data item has been deleted'
+        ),
+        'entity_id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'The entity id this data is attached to',
+        ),
+        'revision_id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => FALSE,
+          'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
+        ),
+        'langcode' => array(
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => TRUE,
+          'default' => '',
+          'description' => 'The language code for this data item.',
+        ),
+        'delta' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'The sequence number for this data item, used for multi-value fields',
+        ),
+      ),
+      'primary key' => array('entity_id', 'deleted', 'delta', 'langcode'),
+      'indexes' => array(
+        'bundle' => array('bundle'),
+        'deleted' => array('deleted'),
+        'entity_id' => array('entity_id'),
+        'revision_id' => array('revision_id'),
+        'langcode' => array('langcode'),
+      ),
+    );
+
+    if (!$schema) {
+      $schema = $field->getSchema();
+    }
+
+    // Add field columns.
+    foreach ($schema['columns'] as $column_name => $attributes) {
+      $real_name = static::_fieldColumnName($field, $column_name);
+      $current['fields'][$real_name] = $attributes;
+    }
+
+    // Add indexes.
+    foreach ($schema['indexes'] as $index_name => $columns) {
+      $real_name = static::_fieldIndexName($field, $index_name);
+      foreach ($columns as $column_name) {
+        // Indexes can be specified as either a column name or an array with
+        // column name and length. Allow for either case.
+        if (is_array($column_name)) {
+          $current['indexes'][$real_name][] = array(
+            static::_fieldColumnName($field, $column_name[0]),
+            $column_name[1],
+          );
+        }
+        else {
+          $current['indexes'][$real_name][] = static::_fieldColumnName($field, $column_name);
+        }
+      }
+    }
+
+    // Add foreign keys.
+    foreach ($schema['foreign keys'] as $specifier => $specification) {
+      $real_name = static::_fieldIndexName($field, $specifier);
+      $current['foreign keys'][$real_name]['table'] = $specification['table'];
+      foreach ($specification['columns'] as $column_name => $referenced) {
+        $sql_storage_column = static::_fieldColumnName($field, $column_name);
+        $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
+      }
+    }
+
+    // Construct the revision table.
+    $revision = $current;
+    $revision['description'] = $description_revision;
+    $revision['primary key'] = array('entity_id', 'revision_id', 'deleted', 'delta', 'langcode');
+    $revision['fields']['revision_id']['not null'] = TRUE;
+    $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
+
+    return array(
+      static::_fieldTableName($field) => $current,
+      static::_fieldRevisionTableName($field) => $revision,
+    );
+  }
+
+  /**
+   * Generates a table name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field object.
+   *
+   * @return string
+   *   A string containing the generated name for the database table.
+   *
+   */
+  static public function _fieldTableName(FieldInterface $field) {
+    if ($field->deleted) {
+      // When a field is a deleted, the table is renamed to
+      // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
+      // table names longer than 64 characters, we hash the uuid and return the
+      // first 10 characters so we end up with a short unique ID.
+      return "field_deleted_data_" . substr(hash('sha256', $field->uuid()), 0, 10);
+    }
+    else {
+      return static::_generateFieldTableName($field, FALSE);
+    }
+  }
+
+  /**
+   * Generates a table name for a field revision archive table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field object.
+   *
+   * @return string
+   *   A string containing the generated name for the database table.
+   */
+  static public function _fieldRevisionTableName(FieldInterface $field) {
+    if ($field->deleted) {
+      // When a field is a deleted, the table is renamed to
+      // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with
+      // table names longer than 64 characters, we hash the uuid and return the
+      // first 10 characters so we end up with a short unique ID.
+      return "field_deleted_revision_" . substr(hash('sha256', $field->uuid()), 0, 10);
+    }
+    else {
+      return static::_generateFieldTableName($field, TRUE);
+    }
+  }
+
+  /**
+   * Generates a safe and unanbiguous field table name.
+   *
+   * The method accounts for a maximum table name length of 64 characters, and
+   * takes care of disambiguation.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field object.
+   * @param bool $revision
+   *   TRUE for revision table, FALSE otherwise.
+   *
+   * @return string
+   *   The final table name.
+   */
+  static protected function _generateFieldTableName(FieldInterface $field, $revision) {
+    $separator = $revision ? '_revision__' : '__';
+    $table_name = $field->entity_type . $separator .  $field->name;
+    // Limit the string to 48 characters, keeping a 16 characters margin for db
+    // prefixes.
+    if (strlen($table_name) > 48) {
+      // Use a shorter separator, a truncated entity_type, and a hash of the
+      // field UUID.
+      $separator = $revision ? '_r__' : '__';
+      // Truncate to the same length for the current and revision tables.
+      $entity_type = substr($field->entity_type, 0, 34);
+      $field_hash = substr(hash('sha256', $field->uuid), 0, 10);
+      $table_name = $entity_type . $separator . $field_hash;
+    }
+    return $table_name;
+  }
+
+  /**
+   * Generates an index name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field structure
+   * @param string $index
+   *   The name of the index.
+   *
+   * @return string
+   *   A string containing a generated index name for a field data table that is
+   *   unique among all other fields.
+   */
+  static public function _fieldIndexName(FieldInterface $field, $index) {
+    return $field->getFieldName() . '_' . $index;
+  }
+
+  /**
+   * Generates a column name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field object.
+   * @param string $column
+   *   The name of the column.
+   *
+   * @return string
+   *   A string containing a generated column name for a field data table that is
+   *   unique among all other fields.
+   */
+  static public function _fieldColumnName(FieldInterface $field, $column) {
+    return in_array($column, Field::getReservedColumns()) ? $column : $field->getFieldName() . '_' . $column;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
index 3457acb2a709a8e0001cb2e99b35032be2e6ebc0..f16d863138aaa5c4ec43d8df0dbfc34a9e5a4023 100644
--- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
@@ -303,4 +303,43 @@ abstract protected function purgeFieldItems(EntityInterface $entity, FieldInstan
    */
   public function onFieldPurge(FieldInterface $field) { }
 
+  /**
+   * Checks translation statuses and invoke the related hooks if needed.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *   The entity being saved.
+   */
+  protected function invokeTranslationHooks(ContentEntityInterface $entity) {
+    $translations = $entity->getTranslationLanguages(FALSE);
+    $original_translations = $entity->original->getTranslationLanguages(FALSE);
+    $all_translations = array_keys($translations + $original_translations);
+
+    // Notify modules of translation insertion/deletion.
+    foreach ($all_translations as $langcode) {
+      if (isset($translations[$langcode]) && !isset($original_translations[$langcode])) {
+        $this->invokeHook('translation_insert', $entity->getTranslation($langcode));
+      }
+      elseif (!isset($translations[$langcode]) && isset($original_translations[$langcode])) {
+        $this->invokeHook('translation_delete', $entity->getTranslation($langcode));
+      }
+    }
+  }
+
+  /**
+   * Invokes a method on the Field objects within an entity.
+   *
+   * @param string $method
+   *   The method name.
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *   The entity object.
+   */
+  protected function invokeFieldMethod($method, ContentEntityInterface $entity) {
+    foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
+      $translation = $entity->getTranslation($langcode);
+      foreach ($translation as $field) {
+        $field->$method();
+      }
+    }
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index 91cac049789b81d3686ce9bd90a550b2747df6de..55ea0538608931ebc8f6becfa79997cb549a052d 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -9,7 +9,7 @@
 
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\Plugin\DataType\EntityReference;
 use Drupal\Core\Entity\Query\QueryException;
 use Drupal\field\Entity\Field;
@@ -163,7 +163,7 @@ public function addField($field, $type, $langcode) {
           $column = 'value';
         }
         $table = $this->ensureFieldTable($index_prefix, $field, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
-        $sql_column = DatabaseStorageController::_fieldColumnName($field, $column);
+        $sql_column = FieldableDatabaseStorageController::_fieldColumnName($field, $column);
       }
       // This is an entity property (non-configurable field).
       else {
@@ -251,7 +251,7 @@ protected function ensureEntityTable($index_prefix, $property, $type, $langcode,
   protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field) {
     $field_name = $field->getFieldName();
     if (!isset($this->fieldTables[$index_prefix . $field_name])) {
-      $table = $this->sqlQuery->getMetaData('age') == EntityStorageControllerInterface::FIELD_LOAD_CURRENT ? DatabaseStorageController::_fieldTableName($field) : DatabaseStorageController::_fieldRevisionTableName($field);
+      $table = $this->sqlQuery->getMetaData('age') == EntityStorageControllerInterface::FIELD_LOAD_CURRENT ? FieldableDatabaseStorageController::_fieldTableName($field) : FieldableDatabaseStorageController::_fieldRevisionTableName($field);
       if ($field->getFieldCardinality() != 1) {
         $this->sqlQuery->addMetaData('simple_query', FALSE);
       }
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php b/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php
index 6db9b3f713021c3efd94081a282eabc9519ae5b2..17ea1e25b1b81845cb5a94952676935a0d5669db 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\aggregator;
 
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\aggregator\Entity\Feed;
 use Drupal\Core\Entity\EntityInterface;
 
@@ -17,7 +17,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
  * required special handling for feed entities.
  */
-class FeedStorageController extends DatabaseStorageControllerNG implements FeedStorageControllerInterface {
+class FeedStorageController extends FieldableDatabaseStorageController implements FeedStorageControllerInterface {
 
   /**
    * Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad().
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php b/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php
index e17c82bfbd5d2dedf391aa3d966360b395f58786..7e05027186b4016f4d9f681fb462277758360c5f 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php
@@ -10,7 +10,7 @@
 use Drupal\aggregator\Entity\Item;
 use Drupal\Core\Database\Query\PagerSelectExtender;
 use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 
 /**
  * Controller class for aggregators items.
@@ -18,7 +18,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
  * required special handling for feed item entities.
  */
-class ItemStorageController extends DatabaseStorageControllerNG implements ItemStorageControllerInterface {
+class ItemStorageController extends FieldableDatabaseStorageController implements ItemStorageControllerInterface {
 
   /**
    * Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad().
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 9c5eb21474adc5d70c0ebe712307452e839a5e2f..2078f5804e26742544338950a4c94d738857e632 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
@@ -7,16 +7,16 @@
 
 namespace Drupal\custom_block;
 
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 
 /**
  * Controller class for custom blocks.
  *
- * This extends the Drupal\Core\Entity\DatabaseStorageControllerNG class,
+ * This extends the Drupal\Core\Entity\DatabaseStorageController class,
  * adding required special handling for custom block entities.
  */
-class CustomBlockStorageController extends DatabaseStorageControllerNG {
+class CustomBlockStorageController extends FieldableDatabaseStorageController {
 
   /**
    * Overrides \Drupal\Core\Entity\DatabaseStorageController::attachLoad().
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
index e91d8a134288925edbaa20b4e3ad3db31abd40b8..2c8f100c8a026698b4656950227b300c2f634191 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
@@ -8,7 +8,7 @@
 namespace Drupal\comment;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityChangedInterface;
 
 /**
@@ -17,7 +17,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
  * required special handling for comment entities.
  */
-class CommentStorageController extends DatabaseStorageControllerNG implements CommentStorageControllerInterface {
+class CommentStorageController extends FieldableDatabaseStorageController implements CommentStorageControllerInterface {
 
   /**
    * The thread for which a lock was acquired.
diff --git a/core/modules/contact/lib/Drupal/contact/Entity/Message.php b/core/modules/contact/lib/Drupal/contact/Entity/Message.php
index 1d0f5cdaea3d35a3ba798ae3a31c7da633d27826..2ea1def53889a259be1d093902a15d080c99037a 100644
--- a/core/modules/contact/lib/Drupal/contact/Entity/Message.php
+++ b/core/modules/contact/lib/Drupal/contact/Entity/Message.php
@@ -18,7 +18,7 @@
  *   label = @Translation("Contact message"),
  *   module = "contact",
  *   controllers = {
- *     "storage" = "Drupal\Core\Entity\DatabaseStorageControllerNG",
+ *     "storage" = "Drupal\Core\Entity\FieldableDatabaseStorageController",
  *     "render" = "Drupal\contact\MessageRenderController",
  *     "form" = {
  *       "default" = "Drupal\contact\MessageFormController"
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php b/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
index 650a2167e73f5cdf487d09d120f46ded76189698..709449c66e105aa1a96c4b717e1bf018946a6148 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\contact\Tests\Views;
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\views\Tests\ViewTestBase;
 
 /**
@@ -62,7 +62,7 @@ protected function setUp() {
   public function testViewsData() {
     // Test that the field is not exposed to views, since contact_message
     // entities have no storage.
-    $table_name = DatabaseStorageController::_fieldTableName($this->field);
+    $table_name = FieldableDatabaseStorageController::_fieldTableName($this->field);
     $data = $this->container->get('views.views_data')->get($table_name);
     $this->assertFalse($data, 'The field is not exposed to Views.');
   }
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
index b43d9213940a1bc4f2e43febc1f1d06a0a24117b..8bd48768733cd903dbfd780bad60b1db45a7d718 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\content_translation\Tests;
 
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Language\Language;
 use Drupal\simpletest\WebTestBase;
 
@@ -204,7 +204,7 @@ protected function createEntity($values, $langcode, $bundle_name = NULL) {
       $entity_values[$info['entity_keys']['bundle']] = $bundle_name ?: $this->bundle;
     }
     $controller = $this->container->get('entity.manager')->getStorageController($this->entityType);
-    if (!($controller instanceof DatabaseStorageControllerNG)) {
+    if (!($controller instanceof FieldableDatabaseStorageController)) {
       foreach ($values as $property => $value) {
         if (is_array($value)) {
           $entity_values[$property] = array($langcode => $value);
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceItem.php
index 96dbdc3e487f90e04b1f925e3fa600b6fa41b882..91bea275ac7477012888e82bb88fffee546a15ee 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceItem.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceItem.php
@@ -69,7 +69,7 @@ public static function schema(FieldInterface $field) {
     // Create a foreign key to the target entity type base type.
     $entity_manager = \Drupal::service('entity.manager');
     $target_type = $field->getFieldSetting('target_type');
-    if (is_subclass_of($entity_manager->getControllerClass($target_type, 'storage'), 'Drupal\Core\Entity\DatabaseStorageController')) {
+    if (is_subclass_of($entity_manager->getControllerClass($target_type, 'storage'), 'Drupal\Core\Entity\FieldableDatabaseStorageController')) {
       $entity_info = $entity_manager->getDefinition($target_type);
       $base_table = $entity_info['base_table'];
       $schema['foreign keys'][$base_table] = array(
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
index b37030a6a82fc2596c4de97c32ba655c19a77b5f..69257b0f49df9508925dc5603327a6497a442ebe 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\entity_reference\Tests;
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\Field\FieldItemListInterface;
 use Drupal\Core\Entity\Field\FieldItemInterface;
 use Drupal\Core\Language\Language;
@@ -115,12 +115,12 @@ public function testEntityReferenceFieldSchema() {
     $foreign_key_column_name = 'target_id';
 
     // Grab the SQL schema and verify that the 'foreign keys' are present.
-    $schemas = DatabaseStorageController::_fieldSqlSchema($field);
-    $schema = $schemas[DatabaseStorageController::_fieldTableName($field)];
+    $schemas = FieldableDatabaseStorageController::_fieldSqlSchema($field);
+    $schema = $schemas[FieldableDatabaseStorageController::_fieldTableName($field)];
     $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema.');
 
     $foreign_key = reset($schema['foreign keys']);
-    $foreign_key_column = DatabaseStorageController::_fieldColumnName($field, $foreign_key_column_name);
+    $foreign_key_column = FieldableDatabaseStorageController::_fieldColumnName($field, $foreign_key_column_name);
     $this->assertEqual($foreign_key['table'], 'taxonomy_term_data', 'Foreign key table name preserved in the schema.');
     $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'tid', 'Foreign key column name preserved in the schema.');
 
@@ -130,8 +130,8 @@ public function testEntityReferenceFieldSchema() {
     entity_reference_create_instance('entity_test', 'entity_test', $field_name, 'Test vocabulary reference', 'taxonomy_vocabulary');
     $field = field_info_field('entity_test', $field_name);
 
-    $schemas = DatabaseStorageController::_fieldSqlSchema($field);
-    $schema = $schemas[DatabaseStorageController::_fieldTableName($field)];
+    $schemas = FieldableDatabaseStorageController::_fieldSqlSchema($field);
+    $schema = $schemas[FieldableDatabaseStorageController::_fieldTableName($field)];
     $this->assertFalse(isset($schema['foreign keys']), 'There is no foreign key in the schema.');
   }
 }
diff --git a/core/modules/field/field.install b/core/modules/field/field.install
index 36eb9269cf8b3547398b5e8a2913234ebaa0ad9e..884e4577e6fc1cb9bf98058235ff5584cf673ea7 100644
--- a/core/modules/field/field.install
+++ b/core/modules/field/field.install
@@ -5,7 +5,7 @@
  * Install, update, and uninstall functions for the Field module.
  */
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\field\Entity\Field;
 
 /**
@@ -48,7 +48,7 @@ function _update_8003_field_create_field(array &$field_config) {
 
   // Create storage for the field. This requires a field entity, but cannot use
   // the regular entity_create() function here.
-  $schema = DatabaseStorageController::_fieldSqlSchema(new Field($field_config), $field_config['schema']);
+  $schema = FieldableDatabaseStorageController::_fieldSqlSchema(new Field($field_config), $field_config['schema']);
   foreach ($schema as $name => $table) {
     db_create_table($name, $table);
   }
@@ -118,8 +118,8 @@ function _update_8006_field_write_data_sql($entity_type, $bundle, $entity_id, $r
   $field_config = \Drupal::config("field.field.$entity_type.$field_name");
   $field = new Field($field_config);
 
-  $table_name = DatabaseStorageController::_fieldTableName($field);
-  $revision_name = DatabaseStorageController::_fieldRevisionTableName($field);
+  $table_name = FieldableDatabaseStorageController::_fieldTableName($field);
+  $revision_name = FieldableDatabaseStorageController::_fieldRevisionTableName($field);
 
   db_delete($table_name)
     ->condition('entity_id', $entity_id)
@@ -140,7 +140,7 @@ function _update_8006_field_write_data_sql($entity_type, $bundle, $entity_id, $r
         'langcode' => $langcode,
       );
       foreach ($item as $column => $value) {
-        $record[DatabaseStorageController::_fieldColumnName($field_name, $column)] = $value;
+        $record[FieldableDatabaseStorageController::_fieldColumnName($field_name, $column)] = $value;
       }
 
       $records[] = $record;
@@ -367,8 +367,8 @@ function field_update_8003() {
       $field = new Field($config);
       // Additionally, rename the data tables for deleted fields.
       $tables = array(
-        "field_deleted_data_{$record['id']}" => 'old_' . DatabaseStorageController::_fieldTableName($field),
-        "field_deleted_revision_{$record['id']}" => 'old_' . DatabaseStorageController::_fieldRevisionTableName($field),
+        "field_deleted_data_{$record['id']}" => 'old_' . FieldableDatabaseStorageController::_fieldTableName($field),
+        "field_deleted_revision_{$record['id']}" => 'old_' . FieldableDatabaseStorageController::_fieldRevisionTableName($field),
       );
       foreach ($tables as $table_old => $table_new) {
         if (db_table_exists($table_old)) {
@@ -516,7 +516,7 @@ function field_update_8006(&$sandbox) {
   $tables = array(
     array(
       'old_table' => 'field_data_' . $field_config['name'],
-      'new_table' => DatabaseStorageController::_fieldTableName($field),
+      'new_table' => FieldableDatabaseStorageController::_fieldTableName($field),
       'primary_key' => array(
         'entity_id',
         'deleted',
@@ -526,7 +526,7 @@ function field_update_8006(&$sandbox) {
     ),
     array(
       'old_table' => 'field_revision_' . $field_config['name'],
-      'new_table' => DatabaseStorageController::_fieldRevisionTableName($field),
+      'new_table' => FieldableDatabaseStorageController::_fieldRevisionTableName($field),
       'primary_key' => array(
         'entity_id',
         'revision_id',
diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc
index 517da7405394e6c8801dab5efbecc6b3404de0f8..aecc48c45f7624249bb18b263a2dea4fae12008d 100644
--- a/core/modules/field/field.views.inc
+++ b/core/modules/field/field.views.inc
@@ -8,7 +8,7 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\field\FieldInterface;
 
@@ -70,7 +70,7 @@ function field_views_data_alter(&$data) {
 function _field_views_is_sql_entity_type(FieldInterface $field) {
   $entity_manager = \Drupal::entityManager();
   try {
-    if ($entity_manager->getStorageController($field->entity_type) instanceof DatabaseStorageController) {
+    if ($entity_manager->getStorageController($field->entity_type) instanceof FieldableDatabaseStorageController) {
       return TRUE;
     }
   }
@@ -148,13 +148,13 @@ function field_views_field_default_views_data(FieldInterface $field) {
   // Description of the field tables.
   $field_tables = array(
     EntityStorageControllerInterface::FIELD_LOAD_CURRENT => array(
-      'table' => DatabaseStorageController::_fieldTableName($field),
+      'table' => FieldableDatabaseStorageController::_fieldTableName($field),
       'alias' => "{$entity_type}__{$field_name}",
     ),
   );
   if ($supports_revisions) {
     $field_tables[EntityStorageControllerInterface::FIELD_LOAD_REVISION] = array(
-      'table' => DatabaseStorageController::_fieldRevisionTableName($field),
+      'table' => FieldableDatabaseStorageController::_fieldRevisionTableName($field),
       'alias' => "{$entity_type}_revision__{$field_name}",
     );
   }
@@ -185,7 +185,7 @@ function field_views_field_default_views_data(FieldInterface $field) {
   // Build the list of additional fields to add to queries.
   $add_fields = array('delta', 'langcode', 'bundle');
   foreach (array_keys($field_columns) as $column) {
-    $add_fields[] = DatabaseStorageController::_fieldColumnName($field, $column);
+    $add_fields[] = FieldableDatabaseStorageController::_fieldColumnName($field, $column);
   }
   // Determine the label to use for the field. We don't have a label available
   // at the field level, so we just go through all instances and take the one
@@ -309,10 +309,10 @@ function field_views_field_default_views_data(FieldInterface $field) {
       else {
         $group = t('@group (historical data)', array('@group' => $group_name));
       }
-      $column_real_name = DatabaseStorageController::_fieldColumnName($field, $column);
+      $column_real_name = FieldableDatabaseStorageController::_fieldColumnName($field, $column);
 
       // Load all the fields from the table by default.
-      $field_sql_schema = DatabaseStorageController::_fieldSqlSchema($field);
+      $field_sql_schema = FieldableDatabaseStorageController::_fieldSqlSchema($field);
       $additional_fields = array_keys($field_sql_schema[$table]['fields']);
 
       $data[$table_alias][$column_real_name] = array(
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index bc16c8391e64a7b6bae49ecceca4f10a0b5c28a7..97797333e3c20a1d60cc40c00bf9589ca41f9eca 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\field\Plugin\views\field;
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\Core\Language\Language;
@@ -303,7 +303,7 @@ public function clickSort($order) {
 
     $this->ensureMyTable();
     $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
-    $column = DatabaseStorageController::_fieldColumnName($field, $this->options['click_sort_column']);
+    $column = FieldableDatabaseStorageController::_fieldColumnName($field, $this->options['click_sort_column']);
     if (!isset($this->aliases[$column])) {
       // Column is not in query; add a sort on it (without adding the column).
       $this->aliases[$column] = $this->tableAlias . '.' . $column;
diff --git a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
index d015ffa79f935a1bc2ece35689fef8f72d407ea9..236763422f787c1c15d22bad8f7b8cb10ff58408 100644
--- a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\field\Tests;
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\field\FieldInterface;
 
@@ -186,9 +186,9 @@ function testDeleteFieldInstance() {
     $this->assertEqual($instance->bundle, $bundle, 'The deleted instance is for the correct bundle');
 
     // Check that the actual stored content did not change during delete.
-    $schema = DatabaseStorageController::_fieldSqlSchema($field);
-    $table = DatabaseStorageController::_fieldTableName($field);
-    $column = DatabaseStorageController::_fieldColumnName($field, 'value');
+    $schema = FieldableDatabaseStorageController::_fieldSqlSchema($field);
+    $table = FieldableDatabaseStorageController::_fieldTableName($field);
+    $column = FieldableDatabaseStorageController::_fieldColumnName($field, 'value');
     $result = db_select($table, 't')
       ->fields('t', array_keys($schema[$table]['fields']))
       ->execute();
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
index ad9963dce9bdcdfa99e71724221022d5ee5f7b16..3e6bbe668b96f9e4964b0b30bbe42e65a9754582 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
@@ -6,7 +6,7 @@
  */
 
 namespace Drupal\field\Tests\Views;
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 
 /**
  * Test the produced views_data.
@@ -62,8 +62,8 @@ function testViewsData() {
     // Check the table and the joins of the first field.
     // Attached to node only.
     $field = $this->fields[0];
-    $current_table = DatabaseStorageController::_fieldTableName($field);
-    $revision_table = DatabaseStorageController::_fieldRevisionTableName($field);
+    $current_table = FieldableDatabaseStorageController::_fieldTableName($field);
+    $revision_table = FieldableDatabaseStorageController::_fieldRevisionTableName($field);
     $data[$current_table] = $views_data->get($current_table);
     $data[$revision_table] = $views_data->get($revision_table);
 
diff --git a/core/modules/file/file.install b/core/modules/file/file.install
index 7fb57720a0a6271775f61300b4bd86e111f82852..c5ec889d916159342e83893db8bdc87ecb027467 100644
--- a/core/modules/file/file.install
+++ b/core/modules/file/file.install
@@ -5,7 +5,7 @@
  * Install, update and uninstall functions for File module.
  */
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\field\Entity\Field;
 
 /**
@@ -271,10 +271,10 @@ function file_update_8003() {
     if (in_array($field_config->get('type'), array('file', 'image'))) {
       $field = new Field($field_config->get());
 
-      if (db_table_exists(DatabaseStorageController::_fieldTableName($field))) {
+      if (db_table_exists(FieldableDatabaseStorageController::_fieldTableName($field))) {
         $tables = array(
-          DatabaseStorageController::_fieldTableName($field),
-          DatabaseStorageController::_fieldRevisionTableName($field),
+          FieldableDatabaseStorageController::_fieldTableName($field),
+          FieldableDatabaseStorageController::_fieldRevisionTableName($field),
         );
 
         foreach ($tables as $table_name) {
diff --git a/core/modules/file/file.views.inc b/core/modules/file/file.views.inc
index f42a7b07a2ced9fbff50ad4532ad74acba5bbb84..89053cf84c799b90fddef99ab0d6c27b445f1128 100644
--- a/core/modules/file/file.views.inc
+++ b/core/modules/file/file.views.inc
@@ -7,7 +7,7 @@
  * @ingroup views_module_handlers
  */
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\field\FieldInterface;
 
 /**
@@ -498,7 +498,7 @@ function file_field_views_data_views_data_alter(array &$data, FieldInterface $fi
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type,
-    'field table' => DatabaseStorageController::_fieldTableName($field),
+    'field table' => FieldableDatabaseStorageController::_fieldTableName($field),
     'field field' => $field_name . '_target_id',
     'base' => $entity_info['base_table'],
     'base field' => $entity_info['entity_keys']['id'],
diff --git a/core/modules/file/lib/Drupal/file/FileStorageController.php b/core/modules/file/lib/Drupal/file/FileStorageController.php
index 2f27551862a9d032f93b7d341a1ae9f81f6d0bdb..9dbe289492b3f6f2ccfc8c05480b66225d2ed831 100644
--- a/core/modules/file/lib/Drupal/file/FileStorageController.php
+++ b/core/modules/file/lib/Drupal/file/FileStorageController.php
@@ -7,12 +7,12 @@
 
 namespace Drupal\file;
 
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 
 /**
  * File storage controller for files.
  */
-class FileStorageController extends DatabaseStorageControllerNG implements FileStorageControllerInterface {
+class FileStorageController extends FieldableDatabaseStorageController implements FileStorageControllerInterface {
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/image/image.views.inc b/core/modules/image/image.views.inc
index 7ab6ac65d12b17a51b42fabf81fa9b3e7f1f17fe..f20ad329123c1980e2357b129b81152d98faa7f1 100644
--- a/core/modules/image/image.views.inc
+++ b/core/modules/image/image.views.inc
@@ -7,7 +7,7 @@
  * @ingroup views_module_handlers
  */
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\field\FieldInterface;
 
 /**
@@ -52,7 +52,7 @@ function image_field_views_data_views_data_alter(array &$data, FieldInterface $f
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type,
-    'field table' => DatabaseStorageController::_fieldTableName($field),
+    'field table' => FieldableDatabaseStorageController::_fieldTableName($field),
     'field field' => $field_name . '_target_id',
     'base' => $entity_info['base_table'],
     'base field' => $entity_info['entity_keys']['id'],
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
index e3ddabdf5d552b1c375d7bca8e662a829854267d..c7ef8312442aae6e8912319c70b5f11f5c56ca36 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
@@ -54,15 +54,13 @@ class MenuLinkStorageController extends DatabaseStorageController implements Men
    *   An array of entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   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 \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider
    *   The route provider service.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, RouteProviderInterface $route_provider) {
-    parent::__construct($entity_type, $entity_info, $database, $field_info, $uuid_service);
+  public function __construct($entity_type, array $entity_info, Connection $database, UuidInterface $uuid_service, RouteProviderInterface $route_provider) {
+    parent::__construct($entity_type, $entity_info, $database, $uuid_service);
 
     $this->routeProvider = $route_provider;
 
@@ -91,7 +89,6 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_type,
       $entity_info,
       $container->get('database'),
-      $container->get('field.info'),
       $container->get('uuid'),
       $container->get('router.route_provider')
     );
@@ -174,7 +171,6 @@ public function save(EntityInterface $entity) {
       // Unlike the save() method from DatabaseStorageController, we invoke the
       // 'presave' hook first because we want to allow modules to alter the
       // entity before all the logic from our preSave() method.
-      $this->invokeFieldMethod('preSave', $entity);
       $this->invokeHook('presave', $entity);
       $entity->preSave($this);
 
@@ -190,8 +186,6 @@ public function save(EntityInterface $entity) {
           if (!$entity->isNew()) {
             $this->resetCache(array($entity->{$this->idKey}));
             $entity->postSave($this, TRUE);
-            $this->invokeFieldMethod('update', $entity);
-            $this->saveFieldItems($entity, TRUE);
             $this->invokeHook('update', $entity);
           }
           else {
@@ -200,8 +194,6 @@ public function save(EntityInterface $entity) {
 
             $entity->enforceIsNew(FALSE);
             $entity->postSave($this, FALSE);
-            $this->invokeFieldMethod('insert', $entity);
-            $this->saveFieldItems($entity, FALSE);
             $this->invokeHook('insert', $entity);
           }
         }
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageControllerInterface.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageControllerInterface.php
index 3fb408f4739a6da394c26f9569f0ca4d249b5e99..bc9c6090f91cf590c9f5d382d424829a0880b0df 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageControllerInterface.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageControllerInterface.php
@@ -13,7 +13,7 @@
 /**
  * Defines a common interface for menu link entity controller classes.
  */
-interface MenuLinkStorageControllerInterface extends EntityStorageControllerInterface {
+interface MenuLinkStorageControllerInterface {
 
   /**
    * Sets an internal flag that allows us to prevent the reparenting operations
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 9a8b8623aedea97fccbd2cc0c9a6d9e4cbdc2b34..0c3a623d297609856fb6b02d03e36e08922b80ab 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\node;
 
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
@@ -16,7 +16,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
  * required special handling for node entities.
  */
-class NodeStorageController extends DatabaseStorageControllerNG {
+class NodeStorageController extends FieldableDatabaseStorageController {
 
   /**
    * Overrides Drupal\Core\Entity\DatabaseStorageController::create().
@@ -30,7 +30,7 @@ public function create(array $values) {
   }
 
   /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageControllerNG::attachLoad().
+   * Overrides Drupal\Core\Entity\DatabaseStorageController::attachLoad().
    */
   protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
     $queried_entities = $this->mapFromStorageRecords($queried_entities, $load_revision);
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
index b2391996aeb56f5f7873e14b82ac454d9a0d3629..41cab33c8cceaee57fb0ee96c9dfa64870bb5f38 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
@@ -91,6 +91,16 @@ protected function assertCRUD($entity_type, UserInterface $user1) {
 
     $all = entity_load_multiple($entity_type);
     $this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type)));
+
+    // Verify that all data got deleted.
+    $definition = \Drupal::entityManager()->getDefinition($entity_type);
+    $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition['base_table'] . '}')->fetchField(), 'Base table was emptied');
+    if (isset($definition['data_table'])) {
+      $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition['data_table'] . '}')->fetchField(), 'Data table was emptied');
+    }
+    if (isset($definition['revision_table'])) {
+      $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition['revision_table'] . '}')->fetchField(), 'Data table was emptied');
+    }
   }
 
    /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
index 956e995bdeab5acd2c4bb3d42baac3e1609b108f..45d997f133884d0f96bac723098bf25613292095 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
@@ -8,7 +8,7 @@
 namespace Drupal\system\Tests\Entity;
 
 use Drupal\Core\Database\Database;
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\field\FieldException;
 use Drupal\field\Entity\Field;
 use Drupal\system\Tests\Entity\EntityUnitTestBase;
@@ -85,8 +85,8 @@ function setUp() {
     ));
     $this->instance->save();
 
-    $this->table = DatabaseStorageController::_fieldTableName($this->field);
-    $this->revision_table = DatabaseStorageController::_fieldRevisionTableName($this->field);
+    $this->table = FieldableDatabaseStorageController::_fieldTableName($this->field);
+    $this->revision_table = FieldableDatabaseStorageController::_fieldRevisionTableName($this->field);
   }
 
   /**
@@ -96,7 +96,7 @@ function testFieldLoad() {
     $entity_type = $bundle = 'entity_test_rev';
     $storage_controller = $this->container->get('entity.manager')->getStorageController($entity_type);
 
-    $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', DatabaseStorageController::_fieldColumnName($this->field, 'value'));
+    $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', FieldableDatabaseStorageController::_fieldColumnName($this->field, 'value'));
 
     // Create an entity with four revisions.
     $revision_ids = array();
@@ -353,7 +353,7 @@ function testFieldUpdateFailure() {
     }
 
     // Ensure that the field tables are still there.
-    foreach (DatabaseStorageController::_fieldSqlSchema($prior_field) as $table_name => $table_info) {
+    foreach (FieldableDatabaseStorageController::_fieldSqlSchema($prior_field) as $table_name => $table_info) {
       $this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name)));
     }
   }
@@ -377,7 +377,7 @@ function testFieldUpdateIndexesWithData() {
       'bundle' => $entity_type,
     ));
     $instance->save();
-    $tables = array(DatabaseStorageController::_fieldTableName($field), DatabaseStorageController::_fieldRevisionTableName($field));
+    $tables = array(FieldableDatabaseStorageController::_fieldTableName($field), FieldableDatabaseStorageController::_fieldRevisionTableName($field));
 
     // Verify the indexes we will create do not exist yet.
     foreach ($tables as $table) {
@@ -450,11 +450,11 @@ function testFieldSqlStorageForeignKeys() {
     $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update');
 
     // Verify the SQL schema.
-    $schemas = DatabaseStorageController::_fieldSqlSchema($field);
-    $schema = $schemas[DatabaseStorageController::_fieldTableName($field)];
+    $schemas = FieldableDatabaseStorageController::_fieldSqlSchema($field);
+    $schema = $schemas[FieldableDatabaseStorageController::_fieldTableName($field)];
     $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema');
     $foreign_key = reset($schema['foreign keys']);
-    $foreign_key_column = DatabaseStorageController::_fieldColumnName($field, $foreign_key_name);
+    $foreign_key_column = FieldableDatabaseStorageController::_fieldColumnName($field, $foreign_key_name);
     $this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema');
     $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema');
   }
@@ -504,9 +504,9 @@ public function testTableNames() {
       'type' => 'test_field',
     ));
     $expected = 'short_entity_type__short_field_name';
-    $this->assertEqual(DatabaseStorageController::_fieldTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldTableName($field), $expected);
     $expected = 'short_entity_type_revision__short_field_name';
-    $this->assertEqual(DatabaseStorageController::_fieldRevisionTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldRevisionTableName($field), $expected);
 
     // Short entity type, long field name
     $entity_type = 'short_entity_type';
@@ -517,9 +517,9 @@ public function testTableNames() {
       'type' => 'test_field',
     ));
     $expected = 'short_entity_type__' . substr(hash('sha256', $field->uuid), 0, 10);
-    $this->assertEqual(DatabaseStorageController::_fieldTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldTableName($field), $expected);
     $expected = 'short_entity_type_r__' . substr(hash('sha256', $field->uuid), 0, 10);
-    $this->assertEqual(DatabaseStorageController::_fieldRevisionTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldRevisionTableName($field), $expected);
 
     // Long entity type, short field name
     $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz';
@@ -530,9 +530,9 @@ public function testTableNames() {
       'type' => 'test_field',
     ));
     $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field->uuid), 0, 10);
-    $this->assertEqual(DatabaseStorageController::_fieldTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldTableName($field), $expected);
     $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field->uuid), 0, 10);
-    $this->assertEqual(DatabaseStorageController::_fieldRevisionTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldRevisionTableName($field), $expected);
 
     // Long entity type and field name.
     $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz';
@@ -543,17 +543,17 @@ public function testTableNames() {
       'type' => 'test_field',
     ));
     $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field->uuid), 0, 10);
-    $this->assertEqual(DatabaseStorageController::_fieldTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldTableName($field), $expected);
     $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field->uuid), 0, 10);
-    $this->assertEqual(DatabaseStorageController::_fieldRevisionTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldRevisionTableName($field), $expected);
     // Try creating a second field and check there are no clashes.
     $field2 = entity_create('field_entity', array(
       'entity_type' => $entity_type,
       'name' => $field_name . '2',
       'type' => 'test_field',
     ));
-    $this->assertNotEqual(DatabaseStorageController::_fieldTableName($field), DatabaseStorageController::_fieldTableName($field2));
-    $this->assertNotEqual(DatabaseStorageController::_fieldRevisionTableName($field), DatabaseStorageController::_fieldRevisionTableName($field2));
+    $this->assertNotEqual(FieldableDatabaseStorageController::_fieldTableName($field), FieldableDatabaseStorageController::_fieldTableName($field2));
+    $this->assertNotEqual(FieldableDatabaseStorageController::_fieldRevisionTableName($field), FieldableDatabaseStorageController::_fieldRevisionTableName($field2));
 
     // Deleted field.
     $field = entity_create('field_entity', array(
@@ -563,9 +563,9 @@ public function testTableNames() {
       'deleted' => TRUE,
     ));
     $expected = 'field_deleted_data_' . substr(hash('sha256', $field->uuid), 0, 10);
-    $this->assertEqual(DatabaseStorageController::_fieldTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldTableName($field), $expected);
     $expected = 'field_deleted_revision_' . substr(hash('sha256', $field->uuid), 0, 10);
-    $this->assertEqual(DatabaseStorageController::_fieldRevisionTableName($field), $expected);
+    $this->assertEqual(FieldableDatabaseStorageController::_fieldRevisionTableName($field), $expected);
   }
 
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
index 5e9c2557143a1f639f30c009b0b279d7df486ef1..76c3b06fdd06ea9802fb5b7ae298fc3ed0bb095a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\system\Tests\Upgrade;
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\field\Entity\Field;
 
 /**
@@ -278,7 +278,7 @@ function testFieldUpgradeToConfig() {
 
     // Check that pre-existing deleted field table is renamed correctly.
     $field_entity = new Field($deleted_field);
-    $table_name = DatabaseStorageController::_fieldTableName($field_entity);
+    $table_name = FieldableDatabaseStorageController::_fieldTableName($field_entity);
     $this->assertEqual("field_deleted_data_" . substr(hash('sha256', $deleted_field['uuid']), 0, 10), $table_name);
     $this->assertTrue(db_table_exists($table_name));
 
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
index bb8d3902b9e51eae076dce2380cc0687464b9650..a348fc664d00180f1545771f5fd5730bad367aa9 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\entity_test;
 
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 
 /**
  * Defines the controller class for the test entity.
@@ -15,7 +15,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
  * required special handling for test entities.
  */
-class EntityTestStorageController extends DatabaseStorageControllerNG {
+class EntityTestStorageController extends FieldableDatabaseStorageController {
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php
index f77b1b5c45ab795228d4682b47effd2072c9d4fc..1a473346c41c7b8ca41b095eb00d0b08cc10aff4 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php
@@ -9,12 +9,12 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\Query\QueryInterface;
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 
 /**
  * Defines a Controller class for taxonomy terms.
  */
-class TermStorageController extends DatabaseStorageControllerNG implements TermStorageControllerInterface {
+class TermStorageController extends FieldableDatabaseStorageController implements TermStorageControllerInterface {
 
   /**
    * Overrides Drupal\Core\Entity\DatabaseStorageController::create().
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
index 34899c37e6567c7029cd3ccd94cfc14ed39a136f..845a44e0c2143f18daae801e358ab6859a207b52 100644
--- a/core/modules/taxonomy/taxonomy.install
+++ b/core/modules/taxonomy/taxonomy.install
@@ -5,7 +5,7 @@
  * Install, update and uninstall functions for the taxonomy module.
  */
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\field\Entity\Field;
 
 /**
@@ -353,10 +353,10 @@ function taxonomy_update_8007() {
     if ($field_config->get('type') == 'taxonomy_term_reference') {
       $field = new Field($field_config->get());
 
-      if (db_table_exists(DatabaseStorageController::_fieldTableName($field))) {
+      if (db_table_exists(FieldableDatabaseStorageController::_fieldTableName($field))) {
         $tables = array(
-          DatabaseStorageController::_fieldTableName($field),
-          DatabaseStorageController::_fieldRevisionTableName($field),
+          FieldableDatabaseStorageController::_fieldTableName($field),
+          FieldableDatabaseStorageController::_fieldRevisionTableName($field),
         );
 
         foreach ($tables as $table_name) {
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 4a17f3dbcda7c7cdf896566c1ecdff6b47294ff3..2e67b30c9d5a4d00fdec1311ab81b8a6908d8844 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -5,7 +5,7 @@
  * Enables the organization of content into categories.
  */
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\Field\FieldDefinitionInterface;
 use Drupal\field\FieldInterface;
@@ -1124,7 +1124,7 @@ function taxonomy_node_insert(EntityInterface $node) {
 function taxonomy_build_node_index($node) {
   // We maintain a denormalized table of term/node relationships, containing
   // only data for current, published nodes.
-  if (!\Drupal::config('taxonomy.settings')->get('maintain_index_table') || !(\Drupal::entityManager()->getStorageController('node') instanceof DatabaseStorageController)) {
+  if (!\Drupal::config('taxonomy.settings')->get('maintain_index_table') || !(\Drupal::entityManager()->getStorageController('node') instanceof FieldableDatabaseStorageController)) {
     return;
   }
 
diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc
index 15a978a2af98b7a861f4e8e477261d0d108f9303..e33f2659c188e2360b720fc7a9139be5d9c507da 100644
--- a/core/modules/taxonomy/taxonomy.views.inc
+++ b/core/modules/taxonomy/taxonomy.views.inc
@@ -7,7 +7,7 @@
  * @ingroup views_module_handlers
  */
 
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\field\FieldInterface;
 
 /**
@@ -450,7 +450,7 @@ function taxonomy_field_views_data_views_data_alter(array &$data, FieldInterface
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type,
-    'field table' => DatabaseStorageController::_fieldTableName($field),
+    'field table' => FieldableDatabaseStorageController::_fieldTableName($field),
     'field field' => $field_name . '_target_id',
     'base' => $entity_info['base_table'],
     'base field' => $entity_info['entity_keys']['id'],
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index 03d32b1b676b53e30412d3de09988e2558114f2c..48e8206b9bd4f8fe79f90d9eb22a2055bdd81d42 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -14,7 +14,7 @@
 use Drupal\field\FieldInfo;
 use Drupal\user\UserDataInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Core\Entity\FieldableDatabaseStorageController;
 
 /**
  * Controller class for users.
@@ -22,7 +22,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
  * required special handling for user objects.
  */
-class UserStorageController extends DatabaseStorageControllerNG implements UserStorageControllerInterface {
+class UserStorageController extends FieldableDatabaseStorageController implements UserStorageControllerInterface {
 
   /**
    * Provides the password hashing service object.
diff --git a/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php b/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php
index edb6737a4cc5bb9800a182bb1f87ac9a83b14ad3..7c09bbf85eac65b5ce3a6a901effb282011ebdc0 100644
--- a/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php
@@ -29,7 +29,7 @@ class QueryGroupByTest extends ViewUnitTestBase {
   /**
    * The storage controller for the test entity type.
    *
-   * @var \Drupal\Core\Entity\DatabaseStorageController
+   * @var \Drupal\Core\Entity\FieldableDatabaseStorageController
    */
   public $storageController;