---
title: "Breaking: #96287 - Doctrine DBAL v3"
manual: "TYPO3 Core Changelog"
version: "main"
permalink: "https://docs.typo3.org/permalink/changelog:breaking-96287"
source: "Changelog/12.0/Breaking-96287-DoctrineDBALv3.rst"
typo3-version: "12.0"
typo3-major: 12
type: "breaking"
issue: 96287
forge: "https://forge.typo3.org/issues/96287"
tags: ["Database", "NotScanned", "ext:core"]
rendered: "2026-09-17T21:17:42+00:00"
---

# Breaking: #96287 - Doctrine DBAL v3 {#breaking-96287-doctrine-dbal-v3}

See [forge#96287](https://forge.typo3.org/issues/96287)

## Description {#description}

TYPO3 v12.0 has updated its Database Abstraction package based on Doctrine
DBAL to the next major version Doctrine DBAL v3.

## Impact {#impact}

Doctrine DBAL 3 has undergone major refactorings internally by separating
Doctrine's internal driver logic from PHP's native PDO functionality.

See [https://www.doctrine-project.org/2021/03/29/dbal-2.13.html](https://www.doctrine-project.org/2021/03/29/dbal-2.13.html) and
[https://www.doctrine-project.org/2020/11/17/dbal-3.0.0.html](https://www.doctrine-project.org/2020/11/17/dbal-3.0.0.html)
for more details.

In addition, most database APIs which TYPO3 provides as wrappers around
the existing functionality is already available in TYPO3 v11 and
continue to work in TYPO3 v12.

## Affected Installations {#affected-installations}

TYPO3 installations with custom third-party extensions using TYPO3's
Database Abstraction functionality, or extensions using
the Doctrine DBAL API directly.

## Migration {#migration}

Read Doctrine's migration paths (see links above) to migrate any existing
code.

The main change for 95% of the developers are, that queries and database result-sets
now have more explicit APIs when querying the database.

Examples:

```php
$result = $queryBuilder
  ->select(...)
  ->from(...)
  // use executeQuery() instead of execute()
  ->executeQuery();
```

`$result` is now of type `\Doctrine\DBAL\Result`, and not of type
`\Doctrine\DBAL\Statement` anymore, which allows to fetch rows / columns via
new and more speaking methods:

-   `->fetchAllAssociative()` instead of `->fetchAll()`
-   `->fetchAssociative()` \- instead of `->fetch()`
-   `->fetchOne()` \- instead of `->fetchColumn(0)`

The method `executeQuery` \- available in the QueryBuilder and
the Connection class is now in for select/count queries and returns a Result
object directly, whereas `executeStatement()` is used for insert / update / delete
statements, returning an integer - the number of affected rows.

Use both methods instead of the previous `execute()` method,
which is still available for backwards-compatibility.
