diff --git a/Build/phpstan/phpstan-baseline.neon b/Build/phpstan/phpstan-baseline.neon
index 9f4c1a7c39d455c001f84297784f3e538efdbb19..3c487458229d2bd62d9eaee9a1c5b629f5454c54 100644
--- a/Build/phpstan/phpstan-baseline.neon
+++ b/Build/phpstan/phpstan-baseline.neon
@@ -1815,16 +1815,6 @@ parameters:
 			count: 1
 			path: ../../typo3/sysext/extbase/Tests/Unit/Mvc/View/JsonViewTest.php
 
-		-
-			message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNull\\(\\) with DateTime&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&TYPO3\\\\TestingFramework\\\\Core\\\\AccessibleObjectInterface will always evaluate to false\\.$#"
-			count: 1
-			path: ../../typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
-
-		-
-			message: "#^Parameter \\#2 \\$identifier of method TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\Generic\\\\Session\\:\\:registerObject\\(\\) expects string, int given\\.$#"
-			count: 1
-			path: ../../typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
-
 		-
 			message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNull\\(\\) with object will always evaluate to false\\.$#"
 			count: 1
diff --git a/composer.json b/composer.json
index 5a49c07550ad48bbfb8568febd001d202b81240a..721dc1968f132368a2f7a56f2f8c96825b28a217 100644
--- a/composer.json
+++ b/composer.json
@@ -295,6 +295,7 @@
 			"TYPO3\\CMS\\Recycler\\Tests\\": "typo3/sysext/recycler/Tests/",
 			"TYPO3\\CMS\\T3editor\\Tests\\": "typo3/sysext/t3editor/Tests/",
 			"TYPO3\\CMS\\Tstemplate\\Tests\\": "typo3/sysext/tstemplate/Tests/",
+			"TYPO3Tests\\TestDataMapper\\": "typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/",
 			"TYPO3Tests\\TestFluidTemplate\\": "typo3/sysext/frontend/Tests/Functional/Fixtures/Extensions/test_fluid_template/Classes/",
 			"TYPO3Tests\\TestLogger\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_logger/Classes/",
 			"TYPO3Tests\\TestTyposcriptAstFunctionEvent\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Classes/"
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/CustomDateTime.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/CustomDateTime.php
similarity index 88%
rename from typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/CustomDateTime.php
rename to typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/CustomDateTime.php
index 03029a2c14aadbeedd66156e434f7fc701236b99..644cbd61bf998feb762c9dc0a0f06eabfd97725a 100644
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/CustomDateTime.php
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/CustomDateTime.php
@@ -15,7 +15,7 @@ declare(strict_types=1);
  * The TYPO3 project - inspiring people to share!
  */
 
-namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Fixture\Model;
+namespace TYPO3Tests\TestDataMapper\Domain\Model;
 
 class CustomDateTime extends \DateTime
 {
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/Example.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/Example.php
new file mode 100644
index 0000000000000000000000000000000000000000..6a7449a4c89bf351d334593676ac4e784610b9f4
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/Example.php
@@ -0,0 +1,124 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3Tests\TestDataMapper\Domain\Model;
+
+use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
+
+class Example extends AbstractEntity
+{
+    protected string $firstProperty = '';
+    protected int $secondProperty = 0;
+    protected float $thirdProperty = 0.0;
+    protected bool $fourthProperty = true;
+    protected string $uninitializedStringProperty;
+    protected ?\DateTime $uninitializedDateTimeProperty;
+    protected \DateTime $uninitializedMandatoryDateTimeProperty;
+    protected ?\DateTime $initializedDateTimeProperty = null;
+    protected ?CustomDateTime $customDateTime = null;
+    public $unknownType;
+
+    public function getFirstProperty(): string
+    {
+        return $this->firstProperty;
+    }
+
+    public function setFirstProperty(string $firstProperty): void
+    {
+        $this->firstProperty = $firstProperty;
+    }
+
+    public function getSecondProperty(): int
+    {
+        return $this->secondProperty;
+    }
+
+    public function setSecondProperty(int $secondProperty): void
+    {
+        $this->secondProperty = $secondProperty;
+    }
+
+    public function getThirdProperty(): float
+    {
+        return $this->thirdProperty;
+    }
+
+    public function setThirdProperty(float $thirdProperty): void
+    {
+        $this->thirdProperty = $thirdProperty;
+    }
+
+    public function isFourthProperty(): bool
+    {
+        return $this->fourthProperty;
+    }
+
+    public function setFourthProperty(bool $fourthProperty): void
+    {
+        $this->fourthProperty = $fourthProperty;
+    }
+
+    public function getUninitializedStringProperty(): string
+    {
+        return $this->uninitializedStringProperty;
+    }
+
+    public function setUninitializedStringProperty(string $uninitializedStringProperty): void
+    {
+        $this->uninitializedStringProperty = $uninitializedStringProperty;
+    }
+
+    public function getUninitializedDateTimeProperty(): ?\DateTime
+    {
+        return $this->uninitializedDateTimeProperty;
+    }
+
+    public function setUninitializedDateTimeProperty(?\DateTime $uninitializedDateTimeProperty): void
+    {
+        $this->uninitializedDateTimeProperty = $uninitializedDateTimeProperty;
+    }
+
+    public function getUninitializedMandatoryDateTimeProperty(): \DateTime
+    {
+        return $this->uninitializedMandatoryDateTimeProperty;
+    }
+
+    public function setUninitializedMandatoryDateTimeProperty(\DateTime $uninitializedMandatoryDateTimeProperty): void
+    {
+        $this->uninitializedMandatoryDateTimeProperty = $uninitializedMandatoryDateTimeProperty;
+    }
+
+    public function getInitializedDateTimeProperty(): ?\DateTime
+    {
+        return $this->initializedDateTimeProperty;
+    }
+
+    public function setInitializedDateTimeProperty(?\DateTime $initializedDateTimeProperty): void
+    {
+        $this->initializedDateTimeProperty = $initializedDateTimeProperty;
+    }
+
+    public function getCustomDateTime(): ?CustomDateTime
+    {
+        return $this->customDateTime;
+    }
+
+    public function setCustomDateTime(?CustomDateTime $customDateTime): void
+    {
+        $this->customDateTime = $customDateTime;
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyChildEntity.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/TraversableDomainObjectExample.php
similarity index 51%
rename from typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyChildEntity.php
rename to typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/TraversableDomainObjectExample.php
index 94de351563cb06828be112b444f29738b50a2836..782292635b2aa4cc560a880c482ff700818153d6 100644
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyChildEntity.php
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/TraversableDomainObjectExample.php
@@ -15,13 +15,37 @@ declare(strict_types=1);
  * The TYPO3 project - inspiring people to share!
  */
 
-namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper\Fixture;
+namespace TYPO3Tests\TestDataMapper\Domain\Model;
 
 use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
 
-/**
- * Fixture
- */
-class DummyChildEntity extends AbstractEntity
+class TraversableDomainObjectExample extends AbstractEntity implements \Iterator
 {
+    public function getUid(): ?int
+    {
+        return 1;
+    }
+
+    public function current(): mixed
+    {
+        return [];
+    }
+
+    public function next(): void
+    {
+    }
+
+    public function key(): mixed
+    {
+        return 1;
+    }
+
+    public function valid(): bool
+    {
+        return true;
+    }
+
+    public function rewind(): void
+    {
+    }
 }
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Configuration/TCA/tx_testdatamapper_domain_model_example.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Configuration/TCA/tx_testdatamapper_domain_model_example.php
new file mode 100644
index 0000000000000000000000000000000000000000..e2123dada3bf2ac161e183ddef2c1136b76c4c2d
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Configuration/TCA/tx_testdatamapper_domain_model_example.php
@@ -0,0 +1,58 @@
+<?php
+
+return [
+    'columns' => [
+        'first_property' => [
+            'config' => [
+                'type' => 'input',
+            ],
+        ],
+        'second_property' => [
+            'config' => [
+                'type' => 'number',
+            ],
+        ],
+        'third_property' => [
+            'config' => [
+                'type' => 'number',
+                'format' => 'decimal',
+            ],
+        ],
+        'fourth_property' => [
+            'config' => [
+                'type' => 'check',
+            ],
+        ],
+        'uninitialized_string_property' => [
+            'config' => [
+                'type' => 'input',
+            ],
+        ],
+        'uninitialized_date_time_property' => [
+            'config' => [
+                'type' => 'datetime',
+            ],
+        ],
+        'uninitialized_mandatory_date_time_property' => [
+            'config' => [
+                'type' => 'datetime',
+            ],
+        ],
+        'initialized_date_time_property' => [
+            'config' => [
+                'type' => 'datetime',
+            ],
+        ],
+        'custom_date_time' => [
+            'config' => [
+                'type' => 'datetime',
+                'dbType' => 'datetime',
+            ],
+        ],
+        'unknown_type' => [
+            'config' => [
+                'type' => 'passthrough',
+            ],
+        ],
+    ],
+];
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/ext_emconf.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/ext_emconf.php
new file mode 100644
index 0000000000000000000000000000000000000000..17ee481f4d391316fa552634d7efe63634f44c2e
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/ext_emconf.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+$EM_CONF[$_EXTKEY] = [
+    'title' => 'Extension for testing DataMapper functionality.',
+    'description' => 'Extension for testing DataMapper functionality.',
+    'category' => 'example',
+    'author' => 'TYPO3 core team',
+    'author_company' => '',
+    'author_email' => '',
+    'state' => 'stable',
+    'version' => '12.1.0',
+    'constraints' => [
+        'depends' => [
+            'typo3' => '12.1.0',
+        ],
+        'conflicts' => [],
+        'suggests' => [],
+    ],
+];
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/DataMapperTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/DataMapperTest.php
index 69275bdcf7f5c25e31e5495d54be334cbcba727c..7077059786aa167e5b2480183142d9b6e5dd26f5 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/DataMapperTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/DataMapperTest.php
@@ -17,17 +17,29 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Extbase\Tests\Functional\Persistence\Generic\Mapper;
 
+use ExtbaseTeam\BlogExample\Domain\Model\Blog;
 use ExtbaseTeam\BlogExample\Domain\Model\DateExample;
 use ExtbaseTeam\BlogExample\Domain\Model\DateTimeImmutableExample;
+use ExtbaseTeam\BlogExample\Domain\Model\Post;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder;
 use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
+use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
+use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\Exception\UnknownPropertyTypeException;
 use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+use TYPO3Tests\TestDataMapper\Domain\Model\CustomDateTime;
+use TYPO3Tests\TestDataMapper\Domain\Model\Example;
+use TYPO3Tests\TestDataMapper\Domain\Model\TraversableDomainObjectExample;
 
 class DataMapperTest extends FunctionalTestCase
 {
-    protected array $testExtensionsToLoad = ['typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example'];
+    protected array $testExtensionsToLoad = [
+        'typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example',
+        'typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper',
+    ];
 
     protected PersistenceManager $persistenceManager;
 
@@ -160,4 +172,332 @@ class DataMapperTest extends FunctionalTestCase
 
         self::assertSame($date->getTimestamp(), $subject->getDatetimeImmutableDatetime()->getTimestamp());
     }
+
+    /**
+     * @test
+     */
+    public function mapMapsArrayToObject(): void
+    {
+        $rows = [['uid' => 1234]];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+
+        $mappedObjectArray = $dataMapper->map(Blog::class, $rows);
+
+        self::assertCount(1, $mappedObjectArray);
+        self::assertSame(1234, $mappedObjectArray[0]->getUid());
+    }
+
+    /**
+     * @test
+     */
+    public function mapMapsArrayToObjectFromPersistence(): void
+    {
+        $rows1 = [['uid' => 1234, 'title' => 'From persistence']];
+        $rows2 = [['uid' => 1234, 'title' => 'Already persisted']];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+
+        $dataMapper->map(Blog::class, $rows1);
+        $mappedObjectArray = $dataMapper->map(Blog::class, $rows2);
+
+        self::assertCount(1, $mappedObjectArray);
+        self::assertSame(1234, $mappedObjectArray[0]->getUid());
+        self::assertSame('From persistence', $mappedObjectArray[0]->getTitle());
+    }
+
+    /**
+     * @test
+     */
+    public function thawPropertiesSetsPropertyValues(): void
+    {
+        $rows = [
+            [
+                'uid' => '1234',
+                'first_property' => 'firstValue',
+                'second_property' => 1234,
+                'third_property' => 1.234,
+                'fourth_property' => false,
+                'uninitialized_string_property' => 'foo',
+                'uninitialized_date_time_property' => 0,
+                'uninitialized_mandatory_date_time_property' => 0,
+                'initialized_date_time_property' => 0,
+            ],
+        ];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+
+        $mappedObjectsArray = $dataMapper->map(Example::class, $rows);
+
+        /** @var Example $object */
+        $object = $mappedObjectsArray[0];
+        self::assertEquals('firstValue', $object->getFirstProperty());
+        self::assertEquals(1234, $object->getSecondProperty());
+        self::assertEquals(1.234, $object->getThirdProperty());
+        self::assertFalse($object->isFourthProperty());
+        self::assertSame('foo', $object->getUninitializedStringProperty());
+        self::assertNull($object->getUninitializedDateTimeProperty());
+        $reflectionProperty = new \ReflectionProperty($object, 'uninitializedMandatoryDateTimeProperty');
+        self::assertFalse($reflectionProperty->isInitialized($object));
+        $reflectionProperty = new \ReflectionProperty($object, 'initializedDateTimeProperty');
+        self::assertTrue($reflectionProperty->isInitialized($object));
+    }
+
+    /**
+     * @test
+     */
+    public function thawPropertiesThrowsExceptionOnUnknownPropertyType(): void
+    {
+        $rows = [
+            [
+                'uid' => '1234',
+                'unknown_type' => 'What am I?',
+            ],
+        ];
+
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+
+        $this->expectException(UnknownPropertyTypeException::class);
+        $dataMapper->map(Example::class, $rows);
+    }
+
+    /**
+     * @test
+     */
+    public function fetchRelatedEagerReturnsNullForEmptyRelationHasOne(): void
+    {
+        $rows = [
+            [
+                'uid' => 123,
+                'blog' => '',
+            ],
+        ];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+        $mappedObjectsArray = $dataMapper->map(Post::class, $rows);
+
+        self::assertNull($mappedObjectsArray[0]->getBlog());
+    }
+
+    /**
+     * @test
+     */
+    public function fetchRelatedEagerReturnsEmptyArrayForEmptyRelationNotHasOne(): void
+    {
+        $rows = [
+            [
+                'uid' => 123,
+                'posts' => 0,
+            ],
+        ];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+        $mappedObjectsArray = $dataMapper->map(Blog::class, $rows);
+
+        self::assertCount(0, $mappedObjectsArray[0]->getPosts());
+    }
+
+    /**
+     * @test
+     */
+    public function mapObjectToClassPropertyReturnsExistingObjectWithoutCallingFetchRelated(): void
+    {
+        $blogRows = [
+            [
+                'uid' => 123,
+                'posts' => 1,
+            ],
+        ];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+        $dataMapper->map(Blog::class, $blogRows);
+        $postRows = [
+            [
+                'uid' => 234,
+                'blog' => 123,
+            ],
+        ];
+
+        $mappedObjectsArray = $dataMapper->map(Post::class, $postRows);
+
+        self::assertSame(123, $mappedObjectsArray[0]->getBlog()->getUid());
+    }
+
+    /**
+     * Data provider for date checks. Date will be stored based on UTC in
+     * the database. That's why it's not possible to check for explicit date
+     * strings but using the date('c') conversion instead, which considers the
+     * current local timezone setting.
+     */
+    public function mapDateTimeHandlesDifferentFieldEvaluationsDataProvider(): array
+    {
+        return [
+            'nothing' => [null, null, null],
+            'timestamp' => [1, null, date('c', 1)],
+            'invalid date' => ['0000-00-00', 'date', null],
+            'valid date' => ['2013-01-01', 'date', date('c', strtotime('2013-01-01 00:00:00'))],
+            'invalid datetime' => ['0000-00-00 00:00:00', 'datetime', null],
+            'valid datetime' => ['2013-01-01 01:02:03', 'datetime', date('c', strtotime('2013-01-01 01:02:03'))],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider mapDateTimeHandlesDifferentFieldEvaluationsDataProvider
+     */
+    public function mapDateTimeHandlesDifferentFieldEvaluations(string|int|null $value, string|null $storageFormat, string|null $expectedValue): void
+    {
+        $GLOBALS['TCA']['tx_testdatamapper_domain_model_example']['columns']['initialized_date_time_property']['config']['dbType'] = $storageFormat;
+        $rows = [
+            [
+                'uid' => 123,
+                'initialized_date_time_property' => $value,
+            ],
+        ];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+        $mappedObjectsArray = $dataMapper->map(Example::class, $rows);
+
+        self::assertSame($expectedValue, $mappedObjectsArray[0]->getInitializedDateTimeProperty()?->format('c'));
+
+        // Flush DataMapFactory cache on each run.
+        /** @var CacheManager $cacheManager */
+        $cacheManager = $this->get(CacheManager::class);
+        $cacheManager->getCache('extbase')->flush();
+    }
+
+    public function mapDateTimeHandlesDifferentFieldEvaluationsWithTimeZoneDataProvider(): array
+    {
+        return [
+            'nothing' => [null, null, null],
+            'timestamp' => [1, null, '@1'],
+            'invalid date' => ['0000-00-00', 'date', null],
+            'valid date' => ['2013-01-01', 'date', '2013-01-01T00:00:00'],
+            'invalid datetime' => ['0000-00-00 00:00:00', 'datetime', null],
+            'valid datetime' => ['2013-01-01 01:02:03', 'datetime', '2013-01-01T01:02:03'],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider mapDateTimeHandlesDifferentFieldEvaluationsWithTimeZoneDataProvider
+     */
+    public function mapDateTimeHandlesDifferentFieldEvaluationsWithTimeZone(string|int|null $value, ?string $storageFormat, ?string $expectedValue): void
+    {
+        $originalTimeZone = date_default_timezone_get();
+        date_default_timezone_set('America/Chicago');
+        $usedTimeZone = date_default_timezone_get();
+
+        $GLOBALS['TCA']['tx_testdatamapper_domain_model_example']['columns']['initialized_date_time_property']['config']['dbType'] = $storageFormat;
+        $rows = [
+            [
+                'uid' => 123,
+                'initialized_date_time_property' => $value,
+            ],
+        ];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+        $mappedObjectsArray = $dataMapper->map(Example::class, $rows);
+
+        $expectedValue = $expectedValue !== null ? new \DateTime($expectedValue, new \DateTimeZone($usedTimeZone)) : $expectedValue;
+        self::assertEquals($expectedValue, $mappedObjectsArray[0]->getInitializedDateTimeProperty());
+
+        // Flush DataMapFactory cache on each run.
+        /** @var CacheManager $cacheManager */
+        $cacheManager = $this->get(CacheManager::class);
+        $cacheManager->getCache('extbase')->flush();
+
+        // Restore the systems current timezone
+        date_default_timezone_set($originalTimeZone);
+    }
+
+    /**
+     * @test
+     */
+    public function mapDateTimeHandlesSubclassesOfDateTime(): void
+    {
+        $rows = [
+            [
+                'uid' => 123,
+                'custom_date_time' => '2013-01-01 01:02:03',
+            ],
+        ];
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+        $mappedObjectsArray = $dataMapper->map(Example::class, $rows);
+
+        self::assertInstanceOf(CustomDateTime::class, $mappedObjectsArray[0]->getCustomDateTime());
+    }
+
+    /**
+     * @test
+     */
+    public function getPlainValueReturnsCorrectDateTimeFormat(): void
+    {
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+        $columnMapDateTime = new ColumnMap('column_name', 'propertyName');
+        $columnMapDateTime->setDateTimeStorageFormat('datetime');
+        $columnMapDate = new ColumnMap('column_name', 'propertyName');
+        $columnMapDate->setDateTimeStorageFormat('date');
+        $input = new \DateTime('2013-04-15 09:30:00');
+
+        $plainValueDateTime = $dataMapper->getPlainValue($input, $columnMapDateTime);
+        $plainValueDate = $dataMapper->getPlainValue($input, $columnMapDate);
+
+        self::assertSame('2013-04-15 09:30:00', $plainValueDateTime);
+        self::assertSame('2013-04-15', $plainValueDate);
+    }
+
+    public function getPlainValueReturnsExpectedValuesDataProvider(): array
+    {
+        return [
+            'datetime to timestamp' => ['1365866253', new \DateTime('@1365866253')],
+            'boolean true to 1' => [1, true],
+            'boolean false to 0' => [0, false],
+            'NULL is handled as string' => ['NULL', null],
+            'string value is returned unchanged' => ['RANDOM string', 'RANDOM string'],
+            'array is flattened' => ['a,b,c', ['a', 'b', 'c']],
+            'deep array is flattened' => ['a,b,c', [['a', 'b'], 'c']],
+            'traversable domain object to identifier' => [1, new TraversableDomainObjectExample()],
+            'integer value is returned unchanged' => [1234, 1234],
+            'float is converted to string' => ['1234.56', 1234.56],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider getPlainValueReturnsExpectedValuesDataProvider
+     */
+    public function getPlainValueReturnsExpectedValues(string|int $expectedValue, mixed $input): void
+    {
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+
+        $plainValue = $dataMapper->getPlainValue($input);
+
+        self::assertSame($expectedValue, $plainValue);
+    }
+
+    /**
+     * @test
+     */
+    public function getPlainValueCallsGetRealInstanceOnInputIfInputIsInstanceOfLazyLoadingProxy(): void
+    {
+        $this->importCSVDataSet('typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/Fixtures/administrators.csv');
+        /** @var DataMapper $dataMapper */
+        $dataMapper = $this->get(DataMapper::class);
+        $rowsBlog = [
+            [
+                'uid' => 1234,
+                'administrator' => 1,
+            ],
+        ];
+        $mappedObjectArray = $dataMapper->map(Blog::class, $rowsBlog);
+
+        $plainValue = $dataMapper->getPlainValue($mappedObjectArray[0]->getAdministrator());
+
+        self::assertSame(1, $plainValue);
+    }
 }
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/Fixtures/administrators.csv b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/Fixtures/administrators.csv
new file mode 100644
index 0000000000000000000000000000000000000000..eacec578f9a03d97b14d057fce622a59c5201d30
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/Fixtures/administrators.csv
@@ -0,0 +1,3 @@
+"fe_users"
+,"uid","pid","tx_extbase_type"
+,1,0,"ExtbaseTeam\BlogExample\Domain\Model\Administrator"
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
deleted file mode 100644
index ea37a5981a99588c0c237bef36b94cc178252e81..0000000000000000000000000000000000000000
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
+++ /dev/null
@@ -1,521 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper;
-
-use PHPUnit\Framework\MockObject\MockObject;
-use Prophecy\PhpUnit\ProphecyTrait;
-use Psr\Container\ContainerInterface;
-use Psr\EventDispatcher\EventDispatcherInterface;
-use TYPO3\CMS\Core\Cache\Frontend\NullFrontend;
-use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
-use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
-use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException;
-use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
-use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
-use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap;
-use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory;
-use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
-use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\Exception\UnknownPropertyTypeException;
-use TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory;
-use TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface;
-use TYPO3\CMS\Extbase\Persistence\Generic\Session;
-use TYPO3\CMS\Extbase\Reflection\ClassSchema;
-use TYPO3\CMS\Extbase\Reflection\ReflectionService;
-use TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper\Fixture\DummyChildEntity;
-use TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper\Fixture\DummyEntity;
-use TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper\Fixture\DummyParentEntity;
-use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
-use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
-
-class DataMapperTest extends UnitTestCase
-{
-    use ProphecyTrait;
-
-    /**
-     * This test does not actually test anything rather than map calls both mocked methods getTargetType and mapSingleRow
-     * while completely ignoring the result of the method.
-     * @todo: Cover this functionality by a functional test
-     *
-     * @test
-     */
-    public function mapMapsArrayToObjectByCallingmapToObject(): void
-    {
-        $rows = [['uid' => '1234']];
-        $object = new \stdClass();
-
-        $dataMapper = $this->getMockBuilder(DataMapper::class)
-            ->setConstructorArgs([
-                $this->createMock(ReflectionService::class),
-                $this->createMock(QueryObjectModelFactory::class),
-                $this->createMock(Session::class),
-                $this->createMock(DataMapFactory::class),
-                $this->createMock(QueryFactoryInterface::class),
-                $this->createMock(EventDispatcherInterface::class),
-            ])
-            ->onlyMethods(['mapSingleRow', 'getTargetType'])
-            ->getMock();
-
-        $dataMapper->method('getTargetType')->willReturnArgument(1);
-        $dataMapper->expects(self::once())->method('mapSingleRow')->with($rows[0])->willReturn($object);
-
-        $dataMapper->map(get_class($object), $rows);
-    }
-
-    /**
-     * This test does not actually test anything rather than mapSingleRow delegates functionality to
-     * the persistence session which is a mock itself.
-     * @todo: Cover this functionality by a functional test
-     *
-     * @test
-     */
-    public function mapSingleRowReturnsObjectFromPersistenceSessionIfAvailable(): void
-    {
-        $row = ['uid' => '1234'];
-        $object = new \stdClass();
-        $persistenceSession = $this->createMock(Session::class);
-        $persistenceSession->expects(self::once())->method('hasIdentifier')->with('1234')->willReturn(true);
-        $persistenceSession->expects(self::once())->method('getObjectByIdentifier')->with('1234')->willReturn($object);
-
-        $dataMapper = $this->getAccessibleMock(
-            DataMapper::class,
-            ['dummy'],
-            [
-                $this->createMock(ReflectionService::class),
-                $this->createMock(QueryObjectModelFactory::class),
-                $persistenceSession,
-                $this->createMock(DataMapFactory::class),
-                $this->createMock(QueryFactoryInterface::class),
-                $this->createMock(EventDispatcherInterface::class),
-            ]
-        );
-
-        $dataMapper->_call('mapSingleRow', get_class($object), $row);
-    }
-
-    /**
-     * This test has a far too complex setup to test a single unit. This actually is a functional test, accomplished
-     * by mocking the whole dependency chain. This test only tests code structure while it should test functionality.
-     * @todo: Cover this functionality by a functional test
-     *
-     * @test
-     */
-    public function thawPropertiesSetsPropertyValues(): void
-    {
-        $className = DummyEntity::class;
-        $object = new DummyEntity();
-        $row = [
-            'uid' => '1234',
-            'firstProperty' => 'firstValue',
-            'secondProperty' => 1234,
-            'thirdProperty' => 1.234,
-            'fourthProperty' => false,
-            'uninitializedStringProperty' => 'foo',
-            'uninitializedDateTimeProperty' => 0,
-            'uninitializedMandatoryDateTimeProperty' => 0,
-            'initializedDateTimeProperty' => 0,
-        ];
-        $columnMaps = [
-            'uid' => new ColumnMap('uid', 'uid'),
-            'pid' => new ColumnMap('pid', 'pid'),
-            'firstProperty' => new ColumnMap('firstProperty', 'firstProperty'),
-            'secondProperty' => new ColumnMap('secondProperty', 'secondProperty'),
-            'thirdProperty' => new ColumnMap('thirdProperty', 'thirdProperty'),
-            'fourthProperty' => new ColumnMap('fourthProperty', 'fourthProperty'),
-            'uninitializedStringProperty' => new ColumnMap('uninitializedStringProperty', 'uninitializedStringProperty'),
-            'uninitializedDateTimeProperty' => new ColumnMap('uninitializedDateTimeProperty', 'uninitializedDateTimeProperty'),
-            'uninitializedMandatoryDateTimeProperty' => new ColumnMap('uninitializedMandatoryDateTimeProperty', 'uninitializedMandatoryDateTimeProperty'),
-            'initializedDateTimeProperty' => new ColumnMap('initializedDateTimeProperty', 'initializedDateTimeProperty'),
-        ];
-        $dataMap = $this->getAccessibleMock(DataMap::class, null, [$className, $className]);
-        $dataMap->_set('columnMaps', $columnMaps);
-        $dataMaps = [
-            $className => $dataMap,
-        ];
-        $classSchema = new ClassSchema($className);
-        $mockReflectionService = $this->getMockBuilder(ReflectionService::class)
-            ->setConstructorArgs([new NullFrontend('extbase'), 'ClassSchemata'])
-            ->onlyMethods(['getClassSchema'])
-            ->getMock();
-        $mockReflectionService->method('getClassSchema')->willReturn($classSchema);
-        $dataMapFactory = $this->getAccessibleMock(DataMapFactory::class, null, [], '', false);
-        $dataMapFactory->_set('dataMaps', $dataMaps);
-        $dataMapper = $this->getAccessibleMock(
-            DataMapper::class,
-            ['dummy'],
-            [
-                $mockReflectionService,
-                $this->createMock(QueryObjectModelFactory::class),
-                $this->createMock(Session::class),
-                $dataMapFactory,
-                $this->createMock(QueryFactoryInterface::class),
-                $this->createMock(EventDispatcherInterface::class),
-            ]
-        );
-        $dataMapper->_call('thawProperties', $object, $row);
-
-        self::assertEquals('firstValue', $object->firstProperty);
-        self::assertEquals(1234, $object->secondProperty);
-        self::assertEquals(1.234, $object->thirdProperty);
-        self::assertFalse($object->fourthProperty);
-        self::assertSame('foo', $object->uninitializedStringProperty);
-        self::assertNull($object->uninitializedDateTimeProperty);
-        self::assertFalse(isset($object->uninitializedMandatoryDateTimeProperty));
-
-        // Property is initialized with "null", so isset would return false.
-        // Test, if property was "really" initialized.
-        $reflectionProperty = new \ReflectionProperty($object, 'initializedDateTimeProperty');
-        self::assertTrue($reflectionProperty->isInitialized($object));
-    }
-
-    /**
-     * @test
-     */
-    public function thawPropertiesThrowsExceptionOnUnknownPropertyType(): void
-    {
-        $className = DummyEntity::class;
-        $object = new DummyEntity();
-        $row = [
-            'uid' => '1234',
-            'unknownType' => 'What am I?',
-        ];
-        $columnMaps = [
-            'unknownType' => new ColumnMap('unknownType', 'unknownType'),
-        ];
-        $dataMap = $this->getAccessibleMock(DataMap::class, null, [$className, $className]);
-        $dataMap->_set('columnMaps', $columnMaps);
-        $dataMaps = [
-            $className => $dataMap,
-        ];
-        $classSchema = new ClassSchema($className);
-        $mockReflectionService = $this->getMockBuilder(ReflectionService::class)
-            ->setConstructorArgs([new NullFrontend('extbase'), 'ClassSchemata'])
-            ->onlyMethods(['getClassSchema'])
-            ->getMock();
-        $mockReflectionService->method('getClassSchema')->willReturn($classSchema);
-        $dataMapFactory = $this->getAccessibleMock(DataMapFactory::class, null, [], '', false);
-        $dataMapFactory->_set('dataMaps', $dataMaps);
-        $dataMapper = $this->getAccessibleMock(
-            DataMapper::class,
-            ['dummy'],
-            [
-                $mockReflectionService,
-                $this->createMock(QueryObjectModelFactory::class),
-                $this->createMock(Session::class),
-                $dataMapFactory,
-                $this->createMock(QueryFactoryInterface::class),
-                $this->createMock(EventDispatcherInterface::class),
-            ]
-        );
-        $this->expectException(UnknownPropertyTypeException::class);
-        $dataMapper->_call('thawProperties', $object, $row);
-    }
-
-    /**
-     * Test if fetchRelatedEager method returns NULL when $fieldValue = '' and relation type == RELATION_HAS_ONE
-     *
-     * This is actually a functional test as it tests multiple units along with a very specific setup of dependencies.
-     * @todo: Cover this functionality by a functional test
-     *
-     * @test
-     */
-    public function fetchRelatedEagerReturnsNullForEmptyRelationHasOne(): void
-    {
-        $columnMap = new ColumnMap('columnName', 'propertyName');
-        $columnMap->setTypeOfRelation(ColumnMap::RELATION_HAS_ONE);
-        $dataMap = $this->getMockBuilder(DataMap::class)
-            ->onlyMethods(['getColumnMap'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $dataMap->method('getColumnMap')->willReturn($columnMap);
-        $dataMapper = $this->getAccessibleMock(DataMapper::class, ['getDataMap'], [], '', false);
-        $dataMapper->method('getDataMap')->willReturn($dataMap);
-        $result = $dataMapper->_call('fetchRelatedEager', $this->createMock(AbstractEntity::class), 'SomeName', '');
-        self::assertNull($result);
-    }
-
-    /**
-     * Test if fetchRelatedEager method returns empty array when $fieldValue = '' and relation type != RELATION_HAS_ONE
-     *
-     * This is actually a functional test as it tests multiple units along with a very specific setup of dependencies.
-     * @todo: Cover this functionality by a functional test
-     *
-     * @test
-     */
-    public function fetchRelatedEagerReturnsEmptyArrayForEmptyRelationNotHasOne(): void
-    {
-        $columnMap = new ColumnMap('columnName', 'propertyName');
-        $columnMap->setTypeOfRelation(ColumnMap::RELATION_BELONGS_TO_MANY);
-        $dataMap = $this->getMockBuilder(DataMap::class)
-            ->onlyMethods(['getColumnMap'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $dataMap->method('getColumnMap')->willReturn($columnMap);
-        $dataMapper = $this->getAccessibleMock(DataMapper::class, ['getDataMap'], [], '', false);
-        $dataMapper->method('getDataMap')->willReturn($dataMap);
-        $result = $dataMapper->_call('fetchRelatedEager', $this->createMock(AbstractEntity::class), 'SomeName', '');
-        self::assertEquals([], $result);
-    }
-
-    /**
-     * Test if fetchRelatedEager method returns NULL when $fieldValue = ''
-     * and relation type == RELATION_HAS_ONE without calling fetchRelated
-     *
-     * This is actually a functional test as it tests multiple units along with a very specific setup of dependencies.
-     * @todo: Cover this functionality by a functional test
-     *
-     * @test
-     */
-    public function MapObjectToClassPropertyReturnsNullForEmptyRelationHasOne(): void
-    {
-        $columnMap = new ColumnMap('columnName', 'propertyName');
-        $columnMap->setTypeOfRelation(ColumnMap::RELATION_HAS_ONE);
-        $dataMap = $this->getMockBuilder(DataMap::class)
-            ->onlyMethods(['getColumnMap'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $dataMap->method('getColumnMap')->willReturn($columnMap);
-        $dataMapper = $this->getAccessibleMock(DataMapper::class, ['getDataMap', 'fetchRelated'], [], '', false);
-        $dataMapper->method('getDataMap')->willReturn($dataMap);
-        $dataMapper->expects(self::never())->method('fetchRelated');
-        $result = $dataMapper->_call('mapObjectToClassProperty', $this->createMock(AbstractEntity::class), 'SomeName', '');
-        self::assertNull($result);
-    }
-
-    /**
-     * Test if mapObjectToClassProperty method returns objects
-     * that are already registered in the persistence session
-     * without query it from the persistence layer
-     *
-     * This is actually a functional test as it tests multiple units along with a very specific setup of dependencies.
-     * @todo: Cover this functionality by a functional test
-     *
-     * @test
-     */
-    public function mapObjectToClassPropertyReturnsExistingObjectWithoutCallingFetchRelated(): void
-    {
-        $columnMap = new ColumnMap('columnName', 'propertyName');
-        $columnMap->setTypeOfRelation(ColumnMap::RELATION_HAS_ONE);
-        $dataMap = $this->getMockBuilder(DataMap::class)
-            ->onlyMethods(['getColumnMap'])
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $object = new DummyParentEntity();
-        $child = new DummyChildEntity();
-
-        $classSchema1 = new ClassSchema(DummyParentEntity::class);
-        $identifier = 1;
-
-        $psrContainer = $this->getMockBuilder(ContainerInterface::class)
-            ->onlyMethods(['has', 'get'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $psrContainer->method('has')->willReturn(false);
-
-        $session = new Session();
-        $session->registerObject($child, $identifier);
-
-        $mockReflectionService = $this->getMockBuilder(ReflectionService::class)
-            ->onlyMethods(['getClassSchema'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $mockReflectionService->method('getClassSchema')->willReturn($classSchema1);
-
-        $dataMap->method('getColumnMap')->willReturn($columnMap);
-
-        $dataMapper = $this->getAccessibleMock(
-            DataMapper::class,
-            ['getDataMap', 'getNonEmptyRelationValue'],
-            [
-                $mockReflectionService,
-                $this->createMock(QueryObjectModelFactory::class),
-                $session,
-                $this->createMock(DataMapFactory::class),
-                $this->createMock(QueryFactoryInterface::class),
-                $this->createMock(EventDispatcherInterface::class),
-            ]
-        );
-        $dataMapper->method('getDataMap')->willReturn($dataMap);
-        $dataMapper->expects(self::never())->method('getNonEmptyRelationValue');
-        $result = $dataMapper->_call('mapObjectToClassProperty', $object, 'relationProperty', $identifier);
-        self::assertEquals($child, $result);
-    }
-
-    /**
-     * Data provider for date checks. Date will be stored based on UTC in
-     * the database. That's why it's not possible to check for explicit date
-     * strings but using the date('c') conversion instead, which considers the
-     * current local timezone setting.
-     */
-    public function mapDateTimeHandlesDifferentFieldEvaluationsDataProvider(): array
-    {
-        return [
-            'nothing' => [null, null, null],
-            'timestamp' => [1, null, date('c', 1)],
-            'invalid date' => ['0000-00-00', 'date', null],
-            'valid date' => ['2013-01-01', 'date', date('c', strtotime('2013-01-01 00:00:00'))],
-            'invalid datetime' => ['0000-00-00 00:00:00', 'datetime', null],
-            'valid datetime' => ['2013-01-01 01:02:03', 'datetime', date('c', strtotime('2013-01-01 01:02:03'))],
-        ];
-    }
-
-    /**
-     * @param string|int|null $value
-     * @param string|null $storageFormat
-     * @param string|null $expectedValue
-     * @test
-     * @dataProvider mapDateTimeHandlesDifferentFieldEvaluationsDataProvider
-     */
-    public function mapDateTimeHandlesDifferentFieldEvaluations($value, $storageFormat, $expectedValue): void
-    {
-        $accessibleDataMapFactory = $this->getAccessibleMock(DataMapper::class, null, [], '', false);
-
-        $dateTime = $accessibleDataMapFactory->_call('mapDateTime', $value, $storageFormat);
-
-        if ($expectedValue === null) {
-            self::assertNull($dateTime);
-        } else {
-            self::assertEquals($expectedValue, $dateTime->format('c'));
-        }
-    }
-
-    public function mapDateTimeHandlesDifferentFieldEvaluationsWithTimeZoneDataProvider(): array
-    {
-        return [
-            'nothing' => [null, null, null],
-            'timestamp' => [1, null, '@1'],
-            'invalid date' => ['0000-00-00', 'date', null],
-            'valid date' => ['2013-01-01', 'date', '2013-01-01T00:00:00'],
-            'invalid datetime' => ['0000-00-00 00:00:00', 'datetime', null],
-            'valid datetime' => ['2013-01-01 01:02:03', 'datetime', '2013-01-01T01:02:03'],
-        ];
-    }
-
-    /**
-     * @param string|int|null $value
-     * @test
-     * @dataProvider mapDateTimeHandlesDifferentFieldEvaluationsWithTimeZoneDataProvider
-     */
-    public function mapDateTimeHandlesDifferentFieldEvaluationsWithTimeZone($value, ?string $storageFormat, ?string $expectedValue): void
-    {
-        $originalTimeZone = date_default_timezone_get();
-        date_default_timezone_set('America/Chicago');
-        $usedTimeZone = date_default_timezone_get();
-        $accessibleDataMapFactory = $this->getAccessibleMock(DataMapper::class, null, [], '', false);
-
-        /** @var \DateTime|MockObject|AccessibleObjectInterface $dateTime */
-        $dateTime = $accessibleDataMapFactory->_call('mapDateTime', $value, $storageFormat);
-
-        if ($expectedValue === null) {
-            self::assertNull($dateTime);
-        } else {
-            self::assertEquals(new \DateTime($expectedValue, new \DateTimeZone($usedTimeZone)), $dateTime);
-        }
-        // Restore the systems current timezone
-        date_default_timezone_set($originalTimeZone);
-    }
-
-    /**
-     * @test
-     */
-    public function mapDateTimeHandlesSubclassesOfDateTime(): void
-    {
-        $accessibleDataMapFactory = $this->getAccessibleMock(DataMapper::class, null, [], '', false);
-        $targetType = 'TYPO3\CMS\Extbase\Tests\Unit\Persistence\Fixture\Model\CustomDateTime';
-        $date = '2013-01-01 01:02:03';
-        $storageFormat = 'datetime';
-
-        /** @var \DateTime|MockObject|AccessibleObjectInterface $dateTime */
-        $dateTime = $accessibleDataMapFactory->_call('mapDateTime', $date, $storageFormat, $targetType);
-
-        self::assertInstanceOf($targetType, $dateTime);
-    }
-
-    /**
-     * @test
-     */
-    public function getPlainValueReturnsCorrectDateTimeFormat(): void
-    {
-        $subject = $this->createPartialMock(DataMapper::class, []);
-
-        $columnMap = new ColumnMap('column_name', 'propertyName');
-        $columnMap->setDateTimeStorageFormat('datetime');
-        $input = new \DateTime('2013-04-15 09:30:00');
-        self::assertEquals('2013-04-15 09:30:00', $subject->getPlainValue($input, $columnMap));
-        $columnMap->setDateTimeStorageFormat('date');
-        self::assertEquals('2013-04-15', $subject->getPlainValue($input, $columnMap));
-    }
-
-    /**
-     * @test
-     * @dataProvider getPlainValueReturnsExpectedValuesDataProvider
-     */
-    public function getPlainValueReturnsExpectedValues($expectedValue, $input): void
-    {
-        $dataMapper = $this->createPartialMock(DataMapper::class, []);
-
-        self::assertSame($expectedValue, $dataMapper->getPlainValue($input));
-    }
-
-    public function getPlainValueReturnsExpectedValuesDataProvider(): array
-    {
-        $traversableDomainObject = $this->prophesize()
-            ->willImplement(\Iterator::class)
-            ->willImplement(DomainObjectInterface::class);
-        $traversableDomainObject->getUid()->willReturn(1);
-
-        return [
-            'datetime to timestamp' => ['1365866253', new \DateTime('@1365866253')],
-            'boolean true to 1' => [1, true],
-            'boolean false to 0' => [0, false],
-            'NULL is handled as string' => ['NULL', null],
-            'string value is returned unchanged' => ['RANDOM string', 'RANDOM string'],
-            'array is flattened' => ['a,b,c', ['a', 'b', 'c']],
-            'deep array is flattened' => ['a,b,c', [['a', 'b'], 'c']],
-            'traversable domain object to identifier' => [1, $traversableDomainObject->reveal()],
-            'integer value is returned unchanged' => [1234, 1234],
-            'float is converted to string' => ['1234.56', 1234.56],
-        ];
-    }
-
-    /**
-     * @test
-     */
-    public function getPlainValueCallsGetRealInstanceOnInputIfInputIsInstanceOfLazyLoadingProxy(): void
-    {
-        $this->expectException(UnexpectedTypeException::class);
-        $this->expectExceptionCode(1274799934);
-
-        $dataMapper = $this->createPartialMock(DataMapper::class, []);
-        $input = $this->createMock(LazyLoadingProxy::class);
-        $input->expects(self::once())->method('_loadRealInstance')->willReturn($dataMapper);
-        $dataMapper->getPlainValue($input);
-    }
-
-    /**
-     * @test
-     */
-    public function getPlainValueCallsGetUidOnDomainObjectInterfaceInput(): void
-    {
-        $dataMapper = $this->createPartialMock(DataMapper::class, []);
-        $input = $this->createMock(DomainObjectInterface::class);
-
-        $input->expects(self::once())->method('getUid')->willReturn(23);
-        self::assertSame(23, $dataMapper->getPlainValue($input));
-    }
-}
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyEntity.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyEntity.php
deleted file mode 100644
index 0526dcb313312f1900e390b97d7ed60b3147e502..0000000000000000000000000000000000000000
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyEntity.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper\Fixture;
-
-use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
-
-/**
- * Fixture
- * @todo Specifying the property types results in test bench errors
- */
-class DummyEntity extends AbstractEntity
-{
-    /**
-     * @var string
-     */
-    public $firstProperty;
-
-    /**
-     * @var int
-     */
-    public $secondProperty;
-
-    /**
-     * @var float
-     */
-    public $thirdProperty;
-
-    /**
-     * @var bool
-     */
-    public $fourthProperty;
-
-    /**
-     * no var here :(
-     */
-    public $unknownType;
-
-    public string $uninitializedStringProperty;
-
-    public ?\DateTime $uninitializedDateTimeProperty;
-
-    public \DateTime $uninitializedMandatoryDateTimeProperty;
-
-    public ?\DateTime $initializedDateTimeProperty = null;
-}
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyParentEntity.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyParentEntity.php
deleted file mode 100644
index 1f6d4d1354defcfe0b9ea5d1bb7f5831403fd0b1..0000000000000000000000000000000000000000
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/Fixture/DummyParentEntity.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper\Fixture;
-
-use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
-
-/**
- * Fixture
- */
-class DummyParentEntity extends AbstractEntity
-{
-    public DummyChildEntity $relationProperty;
-}