diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
index c802e1cc2da352ef23a09e07be997d904c722297..37e6afcde77ac317099e154016a994ee075f6093 100644
--- a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
+++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
@@ -17,10 +17,12 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Storage;
 use Doctrine\DBAL\DBALException;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
+use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
 use TYPO3\CMS\Extbase\Persistence\Generic\Qom;
 use TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\SqlErrorException;
 use TYPO3\CMS\Extbase\Persistence\QueryInterface;
@@ -523,166 +525,51 @@ class Typo3DbBackend implements BackendInterface, SingletonInterface
     }
 
     /**
-     * Checks if a Value Object equal to the given Object exists in the data base
+     * Checks if a Value Object equal to the given Object exists in the database
      *
-     * @param \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object The Value Object
+     * @param AbstractValueObject $object The Value Object
      * @return mixed The matching uid if an object was found, else FALSE
-     * @todo this is the last monster in this persistence series. refactor!
+     * @throws SqlErrorException
      */
-    public function getUidOfAlreadyPersistedValueObject(\TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object)
+    public function getUidOfAlreadyPersistedValueObject(AbstractValueObject $object)
     {
-        $fields = array();
-        $parameters = array();
         $dataMap = $this->dataMapper->getDataMap(get_class($object));
+        $tableName = $dataMap->getTableName();
+        $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
+        if ($this->environmentService->isEnvironmentInFrontendMode()) {
+            $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+        }
+        $whereClause = [];
+        // loop over all properties of the object to exactly set the values of each database field
         $properties = $object->_getProperties();
         foreach ($properties as $propertyName => $propertyValue) {
             // @todo We couple the Backend to the Entity implementation (uid, isClone); changes there breaks this method
             if ($dataMap->isPersistableProperty($propertyName) && $propertyName !== 'uid' && $propertyName !== 'pid' && $propertyName !== 'isClone') {
+                $fieldName = $dataMap->getColumnMap($propertyName)->getColumnName();
                 if ($propertyValue === null) {
-                    $fields[] = $dataMap->getColumnMap($propertyName)->getColumnName() . ' IS NULL';
+                    $whereClause[] = $queryBuilder->expr()->isNull($fieldName);
                 } else {
-                    $fields[] = $dataMap->getColumnMap($propertyName)->getColumnName() . '=?';
-                    $parameters[] = $this->dataMapper->getPlainValue($propertyValue);
+                    $whereClause[] = $queryBuilder->expr()->eq($fieldName, $queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($propertyValue)));
                 }
             }
         }
-        $sql = array();
-        $sql['additionalWhereClause'] = array();
-        $tableName = $dataMap->getTableName();
-        $this->addVisibilityConstraintStatement(new \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings(), $tableName, $sql);
-        $statement = 'SELECT * FROM ' . $tableName;
-        $statement .= ' WHERE ' . implode(' AND ', $fields);
-        if (!empty($sql['additionalWhereClause'])) {
-            $statement .= ' AND ' . implode(' AND ', $sql['additionalWhereClause']);
-        }
-        $this->replacePlaceholders($statement, $parameters, $tableName);
-        // debug($statement,-2);
-        $res = $this->databaseHandle->sql_query($statement);
-        $this->checkSqlErrors($statement);
-        $row = $this->databaseHandle->sql_fetch_assoc($res);
-        if ($row !== false) {
-            return (int)$row['uid'];
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Replace query placeholders in a query part by the given
-     * parameters.
-     *
-     * @param string &$sqlString The query part with placeholders
-     * @param array $parameters The parameters
-     * @param string $tableName
-     *
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception
-     * @deprecated since 6.2, will be removed two versions later
-     * @todo add deprecation notice after getUidOfAlreadyPersistedValueObject is adjusted
-     */
-    protected function replacePlaceholders(&$sqlString, array $parameters, $tableName = 'foo')
-    {
-        // @todo profile this method again
-        if (substr_count($sqlString, '?') !== count($parameters)) {
-            throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception('The number of question marks to replace must be equal to the number of parameters.', 1460975513);
-        }
-        $offset = 0;
-        foreach ($parameters as $parameter) {
-            $markPosition = strpos($sqlString, '?', $offset);
-            if ($markPosition !== false) {
-                if ($parameter === null) {
-                    $parameter = 'NULL';
-                } elseif (is_array($parameter) || $parameter instanceof \ArrayAccess || $parameter instanceof \Traversable) {
-                    $items = array();
-                    foreach ($parameter as $item) {
-                        $items[] = $this->databaseHandle->fullQuoteStr($item, $tableName);
-                    }
-                    $parameter = '(' . implode(',', $items) . ')';
-                } else {
-                    $parameter = $this->databaseHandle->fullQuoteStr($parameter, $tableName);
-                }
-                $sqlString = substr($sqlString, 0, $markPosition) . $parameter . substr($sqlString, ($markPosition + 1));
-            }
-            $offset = $markPosition + strlen($parameter);
-        }
-    }
-
-    /**
-     * Adds enableFields and deletedClause to the query if necessary
-     *
-     * @param \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings
-     * @param string $tableName The database table name
-     * @param array &$sql The query parts
-     * @return void
-     * @todo remove after getUidOfAlreadyPersistedValueObject is adjusted, this was moved to queryParser
-     */
-    protected function addVisibilityConstraintStatement(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings, $tableName, array &$sql)
-    {
-        $statement = '';
-        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
-            $ignoreEnableFields = $querySettings->getIgnoreEnableFields();
-            $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
-            $includeDeleted = $querySettings->getIncludeDeleted();
-            if ($this->environmentService->isEnvironmentInFrontendMode()) {
-                $statement .= $this->getFrontendConstraintStatement($tableName, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
-            } else {
-                // TYPO3_MODE === 'BE'
-                $statement .= $this->getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted);
-            }
-            if (!empty($statement)) {
-                $statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
-                $sql['additionalWhereClause'][] = $statement;
-            }
-        }
-    }
+        $queryBuilder
+            ->select('uid')
+            ->from($tableName)
+            ->where(...$whereClause);
 
-    /**
-     * Returns constraint statement for frontend context
-     *
-     * @param string $tableName
-     * @param bool $ignoreEnableFields A flag indicating whether the enable fields should be ignored
-     * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is NULL or an empty array (default) all enable fields are ignored.
-     * @param bool $includeDeleted A flag indicating whether deleted records should be included
-     * @return string
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InconsistentQuerySettingsException
-     * @todo remove after getUidOfAlreadyPersistedValueObject is adjusted, this was moved to queryParser
-     */
-    protected function getFrontendConstraintStatement($tableName, $ignoreEnableFields, array $enableFieldsToBeIgnored = array(), $includeDeleted)
-    {
-        $statement = '';
-        if ($ignoreEnableFields && !$includeDeleted) {
-            if (!empty($enableFieldsToBeIgnored)) {
-                // array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
-                $statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
+        try {
+            $uid = (int)$queryBuilder
+                ->execute()
+                ->fetchColumn(0);
+            if ($uid > 0) {
+                return $uid;
             } else {
-                $statement .= $this->getPageRepository()->deleteClause($tableName);
+                return false;
             }
-        } elseif (!$ignoreEnableFields && !$includeDeleted) {
-            $statement .= $this->getPageRepository()->enableFields($tableName);
-        } elseif (!$ignoreEnableFields && $includeDeleted) {
-            throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InconsistentQuerySettingsException('Query setting "ignoreEnableFields=FALSE" can not be used together with "includeDeleted=TRUE" in frontend context.', 1327678173);
-        }
-        return $statement;
-    }
-
-    /**
-     * Returns constraint statement for backend context
-     *
-     * @param string $tableName
-     * @param bool $ignoreEnableFields A flag indicating whether the enable fields should be ignored
-     * @param bool $includeDeleted A flag indicating whether deleted records should be included
-     * @return string
-     * @todo remove after getUidOfAlreadyPersistedValueObject is adjusted, this was moved to queryParser
-     */
-    protected function getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted)
-    {
-        $statement = '';
-        if (!$ignoreEnableFields) {
-            $statement .= BackendUtility::BEenableFields($tableName);
-        }
-        if (!$includeDeleted) {
-            $statement .= BackendUtility::deleteClause($tableName);
+        } catch (DBALException $e) {
+            throw new SqlErrorException($e->getPrevious()->getMessage(), 1470231748);
         }
-        return $statement;
     }
 
     /**
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbBackendTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbBackendTest.php
index d7fd8d5a554c463252c7afa5dc1480c14d76b00c..a961bf44044286890e928a3e2b2c2ae16e4f17fe 100644
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbBackendTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbBackendTest.php
@@ -14,11 +14,18 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Storage;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Doctrine\DBAL\Driver\Statement;
+use Prophecy\Argument;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
+use TYPO3\CMS\Core\Database\Query\QueryBuilder;
+use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
 use TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface;
 use TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbBackend;
 use TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser;
 use TYPO3\CMS\Extbase\Persistence\QueryInterface;
+use TYPO3\CMS\Extbase\Service\EnvironmentService;
 
 /**
  * Test case
@@ -30,6 +37,13 @@ class Typo3DbBackendTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
      */
     protected static $dataMapper;
 
+    public function setUp()
+    {
+        parent::setUp();
+        $GLOBALS['TSFE'] = new \stdClass();
+        $GLOBALS['TSFE']->gr_list = '';
+    }
+
     /**
      * Setup DataMapper
      */
@@ -38,16 +52,29 @@ class Typo3DbBackendTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         self::$dataMapper = new DataMapper();
     }
 
+    /**
+     * @return array
+     */
+    public function uidOfAlreadyPersistedValueObjectIsDeterminedCorrectlyDataProvider(): array
+    {
+        return [
+            'isFrontendEnvironment' => [true],
+            'isBackendEnvironment' => [false],
+        ];
+    }
+
     /**
      * @test
+     * @dataProvider uidOfAlreadyPersistedValueObjectIsDeterminedCorrectlyDataProvider
      */
-    public function uidOfAlreadyPersistedValueObjectIsDeterminedCorrectly()
+    public function uidOfAlreadyPersistedValueObjectIsDeterminedCorrectly(bool $isFrontendEnvironment)
     {
         $mockValueObject = $this->getMockBuilder(\TYPO3\CMS\Extbase\DomainObject\AbstractValueObject::class)
             ->setMethods(array('_getProperties'))
             ->disableOriginalConstructor()
             ->getMock();
-        $mockValueObject->expects($this->once())->method('_getProperties')->will($this->returnValue(array('propertyName' => 'propertyValue')));
+        $mockValueObject->expects($this->once())->method('_getProperties')
+            ->will($this->returnValue(['propertyName' => 'propertyValue']));
         $mockColumnMap = $this->getMockBuilder(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap::class)
             ->setMethods(array('isPersistableProperty', 'getColumnName'))
             ->disableOriginalConstructor()
@@ -65,22 +92,44 @@ class Typo3DbBackendTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
             ->setMethods(array('getDataMap', 'getPlainValue'))
             ->disableOriginalConstructor()
             ->getMock();
-        $mockDataMapper->expects($this->once())->method('getDataMap')->will($this->returnValue($mockDataMap));
-        $mockDataMapper->expects($this->once())->method('getPlainValue')->will($this->returnValue('plainPropertyValue'));
-        $expectedStatement = 'SELECT * FROM tx_foo_table WHERE column_name=?';
-        $expectedParameters = array('plainPropertyValue');
+        $mockDataMapper->expects($this->once())->method('getDataMap')
+            ->will($this->returnValue($mockDataMap));
+        $mockDataMapper->expects($this->once())->method('getPlainValue')
+            ->will($this->returnValue('plainPropertyValue'));
         $expectedUid = 52;
-        $mockDataBaseHandle = $this->getMockBuilder(\TYPO3\CMS\Core\Database\DatabaseConnection::class)
-            ->setMethods(array('sql_query', 'sql_fetch_assoc'))
-            ->disableOriginalConstructor()
-            ->getMock();
-        $mockDataBaseHandle->expects($this->once())->method('sql_query')->will($this->returnValue('resource'));
-        $mockDataBaseHandle->expects($this->any())->method('sql_fetch_assoc')->with('resource')->will($this->returnValue(array('uid' => $expectedUid)));
-        $mockTypo3DbBackend = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbBackend::class, array('checkSqlErrors', 'replacePlaceholders', 'addVisibilityConstraintStatement'), array(), '', false);
-        $mockTypo3DbBackend->expects($this->once())->method('addVisibilityConstraintStatement')->with($this->isInstanceOf(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface::class), $tableName, $this->isType('array'));
-        $mockTypo3DbBackend->expects($this->once())->method('replacePlaceholders')->with($expectedStatement, $expectedParameters);
+
+        $expressionBuilderProphet = $this->prophesize(ExpressionBuilder::class);
+        $expressionBuilderProphet->eq(Argument::cetera())->willReturn('1 = 1');
+        $queryResultProphet = $this->prophesize(Statement::class);
+        $queryResultProphet->fetchColumn(Argument::cetera())->willReturn($expectedUid);
+        $queryBuilderProphet = $this->prophesize(QueryBuilder::class);
+        $queryBuilderProphet->execute()->willReturn($queryResultProphet->reveal());
+        $queryBuilderProphet->expr()->willReturn($expressionBuilderProphet->reveal());
+        $queryBuilderProphet->createNamedParameter(Argument::cetera())->willReturnArgument(0);
+        $queryBuilderProphet->select('uid')->willReturn($queryBuilderProphet->reveal());
+        $queryBuilderProphet->from($tableName)->willReturn($queryBuilderProphet->reveal());
+        $queryBuilderProphet->where(Argument::cetera())->willReturn($queryBuilderProphet->reveal());
+        $connectionPoolProphet = $this->prophesize(ConnectionPool::class);
+        $connectionPoolProphet->getQueryBuilderForTable(Argument::cetera())->willReturn($queryBuilderProphet->reveal());
+
+        $environmentServiceProphet = $this->prophesize(EnvironmentService::class);
+        $environmentServiceProphet->isEnvironmentInFrontendMode()->willReturn($isFrontendEnvironment);
+
+        if ($isFrontendEnvironment) {
+            $queryBuilderProphet->setRestrictions(Argument::type(FrontendRestrictionContainer::class))
+                ->shouldBeCalled();
+        }
+
+        $mockTypo3DbBackend = $this->getAccessibleMock(
+            \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbBackend::class,
+            ['dummy'],
+            [],
+            '',
+            false
+        );
         $mockTypo3DbBackend->_set('dataMapper', $mockDataMapper);
-        $mockTypo3DbBackend->_set('databaseHandle', $mockDataBaseHandle);
+        $mockTypo3DbBackend->_set('connectionPool', $connectionPoolProphet->reveal());
+        $mockTypo3DbBackend->_set('environmentService', $environmentServiceProphet->reveal());
         $result = $mockTypo3DbBackend->_callRef('getUidOfAlreadyPersistedValueObject', $mockValueObject);
         $this->assertSame($expectedUid, $result);
     }