diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
index 6b7de03..409191a 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
@@ -8,7 +8,11 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\Database;
 use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Core\Entity\Schema\ContentEntitySchemaHandler;
+use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Language\Language;
 use Drupal\field\FieldInfo;
 use Drupal\field\FieldConfigUpdateForbiddenException;
@@ -25,7 +29,58 @@
  * This class can be used as-is by most simple entity types. Entity types
  * requiring special handling can extend the class.
  */
-class ContentEntityDatabaseStorage extends ContentEntityStorageBase {
+class ContentEntityDatabaseStorage extends ContentEntityStorageBase implements ContentEntitySchemaHandlerInterface, SqlStorageInterface {
+
+  /**
+   * The base table layout: no revision or multilingual support for base fields.
+   *
+   * @var int
+   */
+  const LAYOUT_BASE = 0;
+
+  /**
+   * The revision table layout: provides revision support for base fields.
+   *
+   * @var int
+   */
+  const LAYOUT_REVISION = 1;
+
+  /**
+   * The multilingual table layout: provides multilingual support for base
+   * fields.
+   *
+   * @var int
+   */
+  const LAYOUT_MULTILINGUAL = 2;
+
+  /**
+   * The multilingual revision table layout: provides revision and multilingual
+   * support for base fields.
+   *
+   * @var int
+   */
+  const LAYOUT_MULTILINGUAL_REVISION = 3;
+
+  /**
+   * The base field definitions for this entity type.
+   *
+   * @var \Drupal\Core\Field\FieldDefinitionInterface[]
+   */
+  protected $fieldDefinitions;
+
+  /**
+   * The table layout associated to the entity type.
+   *
+   * @var int
+   */
+  protected $layoutType;
+
+  /**
+   * A mapping of schema fields that will be stored per entity table.
+   *
+   * @var array
+   */
+  protected $tableMapping;
 
   /**
    * Name of entity's revision database table field, if it supports revisions.
@@ -37,6 +92,22 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase {
   protected $revisionKey = FALSE;
 
   /**
+   * Name of entity's default_langcode database table field.
+   *
+   * Has the value FALSE if this entity does not support multilingual storage.
+   *
+   * @var string|bool
+   */
+  protected $defaultLangcodeKey = FALSE;
+
+  /**
+   * The base table of the entity, if the entity has storage.
+   *
+   * @var string
+   */
+  protected $baseTable;
+
+  /**
    * The table that stores revisions, if the entity supports revisions.
    *
    * @var string
@@ -79,13 +150,28 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase {
   protected $fieldInfo;
 
   /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The entity schema builder.
+   *
+   * @var \Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface
+   */
+  protected $schemaHandler;
+
+  /**
    * {@inheritdoc}
    */
   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
     return new static(
       $entity_type,
       $container->get('database'),
-      $container->get('field.info')
+      $container->get('field.info'),
+      $container->get('entity.manager')
     );
   }
 
@@ -98,36 +184,246 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
    *   The database connection to be used.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info) {
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info, EntityManagerInterface $entity_manager) {
     parent::__construct($entity_type);
 
     $this->database = $database;
     $this->fieldInfo = $field_info;
+    $this->entityManager = $entity_manager;
+    $this->fieldDefinitions = $entity_manager->getBaseFieldDefinitions($entity_type->id());
+
+    $this->initTableLayout();
+  }
+
+  /**
+   * Initializes table and key names based on the current layout type.
+   */
+  protected function initTableLayout() {
+    $this->idKey = $this->entityType->getKey('id') ?: 'id';
+    $this->uuidKey = $this->entityType->getKey('uuid') ?: 'uuid';
+
+    // The bundle key is optional.
+    $this->bundleKey = $this->entityType->getKey('bundle');
+
+    // @todo Table names do not belong to the entity type definition, they are
+    //   storage implementation details. Rip them out.
+    $this->baseTable = $this->entityType->getBaseTable() ?: $this->entityTypeId;
+
+    // Retrieve the current table layout type based on the entity type
+    // definition.
+    $layout_type = $this->getLayoutType();
+
+    if ($layout_type & static::LAYOUT_REVISION) {
+      $this->revisionKey = $this->entityType->getKey('revision') ?: 'revision_id';
+      $this->revisionTable = $this->entityType->getRevisionTable() ?: $this->entityTypeId . '_revision';
+    }
+
+    if ($layout_type & static::LAYOUT_MULTILINGUAL) {
+      $this->dataTable = $this->entityType->getDataTable() ?: $this->entityTypeId . '_field_data';
+      $this->langcodeKey = $this->entityType->getKey('langcode') ?: 'langcode';
+      $this->defaultLangcodeKey = $this->entityType->getKey('default_langcode') ?: 'default_langcode';
+    }
+
+    if ($layout_type == static::LAYOUT_MULTILINGUAL_REVISION) {
+      $this->revisionDataTable = $this->entityType->getRevisionDataTable() ?: $this->entityTypeId . '_field_revision';
+    }
+  }
+
+  /**
+   * Returns the current layout type.
+   *
+   * @return int
+   *   An integer value identifiying the current layour type.
+   *
+   * @see static::LAYOUT_BASE
+   * @see static::LAYOUT_REVISION
+   * @see static::LAYOUT_MULTILINGUAL
+   * @see static::LAYOUT_MULTILINGUAL_REVISION
+   */
+  protected function getLayoutType() {
+    if (!isset($this->layoutType)) {
+      $this->layoutType = static::LAYOUT_BASE;
+      if ($this->entityType->hasKey('revision')) {
+        $this->layoutType |= static::LAYOUT_REVISION;
+      }
+      // @todo Remove the data table check once all entity types are using entity
+      //   query. See https://drupal.org/node/2068325.
+      $data_table = $this->entityType->getDataTable();
+      if ($data_table && $this->entityType->isTranslatable()) {
+        $this->layoutType |= static::LAYOUT_MULTILINGUAL;
+      }
+    }
+    return $this->layoutType;
+  }
+
+  /**
+   * Returns the base table name.
+   *
+   * @return string
+   *   The table name.
+   */
+  public function getBaseTable() {
+    return $this->baseTable;
+  }
+
+  /**
+   * Returns the revision table name.
+   *
+   * @return string|bool
+   *   The table name or FALSE if it is not available.
+   */
+  public function getRevisionTable() {
+    return $this->revisionTable;
+  }
 
-    // Check if the entity type supports IDs.
-    if ($this->entityType->hasKey('id')) {
-      $this->idKey = $this->entityType->getKey('id');
+  /**
+   * Returns the data table name.
+   *
+   * @return string|bool
+   *   The table name or FALSE if it is not available.
+   */
+  public function getDataTable() {
+    return $this->dataTable;
+  }
+
+  /**
+   * Returns the revision data table name.
+   *
+   * @return string|bool
+   *   The table name or FALSE if it is not available.
+   */
+  public function getRevisionDataTable() {
+    return $this->revisionDataTable;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSchema() {
+    return $this->schemaHandler()->getSchema();
+  }
+
+  /**
+   * Gets the schema builder for this storage controller.
+   */
+  protected function schemaHandler() {
+    if (!isset($this->schemaHandler)) {
+      $this->schemaHandler = new ContentEntitySchemaHandler($this->entityManager, $this->entityType, $this);
     }
+    return $this->schemaHandler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTableMapping() {
+    if (!isset($this->tableMapping)) {
+      $table_mapping = array();
+
+      // @todo Remove this. This is currently needed because some entity types
+      //   declare a schema that does not match their field definitions.
+      //   Therefore we use the legacy schema if it's still declared. This
+      //   allows to iteratively enable the automatic schema generation for all
+      //   entity types.
+      if (drupal_get_schema($this->baseTable)) {
+        $tables = array_filter(array($this->baseTable, $this->revisionTable, $this->dataTable, $this->revisionDataTable));
+        foreach ($tables as $table) {
+          foreach (array_keys(drupal_get_schema($table)['fields']) as $field_name) {
+            $table_mapping[$table][$field_name] = array($field_name);
+          }
+        }
+      }
+      else {
+        $key_fields = array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey));
+
+        $storable_definitions = array_filter($this->fieldDefinitions, function (FieldDefinitionInterface $field_definition) {
+          return !$field_definition->isComputed() && !$field_definition->hasCustomStorage();
+        });
+        $storable_fields = array_keys($storable_definitions);
+
+        // @todo Provide automatic definitions for revision metadata fields.
+        //   Rename 'log' to 'revision_log'.
+        $revision_metadata_fields = array_intersect(array('revision_timestamp', 'revision_uid', 'log'), $storable_fields);
+        $revisionable_filter_callback = function (FieldDefinitionInterface $definition) { return $definition->isRevisionable(); };
+
+        switch ($this->getLayoutType()) {
+          // The base layout stores all the base field values in the base table.
+          case static::LAYOUT_BASE:
+            $table_mapping[$this->baseTable] = $this->processFields(array_merge($key_fields, array_diff($storable_fields, $key_fields)));
+            break;
+
+          // The base layout stores all the base field values in the base table.
+          // Revisionable fields are also stored in the revision table.
+          case static::LAYOUT_REVISION:
+            $table_mapping[$this->baseTable] = $this->processFields(array_merge($key_fields, array_diff($storable_fields, $key_fields, $revision_metadata_fields)));
+            $revision_key_fields = array($this->idKey, $this->revisionKey);
+            $revisionable_fields = array_keys(array_filter($storable_definitions, $revisionable_filter_callback));
+            $table_mapping[$this->revisionTable] = $this->processFields(array_merge($revision_key_fields, $revisionable_fields));
+            break;
 
-    // Check if the entity type supports UUIDs.
-    $this->uuidKey = $this->entityType->getKey('uuid');
+          // Multilingual layouts store key field values in the base table. The
+          // other base field values are stored in the data table, no matter
+          // whether they are translatable or not. The data table holds also a
+          // denormalized copy of the bundle field value to allow for more
+          // performant queries.
+          case static::LAYOUT_MULTILINGUAL:
+            $table_mapping[$this->baseTable] = $this->processFields($key_fields);
+            $data_key_fields = array_diff($key_fields, array($this->uuidKey));
+            $data_fields = array_diff($storable_fields, $key_fields);
+            $table_mapping[$this->dataTable] = $this->processFields(array_merge($data_key_fields, $data_fields));
+            break;
+
+          // The revisionable multilingual layout stores key field values in the
+          // base table, except for language, which is stored in the revision
+          // table along with revision metadata. The revision data table holds
+          // data field values for all the available revisions without
+          // denormalizations.
+          case static::LAYOUT_MULTILINGUAL_REVISION:
+            $table_mapping[$this->baseTable] = $this->processFields(array_diff($key_fields, array($this->langcodeKey)));
+            $data_key_fields = array_diff($key_fields, array($this->uuidKey));
+            $data_fields = array_diff($storable_fields, $key_fields, $revision_metadata_fields);
+            $table_mapping[$this->dataTable] = $this->processFields(array_merge($data_key_fields, $data_fields));
+            $table_mapping[$this->revisionTable] = $this->processFields(array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields));
+            $revision_data_key_fields = array_diff($key_fields, array($this->bundleKey, $this->uuidKey));
+            $revisionable_fields = array_keys(array_filter($storable_definitions, $revisionable_filter_callback));
+            $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields, $revision_data_key_fields);
+            $table_mapping[$this->revisionDataTable] = $this->processFields(array_merge($revision_data_key_fields, $revision_data_fields));
+            break;
+        }
+      }
 
-    // Check if the entity type supports revisions.
-    if ($this->entityType->hasKey('revision')) {
-      $this->revisionKey = $this->entityType->getKey('revision');
-      $this->revisionTable = $this->entityType->getRevisionTable();
+      $this->tableMapping = $table_mapping;
     }
 
-    // Check if the entity type has a dedicated table for fields.
-    if ($data_table = $this->entityType->getDataTable()) {
-      $this->dataTable = $data_table;
-      // Entity types having both revision and translation support should always
-      // define a revision data table.
-      if ($this->revisionTable && $revision_data_table = $this->entityType->getRevisionDataTable()) {
-        $this->revisionDataTable = $revision_data_table;
+    return $this->tableMapping;
+  }
+
+  /**
+   * Returns a mapping between field and column names.
+   *
+   * @param array $field_names
+   *   An array of names of fields to map.
+   *
+   * @return array
+   *   An associative array of arrays of column names keyed by field name.
+   */
+  protected function processFields($field_names) {
+    $mapping = array();
+    foreach ($field_names as $field_name) {
+      $columns = isset($this->fieldDefinitions[$field_name]) ? array_keys($this->fieldDefinitions[$field_name]->getColumns()) : array();
+      if (count($columns) > 1) {
+        foreach ($columns as $column) {
+          $mapping[$field_name][] = $field_name . '__' . $column;
+        }
+      }
+      else {
+        $mapping[$field_name] = array($field_name);
       }
     }
+    return $mapping;
   }
 
   /**
@@ -265,15 +561,18 @@ protected function attachPropertyData(array &$entities) {
       }
 
       $data = $query->execute();
-      $field_definitions = \Drupal::entityManager()->getBaseFieldDefinitions($this->entityTypeId);
+      $field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId);
+
+      $table_mapping = $this->getTableMapping();
       $translations = array();
       if ($this->revisionDataTable) {
-        $data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityType->getRevisionDataTable()), drupal_schema_fields_sql($this->entityType->getBaseTable())));
+        $data_column_names = call_user_func_array('array_merge', array_diff_key($table_mapping[$this->revisionDataTable], $table_mapping[$this->baseTable]));
       }
       else {
-        $data_column_names = array_flip(drupal_schema_fields_sql($this->entityType->getDataTable()));
+        $data_column_names = call_user_func_array('array_merge', $table_mapping[$this->dataTable]);
       }
 
+      $data_column_names = array_combine($data_column_names, $data_column_names);
       foreach ($data as $values) {
         $id = $values[$this->idKey];
 
@@ -403,11 +702,12 @@ protected function buildQuery($ids, $revision_id = FALSE) {
     }
 
     // Add fields from the {entity} table.
-    $entity_fields = drupal_schema_fields_sql($this->entityType->getBaseTable());
+    $table_mapping = $this->getTableMapping();
+    $entity_fields = call_user_func_array('array_merge', $table_mapping[$this->baseTable]);
 
     if ($this->revisionTable) {
       // Add all fields from the {entity_revision} table.
-      $entity_revision_fields = drupal_schema_fields_sql($this->entityType->getRevisionTable());
+      $entity_revision_fields = call_user_func_array('array_merge', $table_mapping[$this->revisionTable]);
       $entity_revision_fields = array_combine($entity_revision_fields, $entity_revision_fields);
       // The ID field is provided by entity, so remove it.
       unset($entity_revision_fields[$this->idKey]);
@@ -552,7 +852,12 @@ public function save(EntityInterface $entity) {
 
       if (!$entity->isNew()) {
         if ($entity->isDefaultRevision()) {
-          $return = drupal_write_record($this->entityType->getBaseTable(), $record, $this->idKey);
+          $this->database
+            ->update($this->baseTable)
+            ->fields((array) $record)
+            ->condition($this->idKey, $record->{$this->idKey})
+            ->execute();
+          $return = SAVED_UPDATED;
         }
         else {
           // @todo, should a different value be returned when saving an entity
@@ -560,13 +865,13 @@ public function save(EntityInterface $entity) {
           $return = FALSE;
         }
         if ($this->revisionTable) {
-          $record->{$this->revisionKey} = $this->saveRevision($entity);
+          $entity->{$this->revisionKey}->value = $this->saveRevision($entity);
         }
         if ($this->dataTable) {
           $this->savePropertyData($entity);
         }
         if ($this->revisionDataTable) {
-          $this->savePropertyData($entity, 'revision_data_table');
+          $this->savePropertyData($entity, $this->revisionDataTable);
         }
         $entity->setNewRevision(FALSE);
         $this->invokeFieldMethod('update', $entity);
@@ -582,7 +887,17 @@ public function save(EntityInterface $entity) {
         // Ensure the entity is still seen as new after assigning it an id,
         // while storing its data.
         $entity->enforceIsNew();
-        $return = drupal_write_record($this->entityType->getBaseTable(), $record);
+        $insert_id = $this->database
+          ->insert($this->baseTable, array('return' => Database::RETURN_INSERT_ID))
+          ->fields((array) $record)
+          ->execute();
+        // Even if this is a new entity, the ID key might have been set in which
+        // case we should not override the provided ID. An empty value for the
+        // id is interpreted as NULL and thus overriden.
+        if (empty($record->{$this->idKey})) {
+          $record->{$this->idKey} = $insert_id;
+        }
+        $return = SAVED_NEW;
         $entity->{$this->idKey}->value = (string) $record->{$this->idKey};
         if ($this->revisionTable) {
           $entity->setNewRevision();
@@ -592,10 +907,9 @@ public function save(EntityInterface $entity) {
           $this->savePropertyData($entity);
         }
         if ($this->revisionDataTable) {
-          $this->savePropertyData($entity, 'revision_data_table');
+          $this->savePropertyData($entity, $this->revisionDataTable);
         }
 
-
         $entity->enforceIsNew(FALSE);
         $this->invokeFieldMethod('insert', $entity);
         $this->saveFieldItems($entity, FALSE);
@@ -623,13 +937,14 @@ public function save(EntityInterface $entity) {
    *
    * @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'.
+   * @param string $table_name
+   *   (optional) The table name to save to. Defaults to the data table.
    */
-  protected function savePropertyData(EntityInterface $entity, $table_key = 'data_table') {
-    $table_name = $this->entityType->get($table_key);
-    $revision = $table_key != 'data_table';
+  protected function savePropertyData(EntityInterface $entity, $table_name = NULL) {
+    if (!isset($table_name)) {
+      $table_name = $this->dataTable;
+    }
+    $revision = $table_name != $this->dataTable;
 
     if (!$revision || !$entity->isNewRevision()) {
       $key = $revision ? $this->revisionKey : $this->idKey;
@@ -644,7 +959,7 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_
 
     foreach ($entity->getTranslationLanguages() as $langcode => $language) {
       $translation = $entity->getTranslation($langcode);
-      $record = $this->mapToDataStorageRecord($translation, $table_key);
+      $record = $this->mapToDataStorageRecord($translation, $table_name);
       $values = (array) $record;
       $query
         ->fields(array_keys($values))
@@ -657,53 +972,52 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_
   /**
    * Maps from an entity object to the storage record.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
    *   The entity object.
-   * @param string $table_key
-   *   (optional) The entity key identifying the target table. Defaults to
-   *   'base_table'.
+   * @param string $table_name
+   *   (optional) The table name to map records to. Defaults to the base table.
    *
    * @return \stdClass
    *   The record to store.
    */
-  protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'base_table') {
+  protected function mapToStorageRecord(ContentEntityInterface $entity, $table_name = NULL) {
+    if (!isset($table_name)) {
+      $table_name = $this->baseTable;
+    }
+
     $record = new \stdClass();
-    $values = array();
-    $definitions = $entity->getFieldDefinitions();
-    $schema = drupal_get_schema($this->entityType->get($table_key));
     $is_new = $entity->isNew();
+    $table_mapping = $this->getTableMapping();
+
+    foreach ($table_mapping[$table_name] as $name => $storage_columns) {
+      if (!empty($this->fieldDefinitions[$name])) {
+        $definition = $this->fieldDefinitions[$name];
+        foreach ($definition->getColumns() as $column => $column_info) {
+          $value = isset($entity->$name->$column) ? $entity->$name->$column : NULL;
+
+          // We allow for storage controllers to have schema fields which are
+          // not entity fields, which can be useful for normalization purposes.
+          // The 'default_langcode' field, for example, is simply a
+          // denormalization of checking the language code in the data table
+          // against the language code in the base table and similarly the
+          // 'isDefaultRevision' field (which we add as a query expression in
+          // self::buildQuery()) is denormalization of checking the revision ID
+          // in the revision table against the revision ID in the base table.
+          if (!empty($column_info['serialize'])) {
+            $value = serialize($value);
+          }
 
-    $multi_column_fields = array();
-    foreach (drupal_schema_fields_sql($this->entityType->get($table_key)) as $name) {
-      // Check for fields which store data in multiple columns and process them
-      // separately.
-      if ($field = strstr($name, '__', TRUE)) {
-        $multi_column_fields[$field] = TRUE;
-        continue;
-      }
-      $values[$name] = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL;
-    }
-
-    // Handle fields that store multiple properties and match each property name
-    // to its schema column name.
-    foreach (array_keys($multi_column_fields) as $field_name) {
-      $field_items = $entity->get($field_name);
-      $field_value = $field_items->getValue();
-      foreach (array_keys($field_items->getFieldDefinition()->getColumns()) as $field_schema_column) {
-        if (isset($schema['fields'][$field_name . '__' . $field_schema_column])) {
-          $values[$field_name . '__' . $field_schema_column] = isset($field_value[0][$field_schema_column]) ? $field_value[0][$field_schema_column] : 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) {
+            $info = $definition->getSchema()['columns'][$column];
+            list(, $storage_column) = each($storage_columns);
+            $record->$storage_column = drupal_schema_get_field_value($info, $value);
+          }
         }
       }
     }
 
-    foreach ($values as $field_name => $value) {
-      // 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->$field_name = drupal_schema_get_field_value($schema['fields'][$field_name], $value);
-      }
-    }
-
     return $record;
   }
 
@@ -712,15 +1026,17 @@ protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'bas
    *
    * @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'.
+   * @param string $table_name
+   *   (optional) The table name to map records to. Defaults to the data table.
    *
    * @return \stdClass
    *   The record to store.
    */
-  protected function mapToDataStorageRecord(EntityInterface $entity, $table_key = 'data_table') {
-    $record = $this->mapToStorageRecord($entity, $table_key);
+  protected function mapToDataStorageRecord(EntityInterface $entity, $table_name = NULL) {
+    if (!isset($table_name)) {
+      $table_name = $this->dataTable;
+    }
+    $record = $this->mapToStorageRecord($entity, $table_name);
     $record->langcode = $entity->language()->id;
     $record->default_langcode = intval($record->langcode == $entity->getUntranslated()->language()->id);
     return $record;
@@ -736,7 +1052,7 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $table_key =
    *   The revision id.
    */
   protected function saveRevision(EntityInterface $entity) {
-    $record = $this->mapToStorageRecord($entity, 'revision_table');
+    $record = $this->mapToStorageRecord($entity, $this->revisionTable);
 
     // When saving a new revision, set any existing revision ID to NULL so as to
     // ensure that a new revision will actually be created.
@@ -747,7 +1063,15 @@ protected function saveRevision(EntityInterface $entity) {
     $entity->preSaveRevision($this, $record);
 
     if ($entity->isNewRevision()) {
-      drupal_write_record($this->revisionTable, $record);
+      $insert_id = $this->database
+        ->insert($this->revisionTable, array('return' => Database::RETURN_INSERT_ID))
+        ->fields((array) $record)
+        ->execute();
+      // Even if this is a new revsision, the revision ID key might have been
+      // set in which case we should not override the provided revision ID.
+      if (!isset($record->{$this->revisionKey})) {
+        $record->{$this->revisionKey} = $insert_id;
+      }
       if ($entity->isDefaultRevision()) {
         $this->database->update($this->entityType->getBaseTable())
           ->fields(array($this->revisionKey => $record->{$this->revisionKey}))
@@ -756,7 +1080,11 @@ protected function saveRevision(EntityInterface $entity) {
       }
     }
     else {
-      drupal_write_record($this->revisionTable, $record, $this->revisionKey);
+      $this->database
+        ->update($this->revisionTable)
+        ->fields((array) $record)
+        ->condition($this->revisionKey, $record->{$this->revisionKey})
+        ->execute();
     }
 
     // Make sure to update the new revision key for the entity.
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index 0c2bfe4..469047c 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -23,6 +23,13 @@
   protected $bundleKey = FALSE;
 
   /**
+   * The entity langcode key
+   *
+   * @var string|bool
+   */
+  protected $langcodeKey = FALSE;
+
+  /**
    * Name of the entity class.
    *
    * @var string
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index ee2bebc..5276c26 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Entity\Plugin\DataType\EntityReference;
 use Drupal\Core\Entity\Query\QueryException;
+use Drupal\Core\Entity\SqlStorageInterface;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Field as FieldInfo;
 
@@ -35,7 +36,6 @@ class Tables implements TablesInterface {
    */
   protected $entityTables = array();
 
-
   /**
    * Field table array, key is table name, value is alias.
    *
@@ -46,10 +46,18 @@ class Tables implements TablesInterface {
   protected $fieldTables = array();
 
   /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
    * @param \Drupal\Core\Database\Query\SelectInterface $sql_query
    */
   public function __construct(SelectInterface $sql_query) {
     $this->sqlQuery = $sql_query;
+    $this->entityManager = \Drupal::entityManager();
   }
 
   /**
@@ -57,7 +65,6 @@ public function __construct(SelectInterface $sql_query) {
    */
   public function addField($field, $type, $langcode) {
     $entity_type_id = $this->sqlQuery->getMetaData('entity_type');
-    $entity_manager = \Drupal::entityManager();
     $field_info = FieldInfo::fieldInfo();
     $age = $this->sqlQuery->getMetaData('age');
     // This variable ensures grouping works correctly. For example:
@@ -74,7 +81,7 @@ public function addField($field, $type, $langcode) {
     // This will contain the definitions of the last specifier seen by the
     // system.
     $propertyDefinitions = array();
-    $entity_type = $entity_manager->getDefinition($entity_type_id);
+    $entity_type = $this->entityManager->getDefinition($entity_type_id);
     // Use the lightweight and fast field map for checking whether a specifier
     // is a field or not. While calling field_info_field() on every specifier
     // delivers the same information, if no specifiers are using the field API
@@ -141,7 +148,7 @@ public function addField($field, $type, $langcode) {
             if ($bundle_key = $entity_type->getKey('bundle')) {
               $values[$bundle_key] = reset($field_map[$entity_type_id][$field_name]['bundles']);
             }
-            $entity = $entity_manager
+            $entity = $this->entityManager
               ->getStorage($entity_type_id)
               ->create($values);
             $propertyDefinitions = $entity->$field_name->getFieldDefinition()->getPropertyDefinitions();
@@ -174,10 +181,10 @@ public function addField($field, $type, $langcode) {
         $entity_tables = array();
         if ($data_table = $entity_type->getDataTable()) {
           $this->sqlQuery->addMetaData('simple_query', FALSE);
-          $entity_tables[$data_table] = drupal_get_schema($data_table);
+          $entity_tables[$data_table] = $this->getTableSchema($data_table);
         }
         $entity_base_table = $entity_type->getBaseTable();
-        $entity_tables[$entity_base_table] = drupal_get_schema($entity_base_table);
+        $entity_tables[$entity_base_table] = $this->getTableSchema($entity_base_table);
         $sql_column = $specifier;
         $table = $this->ensureEntityTable($index_prefix, $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables);
       }
@@ -195,7 +202,7 @@ public function addField($field, $type, $langcode) {
             $bundles = entity_get_bundles($entity_type_id);
             $values[$bundle_key] = key($bundles);
           }
-          $entity = $entity_manager
+          $entity = $this->entityManager
             ->getStorage($entity_type_id)
             ->create($values);
           $propertyDefinitions = $entity->$specifier->getFieldDefinition()->getPropertyDefinitions();
@@ -206,7 +213,7 @@ public function addField($field, $type, $langcode) {
         if (isset($propertyDefinitions[$relationship_specifier]) && $entity->get($specifier)->first()->get('entity') instanceof EntityReference) {
           // If it is, use the entity type.
           $entity_type_id = $propertyDefinitions[$relationship_specifier]->getTargetDefinition()->getEntityTypeId();
-          $entity_type = $entity_manager->getDefinition($entity_type_id);
+          $entity_type = $this->entityManager->getDefinition($entity_type_id);
           // Add the new entity base table using the table and sql column.
           $join_condition= '%alias.' . $entity_type->getKey('id') . " = $table.$sql_column";
           $base_table = $this->sqlQuery->leftJoin($entity_type->getBaseTable(), NULL, $join_condition);
@@ -272,4 +279,26 @@ protected function addJoin($type, $table, $join_condition, $langcode) {
     return $this->sqlQuery->addJoin($type, $table, NULL, $join_condition, $arguments);
   }
 
+  /**
+   * Returns the schema for the given table.
+   *
+   * @param string $table
+   *   The table name.
+   *
+   * @return array|bool
+   *   The schema for the given table or FALSE if not available.
+   */
+  protected function getTableSchema($table) {
+    $entity_type_id = $this->sqlQuery->getMetaData('entity_type');
+    $storage = $this->entityManager->getStorage($entity_type_id);
+    // @todo Stop calling drupal_get_schema() once menu links are converted
+    //   to the Entity Field API. See https://drupal.org/node/1842858.
+    $schema = drupal_get_schema($table);
+    if (!$schema && $storage instanceof SqlStorageInterface) {
+      $storage_schema = $storage->getSchema();
+      $schema = isset($storage_schema[$table]) ? $storage_schema[$table] : FALSE;
+    }
+    return $schema;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php
new file mode 100644
index 0000000..b1ecf72
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php
@@ -0,0 +1,345 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Schema\ContentEntitySchemaHandler.
+ */
+
+namespace Drupal\Core\Entity\Schema;
+
+use Drupal\Core\Entity\ContentEntityType;
+use Drupal\Core\Entity\ContentEntityDatabaseStorage;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+
+/**
+ * An entity schema builder that supports revisionable, translatable entities.
+ */
+class ContentEntitySchemaHandler implements ContentEntitySchemaHandlerInterface {
+
+  /**
+   * The entity type this schema builder is responsible for.
+   *
+   * @var \Drupal\Core\Entity\ContentEntityType
+   */
+  protected $entityType;
+
+  /**
+   * The base field definitions for this entity type.
+   *
+   * @var \Drupal\Core\Field\FieldDefinitionInterface[]
+   */
+  protected $fieldDefinitions;
+
+  /**
+   * The storage object for the given entity type.
+   *
+   * @var \Drupal\Core\Entity\ContentEntityDatabaseStorage
+   */
+  protected $storage;
+
+  /**
+   * A static cache of the generated schema array.
+   *
+   * @var array
+   */
+  protected $schema;
+
+  /**
+   * Constructs a ContentEntitySchemaHandler.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, ContentEntityType $entity_type, ContentEntityDatabaseStorage $storage) {
+    $this->entityType = $entity_type;
+    $this->fieldDefinitions = $entity_manager->getBaseFieldDefinitions($entity_type->id());
+    $this->storage = $storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSchema() {
+    // Prepare basic information about the entity type.
+    $tables = $this->getTables();
+    // If this entity type does not support storage, no schema information can
+    // be collected.
+    if (empty($tables)) {
+      $this->schema[$this->entityType->id()] = NULL;
+    }
+
+    if (!isset($this->schema[$this->entityType->id()])) {
+      // @todo Remove this. This is currently needed because some entity types
+      //   declare a schema that does not match their field definitions.
+      //   Therefore we use the legacy schema if it's still declared. This
+      //   allows to iteratively enable the automatic schema generation for all
+      //   entity types.
+      if ($base_schema = drupal_get_schema($tables['base_table'])) {
+        $schema[$tables['base_table']] = $base_schema;
+        unset($tables['base_table']);
+        foreach ($tables as $table) {
+          $schema[$table] = drupal_get_schema($table);
+        }
+      }
+      else {
+        // Initialize the table schema.
+        $schema[$tables['base_table']] = $this->initializeBaseTable();
+        if (isset($tables['revision_table'])) {
+          $schema[$tables['revision_table']] = $this->initializeRevisionTable();
+        }
+        if (isset($tables['data_table'])) {
+          $schema[$tables['data_table']] = $this->initializeDataTable();
+        }
+        if (isset($tables['revision_data_table'])) {
+          $schema[$tables['revision_data_table']] = $this->initializeRevisionDataTable();
+        }
+
+        // Add the schema from field definitions.
+        foreach ($this->storage->getTableMapping() as $table => $field_names) {
+          foreach ($field_names as $field_name => $column_names) {
+            $this->addFieldSchema($schema[$table], $field_name);
+          }
+        }
+
+        // Process tables after having gathered field information.
+        $this->processBaseTable($schema[$tables['base_table']]);
+        if (isset($tables['revision_table'])) {
+          $this->processRevisionTable($schema[$tables['revision_table']]);
+        }
+        if (isset($tables['data_table'])) {
+          $this->processDataTable($schema[$tables['data_table']]);
+        }
+        if (isset($tables['revision_data_table'])) {
+          $this->processRevisionDataTable($schema[$tables['revision_data_table']]);
+        }
+      }
+
+      $this->schema[$this->entityType->id()] = $schema;
+    }
+
+    return $this->schema[$this->entityType->id()];
+  }
+
+  /**
+   * Gets a list of entity type tables.
+   *
+   * @return array
+   *   A list of entity type tables, keyed by table key.
+   */
+  protected function getTables() {
+    return array_filter(array(
+      'base_table' => $this->storage->getBaseTable(),
+      'revision_table' => $this->storage->getRevisionTable(),
+      'data_table' => $this->storage->getDataTable(),
+      'revision_data_table' => $this->storage->getRevisionDataTable(),
+    ));
+  }
+
+  /**
+   * Returns the schema for a single field definition.
+   *
+   * @param array $schema
+   *   The table schema to add the field schema to, passed by reference.
+   * @param string $field_name
+   *   The name of the field.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The field definition to return the schema for.
+   */
+  protected function addFieldSchema(array &$schema, $field_name) {
+    $field_definition = $this->fieldDefinitions[$field_name];
+    $field_schema = $field_definition->getSchema();
+    if (count($field_schema['columns']) == 1) {
+
+      $schema['fields'][$field_name] = $field_schema['columns'][$field_definition->getMainPropertyName()];
+      $schema['fields'][$field_name]['description'] = $field_definition->getDescription();
+
+      // Only entity keys are required.
+      $schema['fields'][$field_name]['not null'] = (bool) $this->entityType->getKey($field_name);
+
+      if (!empty($field_schema['unique keys'])) {
+        $schema['unique keys'][$field_name] = array($field_name);
+      }
+      if (!empty($field_schema['indexes'])) {
+        // @todo Support indexes specified as an array of column name and
+        //   length.
+        $schema['indexes'][$field_name] = array($field_name);
+      }
+      if (!empty($field_schema['foreign keys'])) {
+        $schema += array('foreign keys' => array());
+        $schema['foreign keys'] += $field_schema['foreign keys'];
+      }
+    }
+    else {
+      foreach ($field_schema['columns'] as $column_name => $column_schema) {
+        // Only entity keys are required.
+        $column_schema['not null'] = (bool) $this->entityType->getKey($field_name);
+        $schema['fields'][$field_name . '__' . $column_name] = $column_schema;
+      }
+    }
+
+    // @todo Descriptions, unique keys, indexes and foreign keys.
+  }
+
+  /**
+   * Returns the schema for the 'default_langcode' metadata field.
+   *
+   * @return array
+   *   A schema field array for the 'default_langcode' metadata field.
+   */
+  protected function getDefaultLangcodeSchema() {
+    return array(
+      'description' => 'Boolean indicating whether field values are in the default entity language.',
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 1,
+    );
+  }
+
+  /**
+   * Initializes common information for a base table.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function initializeBaseTable() {
+    return array(
+      'description' => "The base table for {$this->entityType->id()} entities.",
+      'primary key' => array($this->entityType->getKey('id')),
+    );
+  }
+
+  /**
+   * Initializes common information for a revision table.
+   *
+   * @return array
+   *   A partial schema array for the revision table.
+   */
+  protected function initializeRevisionTable() {
+    $id_key = $this->entityType->getKey('id');
+    return array(
+      'description' => "The revision table for {$this->entityType->id()} entities.",
+      'primary key' => array($this->entityType->getKey('revision')),
+      'foreign keys' => array(
+        $id_key => array(
+          'table' => $this->entityType->getBaseTable(),
+          'columns' => array($id_key => $id_key),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Initializes common information for a data table.
+   *
+   * @return array
+   *   A partial schema array for the data table.
+   */
+  protected function initializeDataTable() {
+    $id_key = $this->entityType->getKey('id');
+    return array(
+      'description' => "The data table for {$this->entityType->id()} entities.",
+      // @todo Use the language entity key when https://drupal.org/node/2143729
+      //   is in.
+      'primary key' => array($id_key, 'langcode'),
+      'foreign keys' => array(
+        $id_key => array(
+          'table' => $this->entityType->getBaseTable(),
+          'columns' => array($id_key => $id_key),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Initializes common information for a revision data table.
+   *
+   * @return array
+   *   A partial schema array for the revision data table.
+   */
+  protected function initializeRevisionDataTable() {
+    $id_key = $this->entityType->getKey('id');
+    $revision_key = $this->entityType->getKey('revision');
+    return array(
+      'description' => "The revision data table for {$this->entityType->id()} entities.",
+      // @todo Use the language entity key when https://drupal.org/node/2143729
+      //   is in.
+      'primary key' => array($revision_key, 'langcode'),
+      'foreign keys' => array(
+        $id_key => array(
+          'table' => $this->entityType->getBaseTable(),
+          'columns' => array($id_key => $id_key),
+        ),
+        $revision_key => array(
+          'table' => $this->entityType->getRevisionTable(),
+          'columns' => array($revision_key => $revision_key),
+        )
+      ),
+    );
+  }
+
+  /**
+   * Processes the gathered schema for a base table.
+   *
+   * @param array $schema
+   *   The table schema, passed by reference.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function processBaseTable(array &$schema) {
+    $id_key = $this->entityType->getKey('id');
+    // Change the ID field in the base table to 'serial' if it is 'int'.
+    if ($schema['fields'][$id_key]['type'] == 'int') {
+      $schema['fields'][$id_key]['type'] = 'serial';
+      unset($schema['fields'][$id_key]['default']);
+    }
+  }
+
+  /**
+   * Processes the gathered schema for a base table.
+   *
+   * @param array $schema
+   *   The table schema, passed by reference.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function processRevisionTable(array &$schema) {
+    $revision_key = $this->entityType->getKey('revision');
+    // Change the revision ID field in the revision table 'serial' if it is
+    // 'int'.
+    if ($schema['fields'][$revision_key]['type'] == 'int') {
+      $schema['fields'][$revision_key]['type'] = 'serial';
+      unset($schema['fields'][$revision_key]['default']);
+    }
+  }
+
+  /**
+   * Processes the gathered schema for a base table.
+   *
+   * @param array $schema
+   *   The table schema, passed by reference.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function processDataTable(array &$schema) {
+    $schema['fields']['default_langcode'] = $this->getDefaultLangcodeSchema();
+  }
+
+  /**
+   * Processes the gathered schema for a base table.
+   *
+   * @param array $schema
+   *   The table schema, passed by reference.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function processRevisionDataTable(array &$schema) {
+    $schema['fields']['default_langcode'] = $this->getDefaultLangcodeSchema();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandlerInterface.php b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandlerInterface.php
new file mode 100644
index 0000000..5b958d8
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandlerInterface.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface.
+ */
+namespace Drupal\Core\Entity\Schema;
+
+/**
+ * A common interface for building the database schema for entities.
+ */
+interface ContentEntitySchemaHandlerInterface {
+
+  /**
+   * Gets the full schema array for a given entity type.
+   *
+   * @return array
+   *   A schema array for the entity type's tables.
+   */
+  public function getSchema();
+
+}
diff --git a/core/lib/Drupal/Core/Entity/SqlStorageInterface.php b/core/lib/Drupal/Core/Entity/SqlStorageInterface.php
new file mode 100644
index 0000000..4bff3c2
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/SqlStorageInterface.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\SqlStorageInterface.
+ */
+
+namespace Drupal\Core\Entity;
+
+/**
+ * A common interface for SQL-based storage controllers.
+ */
+interface SqlStorageInterface {
+
+  /**
+   * Gets a mapping of schema fields that will be stored per entity table.
+   *
+   * @return array
+   *  An nested array where the keys are the names of the entity tables and the
+   *  values are arrays containing the names of the fields that will be stored
+   *  in the respective tables.
+   */
+  public function getTableMapping();
+
+}
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorage.php b/core/modules/comment/lib/Drupal/comment/CommentStorage.php
index d05b2c3..ccc6a74 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorage.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorage.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\field\FieldInfo;
@@ -36,13 +37,15 @@ class CommentStorage extends ContentEntityDatabaseStorage implements CommentStor
    *   An array of entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
    * @param \Drupal\comment\CommentStatisticsInterface $comment_statistics
    *   The comment statistics service.
    */
-  public function __construct(EntityTypeInterface $entity_info, Connection $database, FieldInfo $field_info, CommentStatisticsInterface $comment_statistics) {
-    parent::__construct($entity_info, $database, $field_info);
+  public function __construct(EntityTypeInterface $entity_info, Connection $database, FieldInfo $field_info, EntityManagerInterface $entity_manager, CommentStatisticsInterface $comment_statistics) {
+    parent::__construct($entity_info, $database, $field_info, $entity_manager);
     $this->statistics = $comment_statistics;
   }
 
@@ -54,6 +57,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $entity_info,
       $container->get('database'),
       $container->get('field.info'),
+      $container->get('entity.manager'),
       $container->get('comment.statistics')
     );
   }
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
index 753745f..0ae29c8 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
@@ -27,7 +27,7 @@ class EditTestBase extends DrupalUnitTestBase {
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('entity_test', array('entity_test', 'entity_test_rev'));
+    $this->installEntitySchema('entity_test');
     $this->installConfig(array('field', 'filter'));
   }
 
diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module
index 1fa3852..4ac08c2 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -9,6 +9,7 @@
  */
 
 use Drupal\Core\Config\Entity\ConfigEntityStorage;
+use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface;
 
 /**
  * Implements hook_help().
@@ -99,6 +100,33 @@ function entity_entity_bundle_delete($entity_type_id, $bundle) {
 }
 
 /**
+ * Implements hook_modules_installed().
+ */
+function entity_modules_installed($modules) {
+  // Install entity type tables.
+  $entity_manager = \Drupal::entityManager();
+  $schema = \Drupal::database()->schema();
+  $definitions = $entity_manager->getDefinitions();
+
+  foreach ($modules as $module) {
+    foreach ($definitions as $entity_type_id => $entity_type) {
+      if ($entity_type->getProvider() == $module) {
+        $storage = $entity_manager->getStorage($entity_type_id);
+        if ($storage instanceof ContentEntitySchemaHandlerInterface) {
+          foreach ($storage->getSchema() as $table_name => $table_schema) {
+            // @todo Remove the drupal_get_schema() call once all entity types
+            //   have been converted to an automatic schema.
+            if (!drupal_get_schema($table_name) && !$schema->tableExists($table_name)) {
+              $schema->createTable($table_name, $table_schema);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
  * Implements hook_module_preuninstall().
  */
 function entity_module_preuninstall($module) {
@@ -112,3 +140,30 @@ function entity_module_preuninstall($module) {
     }
   }
 }
+
+/**
+ * Implements hook_modules_uninstalled().
+ */
+function entity_modules_uninstalled($modules) {
+  $entity_manager = \Drupal::entityManager();
+  $schema = \Drupal::database()->schema();
+  $definitions = $entity_manager->getDefinitions();
+
+  foreach ($modules as $module) {
+    foreach ($definitions as $entity_type_id => $entity_type) {
+      if ($entity_type->getProvider() == $module) {
+        // Remove entity tables.
+        $storage = $entity_manager->getStorage($entity_type->id());
+        if ($storage instanceof ContentEntitySchemaHandlerInterface) {
+          foreach ($storage->getSchema() as $table_name => $table_schema) {
+            // @todo Remove the drupal_get_schema() call once all entity types
+            //   have been converted to an automatic schema.
+            if (!drupal_get_schema($table_name) && $schema->tableExists($table_name)) {
+              $schema->dropTable($table_name, $table_schema);
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php
index f035f37..f032080 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php
@@ -75,7 +75,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
+    $this->installEntitySchema('entity_test_rev');
 
     // Setup a field and instance.
     entity_reference_create_instance(
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
index 4b0e85e..eed2791 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
@@ -36,7 +36,7 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
+    $this->installEntitySchema('entity_test_rev');
   }
 
   /**
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
index cf43966..8f9d07c 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
@@ -35,7 +35,7 @@
    */
   function setUp() {
     parent::setUp();
-    $this->installSchema('entity_test', 'entity_test');
+    $this->installEntitySchema('entity_test', 'entity_test');
     $this->installSchema('system', array('sequences', 'config_snapshot'));
     $this->installSchema('user', array('users', 'users_roles'));
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
index d8d9f9c..85ea0dc 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
@@ -33,7 +33,7 @@ class TranslationWebTest extends FieldTestBase {
    *
    * @var string
    */
-  protected $entity_type = 'entity_test_rev';
+  protected $entity_type = 'entity_test_mulrev';
 
   /**
    * The field to use in this test.
@@ -78,7 +78,7 @@ function setUp() {
       'bundle' => $this->entity_type,
     );
     entity_create('field_instance_config', $instance)->save();
-    $this->instance = entity_load('field_instance_config', 'entity_test.' . $instance['bundle'] . '.' . $this->field_name);
+    $this->instance = entity_load('field_instance_config', $this->entity_type . '.' . $instance['bundle'] . '.' . $this->field_name);
 
     entity_get_form_display($this->entity_type, $this->entity_type, 'default')
       ->setComponent($this->field_name)
diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
index 7f859a1..e3c09f6 100644
--- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
+++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
@@ -63,7 +63,7 @@ function setUp() {
     parent::setUp();
     $this->installSchema('system', array('url_alias', 'router'));
     $this->installSchema('user', array('users'));
-    $this->installSchema('entity_test', array('entity_test'));
+    $this->installEntitySchema('entity_test');
     $this->installConfig(array('field', 'language'));
 
     // Add English as a language.
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
index adff21d..78a8629 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
@@ -58,7 +58,6 @@ public function testPatchUpdate() {
     $patch_entity = entity_create($entity_type, $patch_values);
     // We don't want to overwrite the UUID.
     unset($patch_entity->uuid);
-    $patch_entity->save();
     $serialized = $serializer->serialize($patch_entity, $this->defaultFormat);
 
     // Update the entity over the REST API.
diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php b/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php
index 865704d..1ad843a 100644
--- a/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php
+++ b/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php
@@ -80,18 +80,12 @@ public function testNormalize() {
       'id' => array(
         array('value' => 1),
       ),
-      'revision_id' => array(
-        array('value' => 1),
-      ),
       'uuid' => array(
         array('value' => $this->entity->uuid()),
       ),
       'langcode' => array(
         array('value' => Language::LANGCODE_NOT_SPECIFIED),
       ),
-      'default_langcode' => array(
-        array('value' => NULL),
-      ),
       'name' => array(
         array('value' => $this->values['name']),
       ),
@@ -101,6 +95,9 @@ public function testNormalize() {
       'user_id' => array(
         array('target_id' => $this->values['user_id']),
       ),
+      'revision_id' => array(
+        array('value' => 1),
+      ),
       'field_test_text' => array(
         array(
           'value' => $this->values['field_test_text']['value'],
@@ -141,13 +138,12 @@ public function testSerialize() {
     // order.
     $expected = array(
       'id' => '<id><value>' . $this->entity->id() . '</value></id>',
-      'revision_id' => '<revision_id><value>' . $this->entity->getRevisionId() . '</value></revision_id>',
       'uuid' => '<uuid><value>' . $this->entity->uuid() . '</value></uuid>',
       'langcode' => '<langcode><value>' . Language::LANGCODE_NOT_SPECIFIED . '</value></langcode>',
-      'default_langcode' => '<default_langcode><value/></default_langcode>',
       'name' => '<name><value>' . $this->values['name'] . '</value></name>',
       'type' => '<type><value>entity_test_mulrev</value></type>',
       'user_id' => '<user_id><target_id>' . $this->values['user_id'] . '</target_id></user_id>',
+      'revision_id' => '<revision_id><value>' . $this->entity->getRevisionId() . '</value></revision_id>',
       'field_test_text' => '<field_test_text><value>' . $this->values['field_test_text']['value'] . '</value><format>' . $this->values['field_test_text']['format'] . '</format></field_test_text>',
     );
     // Sort it in the same order as normalised.
diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
index 3b0cfd6..c4eb1ca 100644
--- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
+++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
@@ -21,7 +21,7 @@
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_revision', 'entity_test_mulrev_property_data'));
+    $this->installEntitySchema('entity_test_mulrev');
     $this->installSchema('user', array('users', 'users_roles'));
     $this->installSchema('system', array('url_alias'));
     $this->installConfig(array('field'));
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
index eaf7079..255200a 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
@@ -170,10 +170,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('Language code'))
       ->setDescription(t('The language code of the shortcut.'));
 
-    $fields['default_langcode'] = FieldDefinition::create('boolean')
-      ->setLabel(t('Default language'))
-      ->setDescription(t('Flag to indicate whether this is the default language.'));
-
     $fields['path'] = FieldDefinition::create('string')
       ->setLabel(t('Path'))
       ->setDescription(t('The computed shortcut path.'))
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
index 7737cc8..5d89844 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
@@ -7,8 +7,10 @@
 
 namespace Drupal\simpletest;
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface;
 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
 use Drupal\Core\Language\Language;
 use Symfony\Component\DependencyInjection\Reference;
@@ -332,6 +334,36 @@ protected function installSchema($module, $tables) {
   }
 
   /**
+   * Installs the tables for a specific entity type.
+   *
+   * @param string $entity_type_id
+   *   The ID of the entity type.
+   */
+  protected function installEntitySchema($entity_type_id) {
+    /** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
+    $entity_manager = $this->container->get('entity.manager');
+    /** @var \Drupal\Core\Database\Schema $schema_handler */
+    $schema_handler = $this->container->get('database')->schema();
+
+    $storage = $entity_manager->getStorage($entity_type_id);
+    if ($storage instanceof ContentEntitySchemaHandlerInterface) {
+      $schema = $storage->getSchema();
+      foreach ($schema as $table_name => $table_schema) {
+        $schema_handler->createTable($table_name, $table_schema);
+      }
+      $this->pass(String::format('Installed entity type tables for the %entity_type entity type: %tables', array(
+        '%entity_type' => $entity_type_id,
+        '%tables' => '{' . implode('}, {', array_keys($schema)) . '}',
+      )));
+    }
+    else {
+      throw new \RuntimeException(String::format('Entity type %entity_type does not support automatic schema installation.', array(
+        '%entity-type' => $entity_type_id,
+      )));
+    }
+  }
+
+  /**
    * Enables modules for this test.
    *
    * @param array $modules
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
index 2acf138..6cd8d39 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
@@ -19,7 +19,7 @@ class DrupalUnitTestBaseTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('entity', 'entity_test');
+  public static $modules = array('entity', 'entity_test', 'field', 'simpletest_test');
 
   public static function getInfo() {
     return array(
@@ -33,17 +33,18 @@ public static function getInfo() {
    * Tests expected behavior of setUp().
    */
   function testSetUp() {
-    $modules = array('entity', 'entity_test');
-    $table = 'entity_test';
+    $tables = array('entity_test', 'simpletest_test');
 
     // Verify that specified $modules have been loaded.
     $this->assertTrue(function_exists('entity_test_permission'), 'entity_test.module was loaded.');
     // Verify that there is a fixed module list.
-    $this->assertIdentical(array_keys(\Drupal::moduleHandler()->getModuleList()), $modules);
-    $this->assertIdentical(\Drupal::moduleHandler()->getImplementations('permission'), $modules);
+    $this->assertIdentical(array_keys(\Drupal::moduleHandler()->getModuleList()), static::$modules);
+    $this->assertIdentical(\Drupal::moduleHandler()->getImplementations('permission'), array('entity', 'entity_test'));
 
     // Verify that no modules have been installed.
-    $this->assertFalse(db_table_exists($table), "'$table' database table not found.");
+    foreach($tables as $table){
+      $this->assertFalse(db_table_exists($table), "'$table' database table not found.");
+    }
   }
 
   /**
@@ -124,8 +125,8 @@ function testEnableModulesInstallContainer() {
    * Tests expected behavior of installSchema().
    */
   function testInstallSchema() {
-    $module = 'entity_test';
-    $table = 'entity_test';
+    $module = 'simpletest_test';
+    $table = 'simpletest_test';
     // Verify that we can install a table from the module schema.
     $this->installSchema($module, $table);
     $this->assertTrue(db_table_exists($table), "'$table' database table found.");
diff --git a/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.info.yml b/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.info.yml
new file mode 100644
index 0000000..6c98069
--- /dev/null
+++ b/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.info.yml
@@ -0,0 +1,7 @@
+name: Simpletest Test
+type: module
+description: 'Provides dummy hook implementations for use by SimpleTest tests.'
+package: Testing
+version: VERSION
+core: 8.x
+hidden: TRUE
diff --git a/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.install b/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.install
new file mode 100644
index 0000000..f9b16a0
--- /dev/null
+++ b/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.install
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the Schema Test module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function simpletest_test_schema(){
+  $schema['simpletest_test'] = array(
+    'description' => 'Stores simpltest_test data.',
+    'fields' => array(
+      'sid' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique ID.',
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'dummy_textfield' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'A Dummy text field.',
+      ),
+      'dummy_integer' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'A dummy integer field.',
+      ),
+    ),
+    'indexes' => array(
+      'simpltest_test_index' => array('sid', 'dummy_integer'),
+    ),
+    'primary key' => array('sid'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+
+  return $schema;
+}
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 a9f0105..5d2364c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
@@ -25,16 +25,10 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision'
-    ));
+
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
index 61c8c87..7435100 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -42,16 +42,10 @@ public function setUp() {
     parent::setUp();
     $this->installSchema('user', array('users_data'));
     $this->installSchema('node', array('node', 'node_revision', 'node_field_data', 'node_field_revision', 'node_access'));
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision'
-    ));
+
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
 
     // Create the test field.
     entity_test_install();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
index bd2ceb9..db45c92 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
@@ -50,16 +50,10 @@ function setUp() {
 
     $this->languageManager = $this->container->get('language_manager');
 
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision',
-    ));
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
+
     $this->installConfig(array('language'));
 
     // Create the test field.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
index 70f180e..4c2220f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
@@ -57,7 +57,9 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
-    $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision'));
+
+    $this->installEntitySchema('entity_test_mulrev');
+
     $this->installConfig(array('language'));
 
     $figures = drupal_strtolower($this->randomName());
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
index ec7134c..a0ec726 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
@@ -23,16 +23,9 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision',
-    ));
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
index 99fe76a..ffeb3c8 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
@@ -44,7 +44,9 @@ public function setUp() {
 
     $this->installSchema('user', array('users', 'users_roles'));
     $this->installSchema('system', 'sequences');
-    $this->installSchema('entity_test', 'entity_test');
+
+    $this->installEntitySchema('entity_test');
+
     $this->installConfig(array('field'));
   }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
index 118abe9..169ab3d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
@@ -38,16 +38,10 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
     $this->installSchema('user', array('users_data'));
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision'
-    ));
+
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
 
     // Create the test field.
     entity_test_install();
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 82e63b2..de4aa63 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
@@ -66,7 +66,8 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
-    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
+
+    $this->installEntitySchema('entity_test_rev');
     $entity_type = 'entity_test_rev';
 
     $this->field_name = strtolower($this->randomName());
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install
index 9e48029..f2db594 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.install
+++ b/core/modules/system/tests/modules/entity_test/entity_test.install
@@ -36,406 +36,3 @@ function entity_test_install() {
       ->save();
   }
 }
-
-/**
- * Implements hook_schema().
- */
-function entity_test_schema() {
-  // Schema for simple entity.
-  $schema['entity_test'] = array(
-    'description' => 'Stores entity_test items.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique entity-test item ID.',
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'type' => array(
-        'description' => 'The bundle of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of the original variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
-  );
-
-  // Schema for entity with revisions.
-  $schema['entity_test_rev'] = array(
-    'description' => 'Stores entity_test_rev items.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique entity-test item ID.',
-      ),
-      'revision_id' => array(
-        'description' => 'The current {entity_test_rev_property_revision}.revision_id version identifier.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'type' => array(
-        'description' => 'The bundle of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
-  );
-  $schema['entity_test_rev_revision'] = array(
-    'description' => 'Stores entity_test_rev item property revisions.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_rev}.id of the test entity.',
-      ),
-      'revision_id' => array(
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The primary identifier for this version.',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'indexes' => array(
-      'user_id' => array('user_id'),
-    ),
-    'foreign keys' => array(
-      'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test_rev' => 'id'),
-    ),
-    'primary key' => array('revision_id'),
-  );
-
-  // Schema for entity with data table.
-  $schema['entity_test_mul'] = array(
-    'description' => 'Stores entity_test_mul items.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique entity-test item ID.',
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'type' => array(
-        'description' => 'The bundle of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of the original variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
-  );
-  $schema['entity_test_mul_property_data'] = array(
-    'description' => 'Stores entity_test_mul item properties.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_mul}.id of the test entity.',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'default_langcode' => array(
-        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 1,
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => FALSE,
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'indexes' => array(
-      'user_id' => array('user_id'),
-    ),
-    'foreign keys' => array(
-      'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test_mul' => 'id'),
-    ),
-    'primary key' => array('id', 'langcode'),
-  );
-
-  // Schema for entity with data table and revisions.
-  $schema['entity_test_mulrev'] = array(
-    'description' => 'Stores entity_test_mulrev items.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique entity-test item ID.',
-      ),
-      'revision_id' => array(
-        'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'type' => array(
-        'description' => 'The bundle of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
-  );
-  $schema['entity_test_mulrev_revision'] = array(
-    'description' => 'Stores entity_test_rev item property revisions.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_rev}.id of the test entity.',
-      ),
-      'revision_id' => array(
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The primary identifier for this version.',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'foreign keys' => array(
-      'id' => array('entity_test_rev' => 'id'),
-    ),
-    'primary key' => array('revision_id'),
-  );
-  $schema['entity_test_mulrev_property_data'] = array(
-    'description' => 'Stores entity_test_mulrev item properties.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_mulrev}.id of the test entity.',
-      ),
-      'revision_id' => array(
-        'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'default_langcode' => array(
-        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 1,
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'indexes' => array(
-      'user_id' => array('user_id'),
-    ),
-    'foreign keys' => array(
-      'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test_mulrev' => 'id'),
-    ),
-    'primary key' => array('id', 'langcode'),
-  );
-  $schema['entity_test_mulrev_property_revision'] = array(
-    'description' => 'Stores entity_test_mulrev item property revisions.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_mulrev}.id of the test entity.',
-      ),
-      'revision_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The primary identifier for this version.',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'default_langcode' => array(
-        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 1,
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'indexes' => array(
-      'user_id' => array('user_id'),
-    ),
-    'foreign keys' => array(
-      'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test_mulrev' => 'id'),
-    ),
-    'primary key' => array('revision_id', 'langcode'),
-  );
-
-  return $schema;
-}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestBaseFieldDisplay.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestBaseFieldDisplay.php
index c76cff7..6b7100a 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestBaseFieldDisplay.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestBaseFieldDisplay.php
@@ -28,7 +28,6 @@
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
- *     "revision" = "revision_id",
  *     "bundle" = "type"
  *   },
  *   links = {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
index 481711f..e881b9a 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
@@ -15,7 +15,6 @@
  *   label = @Translation("Entity test label callback"),
  *   field_cache = FALSE,
  *   base_table = "entity_test",
- *   revision_table = "entity_test_revision",
  *   label_callback = "entity_test_label_callback",
  *   fieldable = TRUE,
  *   entity_keys = {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
index 49f7d5f..bec368c 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
@@ -44,17 +44,4 @@
  */
 class EntityTestMul extends EntityTest {
 
-  /**
-   * {@inheritdoc}
-   */
-  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
-    $fields = parent::baseFieldDefinitions($entity_type);
-
-    $fields['default_langcode'] = FieldDefinition::create('boolean')
-      ->setLabel(t('Default language'))
-      ->setDescription(t('Flag to indicate whether this is the default language.'));
-
-    return $fields;
-  }
-
 }
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
index 870af93..d1ea8da 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
@@ -44,22 +44,4 @@
  */
 class EntityTestMulRev extends EntityTestRev {
 
-  /**
-   * {@inheritdoc}
-   */
-  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
-    $fields = parent::baseFieldDefinitions($entity_type);
-
-    $fields['revision_id'] = FieldDefinition::create('integer')
-      ->setLabel(t('Revision ID'))
-      ->setDescription(t('The version id of the test entity.'))
-      ->setReadOnly(TRUE);
-
-    $fields['default_langcode'] = FieldDefinition::create('boolean')
-      ->setLabel(t('Default language'))
-      ->setDescription(t('Flag to indicate whether this is the default language.'));
-
-    return $fields;
-  }
-
 }
diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
index 8e37920..633ee0b 100644
--- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
@@ -48,7 +48,7 @@ function setUp() {
 
     // Configure the theme system.
     $this->installConfig(array('system', 'field'));
-    $this->installSchema('entity_test', 'entity_test');
+    $this->installEntitySchema('entity_test');
 
     // @todo Add helper methods for all of the following.
 
diff --git a/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
index 9b93095..30af0bf 100644
--- a/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
@@ -50,7 +50,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
+    $this->installEntitySchema('entity_test_rev');
 
     // Create the necessary formats.
     $this->installConfig(array('filter'));
diff --git a/core/modules/user/lib/Drupal/user/UserStorage.php b/core/modules/user/lib/Drupal/user/UserStorage.php
index d3f4990..81266b4 100644
--- a/core/modules/user/lib/Drupal/user/UserStorage.php
+++ b/core/modules/user/lib/Drupal/user/UserStorage.php
@@ -9,7 +9,9 @@
 
 use Drupal\Component\Uuid\UuidInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface;
 use Drupal\Core\Password\PasswordInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\field\FieldInfo;
@@ -48,15 +50,17 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt
    *   The database connection to be used.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
-   * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
-   *   The UUID Service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface $schema_builder
+   *   The entity schema builder.
    * @param \Drupal\Core\Password\PasswordInterface $password
    *   The password hashing service.
    * @param \Drupal\user\UserDataInterface $user_data
    *   The user data service.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) {
-    parent::__construct($entity_type, $database, $field_info, $uuid_service);
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info, EntityManagerInterface $entity_manager, PasswordInterface $password, UserDataInterface $user_data) {
+    parent::__construct($entity_type, $database, $field_info, $entity_manager);
 
     $this->password = $password;
     $this->userData = $user_data;
@@ -70,7 +74,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $entity_type,
       $container->get('database'),
       $container->get('field.info'),
-      $container->get('uuid'),
+      $container->get('entity.manager'),
       $container->get('password'),
       $container->get('user.data')
     );
diff --git a/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php b/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php
index 7b7d0b7..ff0c225 100644
--- a/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php
@@ -49,7 +49,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('entity_test', array('entity_test'));
+    $this->installEntitySchema('entity_test');
 
     $this->storage = $this->container->get('entity.manager')->getStorage('entity_test');
   }
