diff --git a/core/modules/forum/forum.views.inc b/core/modules/forum/forum.views.inc
index 5f67af5..1565962 100644
--- a/core/modules/forum/forum.views.inc
+++ b/core/modules/forum/forum.views.inc
@@ -74,7 +74,7 @@ function forum_views_data() {
     'filter' => [
       'title' => t('Has taxonomy term'),
       'id' => 'taxonomy_index_tid',
-      'hierarchy table' => 'taxonomy_term_hierarchy',
+      'hierarchy table' => 'taxonomy_term__parent',
       'numeric' => TRUE,
       'skip base' => 'taxonomy_term_data',
       'allow empty' => TRUE,
diff --git a/core/modules/forum/tests/src/Functional/ForumTest.php b/core/modules/forum/tests/src/Functional/ForumTest.php
index 9ba4be7..2bc6a45 100644
--- a/core/modules/forum/tests/src/Functional/ForumTest.php
+++ b/core/modules/forum/tests/src/Functional/ForumTest.php
@@ -433,7 +433,7 @@ public function createForum($type, $parent = 0) {
 
     // Verify forum hierarchy.
     $tid = $term['tid'];
-    $parent_tid = db_query("SELECT t.parent FROM {taxonomy_term_hierarchy} t WHERE t.tid = :tid", [':tid' => $tid])->fetchField();
+    $parent_tid = db_query("SELECT t.parent_target_id FROM {taxonomy_term__parent} t WHERE t.entity_id = :tid", [':tid' => $tid])->fetchField();
     $this->assertTrue($parent == $parent_tid, 'The ' . $type . ' is linked to its container');
 
     $forum = $this->container->get('entity.manager')->getStorage('taxonomy_term')->load($tid);
diff --git a/core/modules/hal/hal.services.yml b/core/modules/hal/hal.services.yml
index a877163..1330180 100644
--- a/core/modules/hal/hal.services.yml
+++ b/core/modules/hal/hal.services.yml
@@ -1,7 +1,7 @@
 services:
   serializer.normalizer.entity_reference_item.hal:
     class: Drupal\hal\Normalizer\EntityReferenceItemNormalizer
-    arguments: ['@hal.link_manager', '@serializer.entity_resolver']
+    arguments: ['@hal.link_manager', '@serializer.entity_resolver', '@entity_type.manager']
     tags:
       - { name: normalizer, priority: 10 }
   serializer.normalizer.field_item.hal:
diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
index fbcae8c..1a4ef54 100644
--- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
+++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
@@ -2,16 +2,22 @@
 
 namespace Drupal\hal\Normalizer;
 
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Field\FieldItemInterface;
+use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
 use Drupal\hal\LinkManager\LinkManagerInterface;
 use Drupal\serialization\EntityResolver\EntityResolverInterface;
 use Drupal\serialization\EntityResolver\UuidReferenceInterface;
+use Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizerTrait;
 
 /**
  * Converts the Drupal entity reference item object to HAL array structure.
  */
 class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidReferenceInterface {
 
+  use EntityReferenceFieldItemNormalizerTrait;
+
   /**
    * The interface or class that this Normalizer supports.
    *
@@ -34,31 +40,41 @@ class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidR
   protected $entityResolver;
 
   /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
    * Constructs an EntityReferenceItemNormalizer object.
    *
    * @param \Drupal\hal\LinkManager\LinkManagerInterface $link_manager
    *   The hypermedia link manager.
    * @param \Drupal\serialization\EntityResolver\EntityResolverInterface $entity_Resolver
    *   The entity resolver.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface|null $entity_type_manager
+   *   The entity type manager.
    */
-  public function __construct(LinkManagerInterface $link_manager, EntityResolverInterface $entity_Resolver) {
+  public function __construct(LinkManagerInterface $link_manager, EntityResolverInterface $entity_Resolver, EntityTypeManagerInterface $entity_type_manager = NULL) {
     $this->linkManager = $link_manager;
     $this->entityResolver = $entity_Resolver;
+    $this->entityTypeManager = $entity_type_manager ?: \Drupal::service('entity_type.manager');
   }
 
   /**
    * {@inheritdoc}
    */
   public function normalize($field_item, $format = NULL, array $context = []) {
-    /** @var $field_item \Drupal\Core\Field\FieldItemInterface */
-    $target_entity = $field_item->get('entity')->getValue();
-
-    // If this is not a content entity, let the parent implementation handle it,
-    // only content entities are supported as embedded resources.
-    if (!($target_entity instanceof FieldableEntityInterface)) {
+    // If this is not a fieldable entity, let the parent implementation handle
+    // it, only fieldable entities are supported as embedded resources.
+    if (!$this->targetEntityIsFieldable($field_item)) {
       return parent::normalize($field_item, $format, $context);
     }
 
+    /** @var $field_item \Drupal\Core\Field\FieldItemInterface */
+    $target_entity = $field_item->get('entity')->getValue();
+
     // If the parent entity passed in a langcode, unset it before normalizing
     // the target entity. Otherwise, untranslatable fields of the target entity
     // will include the langcode.
@@ -92,6 +108,39 @@ public function normalize($field_item, $format = NULL, array $context = []) {
   }
 
   /**
+   * Checks whether the referenced entity is of a fieldable entity type.
+   *
+   * @param \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $item
+   *   The reference field item whose target entity needs to be checked.
+   *
+   * @return bool
+   *   TRUE when the referenced entity is of a fieldable entity type.
+   */
+  protected function targetEntityIsFieldable(EntityReferenceItem $item) {
+    $target_entity = $item->get('entity')->getValue();
+
+    if ($target_entity !== NULL) {
+      return $target_entity instanceof FieldableEntityInterface;
+    }
+
+    $referencing_entity = $item->getEntity();
+    $target_entity_type_id = $item->getFieldDefinition()->getSetting('target_type');
+
+    // If the entity type is the same as the parent, we can check that. This is
+    // just a shortcut to avoid getting the entity type defintition and checking
+    // the class.
+    if ($target_entity_type_id === $referencing_entity->getEntityTypeId()) {
+      return $referencing_entity instanceof FieldableEntityInterface;
+    }
+
+    // Otherwise, we need to get the class for the type.
+    $target_entity_type = $this->entityTypeManager->getDefinition($target_entity_type_id);
+    $target_entity_type_class = $target_entity_type->getClass();
+
+    return is_a($target_entity_type_class, FieldableEntityInterface::class, TRUE);
+  }
+
+  /**
    * {@inheritdoc}
    */
   protected function constructValue($data, $context) {
@@ -108,6 +157,22 @@ protected function constructValue($data, $context) {
   /**
    * {@inheritdoc}
    */
+  protected function normalizedFieldValues(FieldItemInterface $field_item, $format, array $context) {
+    // Normalize root reference values here so we don't need to deal with hal's
+    // nested data structure for field items. This will be called from
+    // \Drupal\hal\Normalizer\FieldItemNormalizer::normalize. Which will only
+    // be called from this class for entities that are not fieldable.
+    $normalized = parent::normalizedFieldValues($field_item, $format, $context);
+
+    $this->normalizeRootReferenceValue($normalized, $field_item);
+
+    return $normalized;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
   public function getUuid($data) {
     if (isset($data['uuid'])) {
       $uuid = $data['uuid'];
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonAnonTest.php
index 9b0cee2..3edd9b1 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonAnonTest.php
@@ -25,11 +25,11 @@ class CommentHalJsonAnonTest extends CommentHalJsonTestBase {
    * @see ::setUpAuthorization
    */
   protected static $patchProtectedFieldNames = [
-    'entity_id',
     'changed',
     'thread',
     'entity_type',
     'field_name',
+    'entity_id',
   ];
 
 }
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php b/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php
index 3deb0ea..1939e04 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php
@@ -26,6 +26,25 @@
    */
   protected static $mimeType = 'application/hal+json';
 
+  /**
+   * {@inheritdoc}
+   *
+   * The HAL+JSON format causes different PATCH-protected fields. For some
+   * reason, the 'pid' and 'homepage' fields are NOT PATCH-protected, even
+   * though they are for non-HAL+JSON serializations.
+   *
+   * @todo fix in https://www.drupal.org/node/2824271
+   */
+  protected static $patchProtectedFieldNames = [
+    'status',
+    'created',
+    'changed',
+    'thread',
+    'entity_type',
+    'field_name',
+    'entity_id',
+    'uid',
+  ];
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php
index 2de6539..e218a73 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php
@@ -33,6 +33,19 @@ class NodeHalJsonAnonTest extends NodeResourceTestBase {
   /**
    * {@inheritdoc}
    */
+  protected static $patchProtectedFieldNames = [
+    'revision_timestamp',
+    'created',
+    'changed',
+    'promote',
+    'sticky',
+    'path',
+    'revision_uid',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
   protected function getExpectedNormalizedEntity() {
     $default_normalization = parent::getExpectedNormalizedEntity();
 
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php
index 73a1549..ba08f24 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\hal\Functional\EntityResource\Term;
 
+use Drupal\taxonomy\Entity\Term;
+use Drupal\taxonomy\TermInterface;
 use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
 use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
 use Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase;
@@ -37,6 +39,114 @@ protected function getExpectedNormalizedEntity() {
 
     $normalization = $this->applyHalFieldNormalization($default_normalization);
 
+    // We test with multiple parent terms, and combinations thereof.
+    // @see ::createEntity()
+    // @see ::testGet()
+    // @see ::testGetTermWithParent()
+    // @see ::providerTestGetTermWithParent()
+    // @see ::testGetTermWithParent()
+    $parent_term_ids = [];
+    for ($i = 0; $i < $this->entity->get('parent')->count(); $i++) {
+      $parent_term_ids[$i] = (int) $this->entity->get('parent')[$i]->target_id;
+    }
+
+    $expected_parent_normalization_links = FALSE;
+    $expected_parent_normalization_embedded = FALSE;
+    switch($parent_term_ids) {
+      case [0]:
+        $expected_parent_normalization_links = [
+          NULL,
+        ];
+        $expected_parent_normalization_embedded = [
+          NULL,
+        ];
+        break;
+      case [2]:
+        $expected_parent_normalization_links = [
+          [
+          'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+          ],
+        ];
+        $expected_parent_normalization_embedded = [
+          [
+            '_links' => [
+              'self' => [
+                'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+              ],
+              'type' => [
+                'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+              ],
+            ],
+            'uuid' => [
+              ['value' => Term::load(2)->uuid()],
+            ],
+          ],
+        ];
+        break;
+      case [0, 2]:
+        $expected_parent_normalization_links = [
+          NULL,
+          [
+            'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+          ],
+        ];
+        $expected_parent_normalization_embedded = [
+          NULL,
+          [
+            '_links' => [
+              'self' => [
+                'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+              ],
+              'type' => [
+                'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+              ],
+            ],
+            'uuid' => [
+              ['value' => Term::load(2)->uuid()],
+            ],
+          ],
+        ];
+        break;
+      case [3, 2]:
+        $expected_parent_normalization_links = [
+          [
+            'href' => $this->baseUrl . '/taxonomy/term/3?_format=hal_json',
+          ],
+          [
+            'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+          ],
+        ];
+        $expected_parent_normalization_embedded = [
+          [
+            '_links' => [
+              'self' => [
+                'href' => $this->baseUrl . '/taxonomy/term/3?_format=hal_json',
+              ],
+              'type' => [
+                'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+              ],
+            ],
+            'uuid' => [
+              ['value' => Term::load(3)->uuid()],
+            ],
+          ],
+          [
+            '_links' => [
+              'self' => [
+                'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+              ],
+              'type' => [
+                'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+              ],
+            ],
+            'uuid' => [
+              ['value' => Term::load(2)->uuid()],
+            ],
+          ],
+        ];
+        break;
+    }
+
     return $normalization + [
       '_links' => [
         'self' => [
@@ -45,6 +155,10 @@ protected function getExpectedNormalizedEntity() {
         'type' => [
           'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
         ],
+        $this->baseUrl . '/rest/relation/taxonomy_term/camelids/parent' => $expected_parent_normalization_links,
+      ],
+      '_embedded' => [
+        $this->baseUrl . '/rest/relation/taxonomy_term/camelids/parent' => $expected_parent_normalization_embedded,
       ],
     ];
   }
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
index 85a4b1a..02e9a3b 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\taxonomy\Entity\Term;
 use Drupal\taxonomy\Entity\Vocabulary;
+use Drupal\taxonomy\TermInterface;
 use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
 use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
 use GuzzleHttp\RequestOptions;
@@ -92,6 +93,66 @@ protected function createEntity() {
    * {@inheritdoc}
    */
   protected function getExpectedNormalizedEntity() {
+    // We test with multiple parent terms, and combinations thereof.
+    // @see ::createEntity()
+    // @see ::testGet()
+    // @see ::testGetTermWithParent()
+    // @see ::providerTestGetTermWithParent()
+    $parent_term_ids = [];
+    for ($i = 0; $i < $this->entity->get('parent')->count(); $i++) {
+      $parent_term_ids[$i] = (int) $this->entity->get('parent')[$i]->target_id;
+    }
+
+    $expected_parent_normalization = FALSE;
+    switch($parent_term_ids) {
+      case [0]:
+        $expected_parent_normalization = [
+          [
+            'target_id' => NULL,
+          ],
+        ];
+        break;
+      case [2]:
+        $expected_parent_normalization = [
+          [
+            'target_id' => 2,
+            'target_type' => 'taxonomy_term',
+            'target_uuid' => Term::load(2)->uuid(),
+            'url' => base_path() . 'taxonomy/term/2',
+          ],
+        ];
+        break;
+      case [0, 2]:
+        $expected_parent_normalization = [
+          [
+            'target_id' => NULL,
+          ],
+          [
+            'target_id' => 2,
+            'target_type' => 'taxonomy_term',
+            'target_uuid' => Term::load(2)->uuid(),
+            'url' => base_path() . 'taxonomy/term/2',
+          ],
+        ];
+        break;
+      case [3, 2]:
+        $expected_parent_normalization = [
+          [
+            'target_id' => 3,
+            'target_type' => 'taxonomy_term',
+            'target_uuid' => Term::load(3)->uuid(),
+            'url' => base_path() . 'taxonomy/term/3',
+          ],
+          [
+            'target_id' => 2,
+            'target_type' => 'taxonomy_term',
+            'target_uuid' => Term::load(2)->uuid(),
+            'url' => base_path() . 'taxonomy/term/2',
+          ],
+        ];
+        break;
+    }
+
     return [
       'tid' => [
         ['value' => 1],
@@ -116,7 +177,7 @@ protected function getExpectedNormalizedEntity() {
           'processed' => "<p>It is a little known fact that llamas cannot count higher than seven.</p>\n",
         ],
       ],
-      'parent' => [],
+      'parent' => $expected_parent_normalization,
       'weight' => [
         ['value' => 0],
       ],
@@ -238,4 +299,54 @@ protected function getExpectedCacheContexts() {
     return Cache::mergeContexts(['url.site'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
   }
 
+  /**
+   * Tests GETting a term with a parent term other than the default <root> (0).
+   *
+   * @see ::getExpectedNormalizedEntity()
+   *
+   * @dataProvider providerTestGetTermWithParent
+   */
+  public function testGetTermWithParent(array $parent_term_ids) {
+    // Create all possible parent terms.
+    Term::create(['vid' => Vocabulary::load('camelids')->id()])
+      ->setName('Lamoids')
+      ->save();
+    Term::create(['vid' => Vocabulary::load('camelids')->id()])
+      ->setName('Wimoids')
+      ->save();
+
+    // Modify the entity under test to use the provided parent terms.
+    $this->entity->set('parent', $parent_term_ids)->save();
+
+    $this->initAuthentication();
+    $url = $this->getEntityResourceUrl();
+    $url->setOption('query', ['_format' => static::$format]);
+    $request_options = $this->getAuthenticationRequestOptions('GET');
+    $this->provisionEntityResource();
+    $this->setUpAuthorization('GET');
+    $response = $this->request('GET', $url, $request_options);
+    $expected = $this->getExpectedNormalizedEntity();
+    static::recursiveKSort($expected);
+    $actual = $this->serializer->decode((string) $response->getBody(), static::$format);
+    static::recursiveKSort($actual);
+    $this->assertSame($expected, $actual);
+  }
+
+  public function providerTestGetTermWithParent() {
+    return [
+      'root parent: [0] (= no parent)' => [
+        [0]
+      ],
+      'non-root parent: [2]' => [
+        [2]
+      ],
+      'multiple parents: [0,2] (root + non-root parent)' => [
+        [0, 2]
+      ],
+      'multiple parents: [3,2] (both non-root parents)' => [
+        [3, 2]
+      ],
+    ];
+  }
+
 }
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/XmlEntityNormalizationQuirksTrait.php b/core/modules/rest/tests/src/Functional/EntityResource/XmlEntityNormalizationQuirksTrait.php
index 962d8e9..b69394b 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/XmlEntityNormalizationQuirksTrait.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/XmlEntityNormalizationQuirksTrait.php
@@ -99,7 +99,7 @@ protected function applyXmlFieldDecodingQuirks(array $normalization) {
         }
       }
 
-      if (!empty($normalization[$field_name])) {
+      if (count($normalization[$field_name]) === 1) {
         $normalization[$field_name] = $normalization[$field_name][0];
       }
     }
diff --git a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php
index ea2e020..2d3c6f0 100644
--- a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php
+++ b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php
@@ -12,6 +12,8 @@
  */
 class EntityReferenceFieldItemNormalizer extends FieldItemNormalizer {
 
+  use EntityReferenceFieldItemNormalizerTrait;
+
   /**
    * The interface or class that this Normalizer supports.
    *
@@ -42,6 +44,8 @@ public function __construct(EntityRepositoryInterface $entity_repository) {
   public function normalize($field_item, $format = NULL, array $context = []) {
     $values = parent::normalize($field_item, $format, $context);
 
+    $this->normalizeRootReferenceValue($values, $field_item);
+
     /** @var \Drupal\Core\Entity\EntityInterface $entity */
     if ($entity = $field_item->get('entity')->getValue()) {
       $values['target_type'] = $entity->getEntityTypeId();
@@ -55,6 +59,7 @@ public function normalize($field_item, $format = NULL, array $context = []) {
         $values['url'] = $url;
       }
     }
+
     return $values;
   }
 
diff --git a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizerTrait.php b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizerTrait.php
new file mode 100644
index 0000000..1acfd70
--- /dev/null
+++ b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizerTrait.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\serialization\Normalizer;
+
+use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
+
+/**
+ * Converts empty reference values for entity reference items.
+ */
+trait EntityReferenceFieldItemNormalizerTrait {
+
+  protected function normalizeRootReferenceValue(&$values, EntityReferenceItem $field_item) {
+    // @todo Generalize for all tree-structured entity types.
+    if ($this->fieldItemReferencesTaxonomyTerm($field_item) && empty($values['target_id'])) {
+      $values['target_id'] = NULL;
+    }
+  }
+
+  /**
+   * Determines if a field item references a taxonomy term.
+   *
+   * @param \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $field_item
+   *
+   * @return bool
+   */
+  protected function fieldItemReferencesTaxonomyTerm(EntityReferenceItem $field_item) {
+    return $field_item->getFieldDefinition()->getSetting('target_type') === 'taxonomy_term';
+  }
+
+}
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
index 5cc6467..9320d17 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
@@ -120,6 +120,13 @@ public function testNormalize() {
       ->willReturn($entity->reveal())
       ->shouldBeCalled();
 
+    $field_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $field_definition->getSetting('target_type')
+      ->willReturn('test_type');
+
+    $this->fieldItem->getFieldDefinition()
+      ->willReturn($field_definition->reveal());
+
     $this->fieldItem->get('entity')
       ->willReturn($entity_reference)
       ->shouldBeCalled();
@@ -142,12 +149,59 @@ public function testNormalize() {
   /**
    * @covers ::normalize
    */
+  public function testNormalizeWithEmptyTaxonomyTermReference() {
+    // Override the serializer prophecy from setUp() to return a zero value.
+    $this->serializer = $this->prophesize(Serializer::class);
+    // Set up the serializer to return an entity property.
+    $this->serializer->normalize(Argument::cetera())
+      ->willReturn(0);
+
+    $this->normalizer->setSerializer($this->serializer->reveal());
+
+    $entity_reference = $this->prophesize(TypedDataInterface::class);
+    $entity_reference->getValue()
+      ->willReturn(NULL)
+      ->shouldBeCalled();
+
+    $field_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $field_definition->getSetting('target_type')
+      ->willReturn('taxonomy_term');
+
+    $this->fieldItem->getFieldDefinition()
+      ->willReturn($field_definition->reveal());
+
+    $this->fieldItem->get('entity')
+      ->willReturn($entity_reference)
+      ->shouldBeCalled();
+
+    $this->fieldItem->getProperties(TRUE)
+      ->willReturn(['target_id' => $this->getTypedDataProperty(FALSE)])
+      ->shouldBeCalled();
+
+    $normalized = $this->normalizer->normalize($this->fieldItem->reveal());
+
+    $expected = [
+      'target_id' => NULL,
+    ];
+    $this->assertSame($expected, $normalized);
+  }
+
+  /**
+   * @covers ::normalize
+   */
   public function testNormalizeWithNoEntity() {
     $entity_reference = $this->prophesize(TypedDataInterface::class);
     $entity_reference->getValue()
       ->willReturn(NULL)
       ->shouldBeCalled();
 
+    $field_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $field_definition->getSetting('target_type')
+      ->willReturn('test_type');
+
+    $this->fieldItem->getFieldDefinition()
+      ->willReturn($field_definition->reveal());
+
     $this->fieldItem->get('entity')
       ->willReturn($entity_reference->reveal())
       ->shouldBeCalled();
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php
new file mode 100644
index 0000000..852261e
--- /dev/null
+++ b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Contains database additions to drupal-8.bare.standard.php.gz for testing the
+ * upgrade path of https://www.drupal.org/node/2455125.
+ */
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Serialization\Yaml;
+
+$connection = Database::getConnection();
+
+$view_file = __DIR__ . '/drupal-8.views-taxonomy-parent-2543726.yml';
+$view_config = Yaml::decode(file_get_contents($view_file));
+
+$connection->insert('config')
+  ->fields(['collection', 'name', 'data'])
+  ->values([
+    'collection' => '',
+    'name' => "views.view.test_taxonomy_parent",
+    'data' => serialize($view_config),
+  ])
+  ->execute();
+
+$uuid = new \Drupal\Component\Uuid\Php();
+
+// The root tid.
+$tids = [0];
+
+for ($i = 0; $i < 4; $i++) {
+  $name = $this->randomString();
+
+  $tid = $connection->insert('taxonomy_term_data')
+    ->fields(['vid', 'uuid', 'langcode'])
+    ->values(['vid' => 'tags', 'uuid' => $uuid->generate(), 'langcode' => 'en'])
+    ->execute();
+
+  $connection->insert('taxonomy_term_field_data')
+    ->fields(['tid', 'vid', 'langcode', 'name', 'weight', 'changed', 'default_langcode'])
+    ->values(['tid' => $tid, 'vid' => 'tags', 'langcode' => 'en', 'name' => $name, 'weight' => 0, 'changed' => REQUEST_TIME, 'default_langcode' => 1])
+    ->execute();
+
+  $tids[] = $tid;
+}
+
+$hierarchy = [
+  // Term with tid 1 has terms with tids 2 and 3 as parents.
+  1 => [2, 3],
+  2 => [3, 0],
+  3 => [0],
+];
+
+$query = $connection->insert('taxonomy_term_hierarchy')->fields(['tid', 'parent']);
+
+foreach ($hierarchy as $tid => $parents) {
+  foreach ($parents as $parent) {
+    $query->values(['tid' => $tids[$tid], 'parent' => $tids[$parent]]);
+  }
+}
+
+$query->execute();
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml
new file mode 100644
index 0000000..65df6a8
--- /dev/null
+++ b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml
@@ -0,0 +1,134 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - taxonomy
+    - user
+id: test_taxonomy_parent
+label: test_taxonomy_parent
+module: views
+description: ''
+tag: ''
+base_table: taxonomy_term_data
+base_field: tid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'access content'
+      cache:
+        type: tag
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: full
+        options:
+          items_per_page: 10
+          offset: 0
+          id: 0
+          total_pages: null
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: '‹ Previous'
+            next: 'Next ›'
+            first: '« First'
+            last: 'Last »'
+          quantity: 9
+      style:
+        type: default
+        options:
+          grouping: {  }
+          row_class: ''
+          default_row_class: true
+          uses_fields: false
+      row:
+        type: fields
+        options:
+          inline: {  }
+          separator: ''
+          hide_empty: false
+          default_field_elements: true
+      fields:
+        name:
+          id: name
+          table: taxonomy_term_data
+          field: name
+          label: ''
+          alter:
+            alter_text: false
+            make_link: false
+            absolute: false
+            trim: false
+            word_boundary: false
+            ellipsis: false
+            strip_tags: false
+            html: false
+          hide_empty: false
+          empty_zero: false
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exclude: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_alter_empty: true
+          plugin_id: field
+          type: string
+          settings:
+            link_to_entity: true
+          entity_type: taxonomy_term
+          entity_field: name
+      filters: {  }
+      sorts: {  }
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships:
+        parent:
+          id: parent
+          table: taxonomy_term__parent
+          field: parent_target_id
+          relationship: none
+          group_type: group
+          admin_label: Parent
+          required: true
+          plugin_id: standard
+      arguments: {  }
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index b8eac0a..3a14073 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -64,22 +64,28 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti
 
     // See if any of the term's children are about to be become orphans.
     $orphans = [];
-    foreach (array_keys($entities) as $tid) {
-      if ($children = $storage->loadChildren($tid)) {
+    /** @var \Drupal\taxonomy\TermInterface $term */
+    foreach ($entities as $tid => $term) {
+      if ($children = $storage->getChildren($term)) {
+        /** @var \Drupal\taxonomy\TermInterface $child */
         foreach ($children as $child) {
+          $parent = $child->get('parent');
+          // Update child parents item list.
+          $parent->filter(function ($item) use ($tid) {
+            return $item->target_id != $tid;
+          });
+
           // If the term has multiple parents, we don't delete it.
-          $parents = $storage->loadParents($child->id());
-          if (empty($parents)) {
+          if ($parent->count()) {
+            $child->save();
+          }
+          else {
             $orphans[] = $child;
           }
         }
       }
     }
 
-    // Delete term hierarchy information after looking up orphans but before
-    // deleting them so that their children/parent information is consistent.
-    $storage->deleteTermHierarchy(array_keys($entities));
-
     if (!empty($orphans)) {
       $storage->delete($orphans);
     }
@@ -88,14 +94,11 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti
   /**
    * {@inheritdoc}
    */
-  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
-    parent::postSave($storage, $update);
-
-    // Only change the parents if a value is set, keep the existing values if
-    // not.
-    if (isset($this->parent->target_id)) {
-      $storage->deleteTermHierarchy([$this->id()]);
-      $storage->updateTermHierarchy($this);
+  public function preSave(EntityStorageInterface $storage) {
+    parent::preSave($storage);
+    // Terms with no parents are mandatory children of <root>.
+    if (!$this->get('parent')->count()) {
+      $this->parent->target_id = 0;
     }
   }
 
@@ -156,8 +159,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('Term Parents'))
       ->setDescription(t('The parents of this term.'))
       ->setSetting('target_type', 'taxonomy_term')
-      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
-      ->setCustomStorage(TRUE);
+      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED);
 
     $fields['changed'] = BaseFieldDefinition::create('changed')
       ->setLabel(t('Changed'))
diff --git a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php
index 02b5995..8eeb1f9 100644
--- a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php
+++ b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php
@@ -111,18 +111,19 @@ public function query($group_by = FALSE) {
     $last = "tn";
 
     if ($this->options['depth'] > 0) {
-      $subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid");
+      $subquery->leftJoin('taxonomy_term__parent', 'th', "th.entity_id = tn.tid");
       $last = "th";
       foreach (range(1, abs($this->options['depth'])) as $count) {
-        $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid");
-        $where->condition("th$count.tid", $tids, $operator);
+        $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.parent_target_id = th$count.entity_id");
+        $where->condition("th$count.entity_id", $tids, $operator);
         $last = "th$count";
       }
     }
     elseif ($this->options['depth'] < 0) {
       foreach (range(1, abs($this->options['depth'])) as $count) {
-        $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent");
-        $where->condition("th$count.tid", $tids, $operator);
+        $field = $count == 1 ? 'tid' : 'entity_id';
+        $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.$field = th$count.parent_target_id");
+        $where->condition("th$count.entity_id", $tids, $operator);
         $last = "th$count";
       }
     }
diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php
index 802786e..ebb5b51 100644
--- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php
@@ -77,18 +77,19 @@ public function query() {
     $last = "tn";
 
     if ($this->options['depth'] > 0) {
-      $subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid");
+      $subquery->leftJoin('taxonomy_term__parent', 'th', "th.entity_id = tn.tid");
       $last = "th";
       foreach (range(1, abs($this->options['depth'])) as $count) {
-        $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid");
-        $where->condition("th$count.tid", $this->value, $operator);
+        $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.parent_target_id = th$count.entity_id");
+        $where->condition("th$count.entity_id", $this->value, $operator);
         $last = "th$count";
       }
     }
     elseif ($this->options['depth'] < 0) {
       foreach (range(1, abs($this->options['depth'])) as $count) {
-        $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent");
-        $where->condition("th$count.tid", $this->value, $operator);
+        $field = $count == 1 ? 'tid' : 'entity_id';
+        $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.$field = th$count.parent_target_id");
+        $where->condition("th$count.entity_id", $this->value, $operator);
         $last = "th$count";
       }
     }
diff --git a/core/modules/taxonomy/src/TermStorage.php b/core/modules/taxonomy/src/TermStorage.php
index 0a00c13..f0ab848 100644
--- a/core/modules/taxonomy/src/TermStorage.php
+++ b/core/modules/taxonomy/src/TermStorage.php
@@ -2,8 +2,8 @@
 
 namespace Drupal\taxonomy;
 
-use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
 
 /**
  * Defines a Controller class for taxonomy terms.
@@ -11,27 +11,6 @@
 class TermStorage extends SqlContentEntityStorage implements TermStorageInterface {
 
   /**
-   * Array of loaded parents keyed by child term ID.
-   *
-   * @var array
-   */
-  protected $parents = [];
-
-  /**
-   * Array of all loaded term ancestry keyed by ancestor term ID.
-   *
-   * @var array
-   */
-  protected $parentsAll = [];
-
-  /**
-   * Array of child terms keyed by parent term ID.
-   *
-   * @var array
-   */
-  protected $children = [];
-
-  /**
    * Array of term parents keyed by vocabulary ID and child term ID.
    *
    * @var array
@@ -60,6 +39,14 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
   protected $trees = [];
 
   /**
+   * Array of all loaded term ancestry keyed by ancestor term ID, keyed by term
+   * ID.
+   *
+   * @var \Drupal\taxonomy\TermInterface[][]
+   */
+  protected $ancestors;
+
+  /**
    * {@inheritdoc}
    *
    * @param array $values
@@ -80,9 +67,6 @@ public function create(array $values = []) {
    */
   public function resetCache(array $ids = NULL) {
     drupal_static_reset('taxonomy_term_count_nodes');
-    $this->parents = [];
-    $this->parentsAll = [];
-    $this->children = [];
     $this->treeChildren = [];
     $this->treeParents = [];
     $this->treeTerms = [];
@@ -93,100 +77,125 @@ public function resetCache(array $ids = NULL) {
   /**
    * {@inheritdoc}
    */
-  public function deleteTermHierarchy($tids) {
-    $this->database->delete('taxonomy_term_hierarchy')
-      ->condition('tid', $tids, 'IN')
-      ->execute();
-  }
+  public function deleteTermHierarchy($tids) { }
 
   /**
    * {@inheritdoc}
    */
-  public function updateTermHierarchy(EntityInterface $term) {
-    $query = $this->database->insert('taxonomy_term_hierarchy')
-      ->fields(['tid', 'parent']);
-
-    foreach ($term->parent as $parent) {
-      $query->values([
-        'tid' => $term->id(),
-        'parent' => (int) $parent->target_id,
-      ]);
-    }
-    $query->execute();
-  }
+  public function updateTermHierarchy(EntityInterface $term) { }
 
   /**
    * {@inheritdoc}
    */
   public function loadParents($tid) {
-    if (!isset($this->parents[$tid])) {
-      $parents = [];
-      $query = $this->database->select('taxonomy_term_field_data', 't');
-      $query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid');
-      $query->addField('t', 'tid');
-      $query->condition('h.tid', $tid);
-      $query->condition('t.default_langcode', 1);
-      $query->addTag('taxonomy_term_access');
-      $query->orderBy('t.weight');
-      $query->orderBy('t.name');
-      if ($ids = $query->execute()->fetchCol()) {
-        $parents = $this->loadMultiple($ids);
+    $terms = [];
+    /** @var \Drupal\taxonomy\TermInterface $term */
+    if ($tid && $term = $this->load($tid)) {
+      foreach ($this->getParents($term) as $id => $parent) {
+        // This method currently doesn't return the <root> parent.
+        // @see https://www.drupal.org/node/2019905
+        if (!empty($id)) {
+          $terms[$id] = $parent;
+        }
+      }
+    }
+
+    return $terms;
+  }
+
+  /**
+   * Returns a list of parents of this term.
+   *
+   * @return \Drupal\taxonomy\TermInterface[]
+   *   The parent taxonomy term entities keyed by term ID. If this term has a
+   *   <root> parent, that item is keyed with 0 and will have NULL as value.
+   *
+   * @internal
+   * @todo Refactor away when TreeInterface is introduced.
+   */
+  protected function getParents(TermInterface $term) {
+    $parents = $ids = [];
+    // Cannot use $this->get('parent')->referencedEntities() here because that
+    // strips out the '0' reference.
+    foreach ($term->get('parent') as $item) {
+      if ($item->target_id == 0) {
+        // The <root> parent.
+        $parents[0] = NULL;
+        continue;
       }
-      $this->parents[$tid] = $parents;
+      $ids[] = $item->target_id;
     }
-    return $this->parents[$tid];
+
+    // @todo Better way to do this? AND handle the NULL/0 parent?
+    // Querying the terms again so that the same access checks are run when
+    // getParents() is called as in Drupal version prior to 8.3.
+    $loaded_parents = [];
+
+    if ($ids) {
+      $query = \Drupal::entityQuery('taxonomy_term')
+        ->condition('tid', $ids, 'IN');
+
+      $loaded_parents = static::loadMultiple($query->execute());
+    }
+
+    return $parents + $loaded_parents;
   }
 
   /**
    * {@inheritdoc}
    */
   public function loadAllParents($tid) {
-    if (!isset($this->parentsAll[$tid])) {
-      $parents = [];
-      if ($term = $this->load($tid)) {
-        $parents[$term->id()] = $term;
-        $terms_to_search[] = $term->id();
-
-        while ($tid = array_shift($terms_to_search)) {
-          if ($new_parents = $this->loadParents($tid)) {
-            foreach ($new_parents as $new_parent) {
-              if (!isset($parents[$new_parent->id()])) {
-                $parents[$new_parent->id()] = $new_parent;
-                $terms_to_search[] = $new_parent->id();
-              }
-            }
+    /** @var \Drupal\taxonomy\TermInterface $term */
+    return (!empty($tid) && $term = $this->load($tid)) ? $this->getAncestors($term) : [];
+  }
+
+  /**
+   * Returns all ancestors of this term.
+   *
+   * @return \Drupal\taxonomy\TermInterface[]
+   *   A list of ancestor taxonomy term entities keyed by term ID.
+   *
+   * @internal
+   * @todo Refactor away when TreeInterface is introduced.
+   */
+  protected function getAncestors(TermInterface $term) {
+    if (!isset($this->ancestors[$term->id()])) {
+      $this->ancestors[$term->id()] = [$term->id() => $term];
+      $search[] = $term->id();
+
+      while ($tid = array_shift($search)) {
+        foreach ($this->getParents(static::load($tid)) as $id => $parent) {
+          if ($parent && !isset($this->ancestors[$term->id()][$id])) {
+            $this->ancestors[$term->id()][$id] = $parent;
+            $search[] = $id;
           }
         }
       }
-
-      $this->parentsAll[$tid] = $parents;
     }
-    return $this->parentsAll[$tid];
+    return $this->ancestors[$term->id()];
   }
 
   /**
    * {@inheritdoc}
    */
   public function loadChildren($tid, $vid = NULL) {
-    if (!isset($this->children[$tid])) {
-      $children = [];
-      $query = $this->database->select('taxonomy_term_field_data', 't');
-      $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
-      $query->addField('t', 'tid');
-      $query->condition('h.parent', $tid);
-      if ($vid) {
-        $query->condition('t.vid', $vid);
-      }
-      $query->condition('t.default_langcode', 1);
-      $query->addTag('taxonomy_term_access');
-      $query->orderBy('t.weight');
-      $query->orderBy('t.name');
-      if ($ids = $query->execute()->fetchCol()) {
-        $children = $this->loadMultiple($ids);
-      }
-      $this->children[$tid] = $children;
-    }
-    return $this->children[$tid];
+    /** @var \Drupal\taxonomy\TermInterface $term */
+    return (!empty($tid) && $term = $this->load($tid)) ? $this->getChildren($term) : [];
+  }
+
+  /**
+   * Returns all children terms of this term.
+   *
+   * @return \Drupal\taxonomy\TermInterface[]
+   *   A list of children taxonomy term entities keyed by term ID.
+   *
+   * @internal
+   * @todo Refactor away when TreeInterface is introduced.
+   */
+  public function getChildren(TermInterface $term) {
+    $query = \Drupal::entityQuery('taxonomy_term')
+      ->condition('parent', $term->id());
+    return static::loadMultiple($query->execute());
   }
 
   /**
@@ -202,11 +211,11 @@ public function loadTree($vid, $parent = 0, $max_depth = NULL, $load_entities =
         $this->treeParents[$vid] = [];
         $this->treeTerms[$vid] = [];
         $query = $this->database->select('taxonomy_term_field_data', 't');
-        $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
+        $query->join('taxonomy_term__parent', 'p', 't.tid = p.entity_id');
+        $query->addExpression('parent_target_id', 'parent');
         $result = $query
           ->addTag('taxonomy_term_access')
           ->fields('t')
-          ->fields('h', ['parent'])
           ->condition('t.vid', $vid)
           ->condition('t.default_langcode', 1)
           ->orderBy('t.weight')
@@ -254,7 +263,9 @@ public function loadTree($vid, $parent = 0, $max_depth = NULL, $load_entities =
               $term = clone $term;
             }
             $term->depth = $depth;
-            unset($term->parent);
+            if (!$load_entities) {
+              unset($term->parent);
+            }
             $tid = $load_entities ? $term->id() : $term->tid;
             $term->parents = $this->treeParents[$vid][$tid];
             $tree[] = $term;
@@ -351,7 +362,7 @@ public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL)
   public function __sleep() {
     $vars = parent::__sleep();
     // Do not serialize static cache.
-    unset($vars['parents'], $vars['parentsAll'], $vars['children'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']);
+    unset($vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']);
     return $vars;
   }
 
@@ -361,9 +372,6 @@ public function __sleep() {
   public function __wakeup() {
     parent::__wakeup();
     // Initialize static caches.
-    $this->parents = [];
-    $this->parentsAll = [];
-    $this->children = [];
     $this->treeChildren = [];
     $this->treeParents = [];
     $this->treeTerms = [];
diff --git a/core/modules/taxonomy/src/TermStorageInterface.php b/core/modules/taxonomy/src/TermStorageInterface.php
index 4ab2d2d..4d7b5cc 100644
--- a/core/modules/taxonomy/src/TermStorageInterface.php
+++ b/core/modules/taxonomy/src/TermStorageInterface.php
@@ -15,6 +15,10 @@
    *
    * @param array $tids
    *   Array of terms that need to be removed from hierarchy.
+   *
+   * @todo Remove this method in Drupal 9.0.x. Now the parent references are
+   *   automatically cleared when deleting a taxonomy term.
+   *   https://www.drupal.org/node/2785693
    */
   public function deleteTermHierarchy($tids);
 
@@ -23,6 +27,10 @@ public function deleteTermHierarchy($tids);
    *
    * @param \Drupal\Core\Entity\EntityInterface $term
    *   Term entity that needs to be added to term hierarchy information.
+   *
+   * @todo remove this method Drupal 9.0.x. Now the parent references are
+   *   automatically updates when when a taxonomy term is added/updated.
+   *   https://www.drupal.org/node/2785693
    */
   public function updateTermHierarchy(EntityInterface $term);
 
diff --git a/core/modules/taxonomy/src/TermStorageSchema.php b/core/modules/taxonomy/src/TermStorageSchema.php
index 5bcb088..2b49ee2 100644
--- a/core/modules/taxonomy/src/TermStorageSchema.php
+++ b/core/modules/taxonomy/src/TermStorageSchema.php
@@ -22,36 +22,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res
       'taxonomy_term__vid_name' => ['vid', 'name'],
     ];
 
-    $schema['taxonomy_term_hierarchy'] = [
-      'description' => 'Stores the hierarchical relationship between terms.',
-      'fields' => [
-        'tid' => [
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'default' => 0,
-          'description' => 'Primary Key: The {taxonomy_term_data}.tid of the term.',
-        ],
-        'parent' => [
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'default' => 0,
-          'description' => "Primary Key: The {taxonomy_term_data}.tid of the term's parent. 0 indicates no parent.",
-        ],
-      ],
-      'indexes' => [
-        'parent' => ['parent'],
-      ],
-      'foreign keys' => [
-        'taxonomy_term_data' => [
-          'table' => 'taxonomy_term_data',
-          'columns' => ['tid' => 'tid'],
-        ],
-      ],
-      'primary key' => ['tid', 'parent'],
-    ];
-
     $schema['taxonomy_index'] = [
       'description' => 'Maintains denormalized information about node/term relationships.',
       'fields' => [
diff --git a/core/modules/taxonomy/src/TermViewsData.php b/core/modules/taxonomy/src/TermViewsData.php
index 5e98bfe..6d47f3f 100644
--- a/core/modules/taxonomy/src/TermViewsData.php
+++ b/core/modules/taxonomy/src/TermViewsData.php
@@ -36,7 +36,7 @@ public function getViewsData() {
     $data['taxonomy_term_field_data']['tid']['filter']['id'] = 'taxonomy_index_tid';
     $data['taxonomy_term_field_data']['tid']['filter']['title'] = $this->t('Term');
     $data['taxonomy_term_field_data']['tid']['filter']['help'] = $this->t('Taxonomy term chosen from autocomplete or select widget.');
-    $data['taxonomy_term_field_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term_hierarchy';
+    $data['taxonomy_term_field_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term__parent';
     $data['taxonomy_term_field_data']['tid']['filter']['numeric'] = TRUE;
 
     $data['taxonomy_term_field_data']['tid_raw'] = [
@@ -146,8 +146,8 @@ public function getViewsData() {
         'left_field' => 'nid',
         'field' => 'nid',
       ],
-      'taxonomy_term_hierarchy' => [
-        'left_field' => 'tid',
+      'taxonomy_term__parent' => [
+        'left_field' => 'entity_id',
         'field' => 'tid',
       ],
     ];
@@ -181,7 +181,7 @@ public function getViewsData() {
       'filter' => [
         'title' => $this->t('Has taxonomy term'),
         'id' => 'taxonomy_index_tid',
-        'hierarchy table' => 'taxonomy_term_hierarchy',
+        'hierarchy table' => 'taxonomy_term__parent',
         'numeric' => TRUE,
         'skip base' => 'taxonomy_term_field_data',
         'allow empty' => TRUE,
@@ -223,40 +223,15 @@ public function getViewsData() {
       ],
     ];
 
-    $data['taxonomy_term_hierarchy']['table']['group']  = $this->t('Taxonomy term');
-    $data['taxonomy_term_hierarchy']['table']['provider']  = 'taxonomy';
-
-    $data['taxonomy_term_hierarchy']['table']['join'] = [
-      'taxonomy_term_hierarchy' => [
-        // Link to self through left.parent = right.tid (going down in depth).
-        'left_field' => 'tid',
-        'field' => 'parent',
-      ],
-      'taxonomy_term_field_data' => [
-        // Link directly to taxonomy_term_field_data via tid.
-        'left_field' => 'tid',
-        'field' => 'tid',
-      ],
+    // Link to self through left.parent = right.tid (going down in depth).
+    $data['taxonomy_term__parent']['table']['join']['taxonomy_term__parent'] = [
+      'left_field' => 'entity_id',
+      'field' => 'parent_target_id',
     ];
 
-    $data['taxonomy_term_hierarchy']['parent'] = [
-      'title' => $this->t('Parent term'),
-      'help' => $this->t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.'),
-      'relationship' => [
-        'base' => 'taxonomy_term_field_data',
-        'field' => 'parent',
-        'label' => $this->t('Parent'),
-        'id' => 'standard',
-      ],
-      'filter' => [
-        'help' => $this->t('Filter the results of "Taxonomy: Term" by the parent pid.'),
-        'id' => 'numeric',
-      ],
-      'argument' => [
-        'help' => $this->t('The parent term of the term.'),
-        'id' => 'taxonomy',
-      ],
-    ];
+    $data['taxonomy_term__parent']['parent_target_id']['help'] = $this->t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.');
+    $data['taxonomy_term__parent']['parent_target_id']['relationship']['label'] = $this->t('Parent');
+    $data['taxonomy_term__parent']['parent_target_id']['argument']['id'] = 'taxonomy';
 
     return $data;
   }
diff --git a/core/modules/taxonomy/src/VocabularyStorage.php b/core/modules/taxonomy/src/VocabularyStorage.php
index bdbf893..57ed1fc 100644
--- a/core/modules/taxonomy/src/VocabularyStorage.php
+++ b/core/modules/taxonomy/src/VocabularyStorage.php
@@ -3,6 +3,7 @@
 namespace Drupal\taxonomy;
 
 use Drupal\Core\Config\Entity\ConfigEntityStorage;
+use Drupal\taxonomy\Entity\Vocabulary;
 
 /**
  * Defines a storage handler class for taxonomy vocabularies.
@@ -21,7 +22,12 @@ public function resetCache(array $ids = NULL) {
    * {@inheritdoc}
    */
   public function getToplevelTids($vids) {
-    return db_query('SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} th ON th.tid = t.tid WHERE t.vid IN ( :vids[] ) AND th.parent = 0', [':vids[]' => $vids])->fetchCol();
+    $tids = \Drupal::entityQuery('taxonomy_term')
+      ->condition('vid', $vids, 'IN')
+      ->condition('parent.target_id', 0)
+      ->execute();
+
+    return array_values($tids);
   }
 
 }
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
new file mode 100644
index 0000000..702ca7c
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.install
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the taxonomy module.
+ */
+
+/**
+ * Convert the custom taxonomy term hierarchy storage to a default storage.
+ */
+function taxonomy_update_8501() {
+  $manager = \Drupal::entityDefinitionUpdateManager();
+  /** @var \Drupal\Core\Field\BaseFieldDefinition $definition */
+  $definition = $manager->getFieldStorageDefinition('parent', 'taxonomy_term');
+  $definition->setCustomStorage(FALSE);
+  $manager->updateFieldStorageDefinition($definition);
+  // Update the entity type because a new interface was added to Term entity.
+  $manager->updateEntityType($manager->getEntityType('taxonomy_term'));
+}
+
+/**
+ * Copy hierarchy from {taxonomy_term_hierarchy} to {taxonomy_term__parent}.
+ */
+function taxonomy_update_8502(&$sandbox) {
+  $database = \Drupal::database();
+
+  if (!isset($sandbox['current'])) {
+    // Set batch ops sandbox.
+    $sandbox['current'] = 0;
+    $sandbox['max'] = $database->select('taxonomy_term_hierarchy')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+  }
+
+  // Save the hierarchy.
+  $select = $database->select('taxonomy_term_hierarchy', 'h');
+  $select->join('taxonomy_term_data', 'd', 'h.tid = d.tid');
+  $hierarchy = $select
+    ->fields('h', ['tid', 'parent'])
+    ->fields('d', ['vid', 'langcode'])
+    ->range($sandbox['current'], $sandbox['current'] + 100)
+    ->execute()
+    ->fetchAll();
+
+  // Restore data.
+  $insert = $database->insert('taxonomy_term__parent')
+    ->fields(['bundle', 'entity_id', 'revision_id', 'langcode', 'delta', 'parent_target_id']);
+  $tid = -1;
+
+  foreach ($hierarchy as $row) {
+    if ($row->tid !== $tid) {
+      $delta = 0;
+      $tid = $row->tid;
+    }
+
+    $insert->values([
+      'bundle' => $row->vid,
+      'entity_id' => $row->tid,
+      'revision_id' => $row->tid,
+      'langcode' => $row->langcode,
+      'delta' => $delta,
+      'parent_target_id' => $row->parent,
+    ]);
+
+    $delta++;
+    $sandbox['current']++;
+  }
+
+  $insert->execute();
+
+  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['current'] / $sandbox['max']);
+
+  if ($sandbox['#finished'] >= 1) {
+    // Drop the legacy table.
+    $database->schema()->dropTable('taxonomy_term_hierarchy');
+    return t('Taxonomy term legacy tables dropped.');
+
+    return t('Taxonomy term hierarchy converted to default entity reference storage.');
+  }
+}
+
+/**
+ * Update views to use {taxonomy_term__parent} in relationships.
+ */
+function taxonomy_update_8503() {
+  $config_factory = \Drupal::configFactory();
+  foreach ($config_factory->listAll('views.view.') as $id) {
+    $view = $config_factory->getEditable($id);
+    $changed = FALSE;
+
+    foreach (array_keys($view->get('display')) as $display_id) {
+      $base_path = "display.$display_id.display_options.relationships.parent";
+      $table_path = $base_path . '.table';
+      $field_path = $base_path . '.field';
+
+      if (!empty($table = $view->get($table_path)) && $table == 'taxonomy_term_hierarchy') {
+        $view->set($table_path, 'taxonomy_term__parent');
+        $view->set($field_path, 'parent_target_id');
+
+        $base_path = "display.{$display_id}.display_options.filters.parent";
+        $table_path = $base_path . '.table';
+        $field_path = $base_path . '.field';
+
+        if (!empty($table = $view->get($table_path)) && $table == 'taxonomy_term_hierarchy') {
+          $view->set($table_path, 'taxonomy_term__parent');
+          $view->set($field_path, 'parent_target_id');
+        }
+
+        $base_path = "display.{$display_id}.display_options.arguments.parent.table";
+        $table_path = $base_path . '.table';
+        $field_path = $base_path . '.field';
+
+        if (!empty($table = $view->get($table_path)) && $table == 'taxonomy_term_hierarchy') {
+          $view->set($table_path, 'taxonomy_term__parent');
+          $view->set($field_path, 'parent_target_id');
+        }
+
+        $changed = TRUE;
+      }
+    }
+
+    if ($changed) {
+      $view->save(TRUE);
+    }
+  }
+}
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml
index 256f618..65df6a8 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml
@@ -124,8 +124,8 @@ display:
       relationships:
         parent:
           id: parent
-          table: taxonomy_term_hierarchy
-          field: parent
+          table: taxonomy_term__parent
+          field: parent_target_id
           relationship: none
           group_type: group
           admin_label: Parent
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
index 80295b6..664ae27 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
@@ -186,8 +186,8 @@ display:
           plugin_id: standard
         parent:
           id: parent
-          table: taxonomy_term_hierarchy
-          field: parent
+          table: taxonomy_term__parent
+          field: parent_target_id
           relationship: none
           group_type: group
           admin_label: Parent
diff --git a/core/modules/taxonomy/tests/src/Functional/TaxonomyQueryAlterTest.php b/core/modules/taxonomy/tests/src/Functional/TaxonomyQueryAlterTest.php
index 85310e9..81bd679 100644
--- a/core/modules/taxonomy/tests/src/Functional/TaxonomyQueryAlterTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TaxonomyQueryAlterTest.php
@@ -50,12 +50,12 @@ public function testTaxonomyQueryAlter() {
     $this->setupQueryTagTestHooks();
     $loaded_terms = $term_storage->loadParents($terms[2]->id());
     $this->assertEqual(count($loaded_terms), 1, 'All parent terms were loaded');
-    $this->assertQueryTagTestResult(2, 1, 'TermStorage::loadParents()');
+    $this->assertQueryTagTestResult(3, 1, 'TermStorage::loadParents()');
 
     $this->setupQueryTagTestHooks();
     $loaded_terms = $term_storage->loadChildren($terms[1]->id());
     $this->assertEqual(count($loaded_terms), 1, 'All child terms were loaded');
-    $this->assertQueryTagTestResult(2, 1, 'TermStorage::loadChildren()');
+    $this->assertQueryTagTestResult(3, 1, 'TermStorage::loadChildren()');
 
     $this->setupQueryTagTestHooks();
     $query = db_select('taxonomy_term_data', 't');
diff --git a/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php
new file mode 100644
index 0000000..c8c9e0c
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\Tests\taxonomy\Functional\Update;
+
+use Drupal\Component\Serialization\Yaml;
+use Drupal\Component\Utility\Unicode;
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+use Drupal\taxonomy\Entity\Term;
+
+/**
+ * Ensure that the taxonomy updates are running as expected.
+ *
+ * @group taxonomy
+ * @group Update
+ */
+class TaxonomyParentUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $db;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->db = $this->container->get('database');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
+      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php',
+    ];
+  }
+
+  /**
+   * Tests taxonomy term parents update.
+   *
+   * @see taxonomy_update_8501()
+   * @see taxonomy_update_8502()
+   * @see taxonomy_update_8503()
+   */
+  public function testTaxonomyUpdateParents() {
+    // Run updates.
+    $this->runUpdates();
+
+    /** @var \Drupal\taxonomy\TermInterface $term */
+    $term = Term::load(1);
+    $parents = [2, 3];
+    $this->assertCount(2, $term->parent);
+    $this->assertTrue(in_array($term->parent[0]->entity->id(), $parents));
+    $this->assertTrue(in_array($term->parent[1]->entity->id(), $parents));
+
+    $term = Term::load(2);
+    $parents = [0, 3];
+    $this->assertCount(2, $term->parent);
+    $this->assertTrue(in_array($term->parent[0]->target_id, $parents));
+    $this->assertTrue(in_array($term->parent[1]->target_id, $parents));
+
+    $term = Term::load(3);
+    $this->assertCount(1, $term->parent);
+    // Target ID is returned as string.
+    $this->assertSame((int) $term->get('parent')[0]->target_id, 0);
+
+    // Test if the view has been converted to use the {taxonomy_term__parent}
+    // table instead of the {taxonomy_term_hierarchy} table.
+    $view = $this->config("views.view.test_taxonomy_parent");
+    $path = 'display.default.display_options.relationships.parent.table';
+    $this->assertSame($view->get($path), 'taxonomy_term__parent');
+
+    // The {taxonomy_term_hierarchy} table has been removed.
+    $this->assertFalse($this->db->schema()->tableExists('taxonomy_term_hierarchy'));
+  }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php
index 03be3e1..3299964 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php
@@ -61,24 +61,24 @@ public function testTaxonomyRelationships() {
     $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'tid');
     $this->assertEqual($views_data['table']['join']['node_field_data']['left_field'], 'nid');
     $this->assertEqual($views_data['table']['join']['node_field_data']['field'], 'nid');
-    $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['left_field'], 'tid');
-    $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['field'], 'tid');
+    $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['left_field'], 'entity_id');
+    $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['field'], 'tid');
 
-    // Check the generated views data of taxonomy_term_hierarchy.
-    $views_data = Views::viewsData()->get('taxonomy_term_hierarchy');
+    // Check the generated views data of taxonomy_term__parent.
+    $views_data = Views::viewsData()->get('taxonomy_term__parent');
     // Check the table join data.
-    $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['left_field'], 'tid');
-    $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['field'], 'parent');
+    $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['left_field'], 'entity_id');
+    $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['field'], 'parent_target_id');
     $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['left_field'], 'tid');
-    $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'tid');
+    $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'entity_id');
     // Check the parent relationship data.
-    $this->assertEqual($views_data['parent']['relationship']['base'], 'taxonomy_term_field_data');
-    $this->assertEqual($views_data['parent']['relationship']['field'], 'parent');
-    $this->assertEqual($views_data['parent']['relationship']['label'], t('Parent'));
-    $this->assertEqual($views_data['parent']['relationship']['id'], 'standard');
+    $this->assertEqual($views_data['parent_target_id']['relationship']['base'], 'taxonomy_term_field_data');
+    $this->assertEqual($views_data['parent_target_id']['relationship']['base field'], 'tid');
+    $this->assertEqual($views_data['parent_target_id']['relationship']['label'], t('Parent'));
+    $this->assertEqual($views_data['parent_target_id']['relationship']['id'], 'standard');
     // Check the parent filter and argument data.
-    $this->assertEqual($views_data['parent']['filter']['id'], 'numeric');
-    $this->assertEqual($views_data['parent']['argument']['id'], 'taxonomy');
+    $this->assertEqual($views_data['parent_target_id']['filter']['id'], 'numeric');
+    $this->assertEqual($views_data['parent_target_id']['argument']['id'], 'taxonomy');
 
     // Check an actual test view.
     $view = Views::getView('test_taxonomy_term_relationship');
@@ -95,7 +95,7 @@ public function testTaxonomyRelationships() {
       if (!$index) {
         $this->assertTrue($row->_relationship_entities['parent'] instanceof TermInterface);
         $this->assertEqual($row->_relationship_entities['parent']->id(), $this->term2->id());
-        $this->assertEqual($row->taxonomy_term_field_data_taxonomy_term_hierarchy_tid, $this->term2->id());
+        $this->assertEqual($row->taxonomy_term_field_data_taxonomy_term__parent_tid, $this->term2->id());
       }
       $this->assertTrue($row->_relationship_entities['nid'] instanceof NodeInterface);
       $this->assertEqual($row->_relationship_entities['nid']->id(), $this->nodes[$index]->id());
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php
index 14fba56..8e24294 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\taxonomy\Kernel\Migrate\d6;
 
 use Drupal\taxonomy\Entity\Term;
+use Drupal\taxonomy\TermInterface;
 use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
 
 /**
@@ -92,7 +93,7 @@ public function testTaxonomyTerms() {
       $this->assertSame($values['vid'], $term->vid->target_id);
       $this->assertSame((string) $values['weight'], $term->weight->value);
       if ($values['parent'] === [0]) {
-        $this->assertNull($term->parent->target_id);
+        $this->assertSame(0, (int) $term->parent->target_id);
       }
       else {
         $parents = [];
