---
title: "Connection"
manual: "TYPO3 Explained"
version: "12.4"
permalink: "https://docs.typo3.org/permalink/t3coreapi:database-connection@12.4"
source: "ApiOverview/Database/Connection/Index.rst"
rendered: "2026-10-21T15:29:37+00:00"
---

> [!WARNING]
> **Attention**
>
> TYPO3 v12 has reached end-of-life as of April 30th 2026 and
> is no longer being maintained. Use the version switcher on the top
> left of this page to select documentation for a supported version of TYPO3.
>
> Need more time before upgrading?
> You can purchase Extended Long Term Support (ELTS) for TYPO3 v12 here:
> [TYPO3 ELTS](https://typo3.com/services/extended-support-elts).

# Connection {#database-connection}

****Table of Contents****

-   [Introduction](https://docs.typo3.org/permalink/t3coreapi:introduction@12.4)
-   [Instantiation](https://docs.typo3.org/permalink/t3coreapi:instantiation@12.4)
-   [Parameter types](https://docs.typo3.org/permalink/t3coreapi:parameter-types@12.4)
-   [insert()](https://docs.typo3.org/permalink/t3coreapi:insert@12.4)
-   [bulkInsert()](https://docs.typo3.org/permalink/t3coreapi:bulkinsert@12.4)
-   [update()](https://docs.typo3.org/permalink/t3coreapi:update@12.4)
-   [delete()](https://docs.typo3.org/permalink/t3coreapi:delete@12.4)
-   [truncate()](https://docs.typo3.org/permalink/t3coreapi:truncate@12.4)
-   [count()](https://docs.typo3.org/permalink/t3coreapi:count@12.4)
-   [select()](https://docs.typo3.org/permalink/t3coreapi:select@12.4)
-   [lastInsertId()](https://docs.typo3.org/permalink/t3coreapi:lastinsertid@12.4)
-   [createQueryBuilder()](https://docs.typo3.org/permalink/t3coreapi:createquerybuilder@12.4)
-   [Native JSON database field type support](https://docs.typo3.org/permalink/t3coreapi:native-json-database-field-type-support@12.4)

## Introduction {#database-connection-introduction}

The `\TYPO3\CMS\Core\Database\Connection` class extends the basic Doctrine
DBAL `\Doctrine\DBAL\Connection` class and is mainly used internally in
TYPO3 to establish, maintain and terminate connections to single database
endpoints. These internal methods are not the scope of this documentation, since
an extension developer usually does not have to deal with them.

However, for an extension developer, the class provides a list of short-hand
methods that allow you to deal with query cases without the complexity
of the [query builder](https://docs.typo3.org/permalink/t3coreapi:database-query-builder@12.4). Using these methods
usually ends up in rather short and easy-to-read code. The methods have in common
that they only support "equal" comparisons in `WHERE` conditions, that all
fields and values are automatically fully quoted, and that the created queries
are executed right away.

> [!NOTE]
> The `Connection` object is designed to work on a single table only. If
> queries are performed on multiple tables, the object must not be reused.
> Instead, a single `Connection` instance per target table should be
> retrieved via [ConnectionPool](https://docs.typo3.org/permalink/t3coreapi:database-connection-pool@12.4). However, it
> is allowed to use one `Connection` object for multiple queries on the
> same table.

## Instantiation {#database-connection-instantiation}

### Using the connection pool {#using-the-connection-pool}

An instance of the `\TYPO3\CMS\Core\Database\Connection` class is retrieved
from the [ConnectionPool](https://docs.typo3.org/permalink/t3coreapi:database-connection-pool@12.4) by calling
`->getConnectionForTable()` and passing the table name for which a query
should be executed. The `ConnectionPool` can be injected via constructor:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_domain_model_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function findSomething()
    {
        $connection = $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME);
    }
}

```

### Via dependency injection {#via-dependency-injection}

Another way is to inject the `Connection` object directly via
[dependency injection](https://docs.typo3.org/permalink/t3coreapi:dependencyinjection@12.4) if you only use one table.

1.  Configure the concrete connection as a service

    To make a concrete `Connection` object available as a service, use
    the factory option in the service configuration:

    **EXT:my_extension/Configuration/Services.yaml**

    ```yaml
    services:
      _defaults:
        autowire: true
        autoconfigure: true
        public: false

      MyVendor\MyExtension\:
        resource: '../Classes/*'

      connection.tx_myextension_domain_model_mytable:
        class: 'TYPO3\CMS\Core\Database\Connection'
        factory: ['@TYPO3\CMS\Core\Database\ConnectionPool', 'getConnectionForTable']
        arguments:
          - 'tx_myextension_domain_model_mytable'

      MyVendor\MyExtension\Domain\Repository\MyTableRepository:
        arguments:
          - '@connection.tx_myextension_domain_model_mytable'

    ```
1.  Use constructor injection in your class

    Now the `Connection` object for a specific table can be injected via
    the constructor:

    **EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

    ```php
    <?php

    declare(strict_types=1);

    namespace MyVendor\MyExtension\Domain\Repository;

    use TYPO3\CMS\Core\Database\Connection;

    final class MyTableRepository
    {
        public function __construct(
            private readonly Connection $connection,
        ) {}

        public function findSomething()
        {
            // Here you can use $this->connection directly
        }
    }

    ```

## Parameter types {#database-connection-parameter-types}

The parameter types are used in various places to bind values to types, for
example, when using named parameters in the
[query builder](https://docs.typo3.org/permalink/t3coreapi:database-query-builder@12.4):

```php
// use TYPO3\CMS\Core\Database\Connection;

$queryBuilder->createNamedParameter(42, Connection::PARAM_INT);
```

The following parameter types are available:

-   **`\TYPO3\CMS\Core\Database\Connection::PARAM_NULL`**

    Represents an SQL `NULL` data type.

-   **`\TYPO3\CMS\Core\Database\Connection::PARAM_INT`**

    Represents an SQL `INTEGER` data type.

-   **`\TYPO3\CMS\Core\Database\Connection::PARAM_STR`**

    Represents an SQL `CHAR` or `VARCHAR` data type.

-   **`\TYPO3\CMS\Core\Database\Connection::PARAM_LOB`**

    Represents an SQL large object data type.

-   **`\TYPO3\CMS\Core\Database\Connection::PARAM_BOOL`**

    Represents a boolean data type.

-   **`\TYPO3\CMS\Core\Database\Connection::PARAM_INT_ARRAY`**

    Represents an array of integer values.

-   **`\TYPO3\CMS\Core\Database\Connection::PARAM_STR_ARRAY`**

    Represents an array of string values.

The default parameter type is `Connection::PARAM_STR`, if this argument
is omitted.

Internally, these parameter types are mapped to the types Doctrine DBAL expects.

## insert() {#database-connection-insert}

The `insert()` method creates and executes an `INSERT INTO` statement.
Example:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function insertSomething(
        int $pid,
        string $someString,
        array $someData,
    ): void {
        $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME)
            ->insert(
                self::TABLE_NAME,
                [
                    'pid' => $pid,
                    'some_string' => $someString,
                    'json_field' => $someData,
                ],
            );
    }
}

```

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.
See available [parameter types](https://docs.typo3.org/permalink/t3coreapi:database-connection-parameter-types@12.4).

<!-- TODO: no Markdown rendering for "versionadded" -->

This method supports the native database field declaration json,
see Native JSON database field type support.

Arguments of the `insert()` method:

1.  The name of the table the row should be inserted. Required.
1.  An associative array containing field/value pairs. The key is a field name,
    the value is the value to be inserted. All keys are quoted to field names
    and all values are quoted to string values. Required.
1.  Specify how single values are quoted. This is useful if a date, number or
    similar should be inserted. Optional.

    The example below quotes the first value to an integer and the second one to
    a string:

    **EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

    ```php
    <?php

    declare(strict_types=1);

    namespace MyVendor\MyExtension\Domain\Repository;

    use TYPO3\CMS\Core\Database\Connection;
    use TYPO3\CMS\Core\Database\ConnectionPool;

    final class MyTableRepository
    {
        private const TABLE_NAME = 'tx_myextension_mytable';

        public function __construct(
            private readonly ConnectionPool $connectionPool,
        ) {}

        public function insertSomething(
            int $pid,
            string $someString,
        ): void {
            $this->connectionPool
                ->getConnectionForTable(self::TABLE_NAME)
                ->insert(
                    self::TABLE_NAME,
                    [
                        'pid' => $pid,
                        'some_string' => $someString,
                    ],
                    [
                        Connection::PARAM_INT,
                        Connection::PARAM_STR,
                    ],
                );
        }
    }

    ```

    Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a
    connection with the connection pool.
    See available [parameter types](https://docs.typo3.org/permalink/t3coreapi:database-connection-parameter-types@12.4).

`insert()` returns the number of affected rows. Guess what? That is the
number `1` ... If something goes wrong, a `\Doctrine\DBAL\Exception` is
thrown.

> [!NOTE]
> A list of allowed field types for proper quoting can be found in the
> `\TYPO3\CMS\Core\Database\Connection` class.

## bulkInsert() {#database-connection-bulk-insert}

This method insert multiple rows at once:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function bulkInsertSomething(
        int $pid1,
        int $pid2,
        string $someString1,
        string $someString2,
    ): void {
        $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME)
            ->bulkInsert(
                self::TABLE_NAME,
                [
                    [$pid1, $someString1],
                    [$pid2, $someString2],
                ],
                [
                    'pid',
                    'title',
                ],
                [
                    Connection::PARAM_INT,
                    Connection::PARAM_STR,
                ],
            );
    }
}

```

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.
See available [parameter types](https://docs.typo3.org/permalink/t3coreapi:database-connection-parameter-types@12.4).

Arguments of the `bulkInsert()` method:

1.  The name of the table the row should be inserted. Required.
1.  An array of the values to be inserted. Required.
1.  An array containing the column names of the data which should be inserted.
    Optional.
1.  Specify how single values are quoted. Similar to [insert()](https://docs.typo3.org/permalink/t3coreapi:database-connection-insert@12.4); if omitted, everything will be quoted
    to strings. Optional.

The number of inserted rows are returned. If something goes wrong, a
`\Doctrine\DBAL\Exception` is thrown.

> [!NOTE]
> MySQL is quite forgiving when it comes to insufficient field quoting:
> Inserting a string into an `int` field does not cause an error and
> MySQL adjusts internally. However, other DBMSes are not that relaxed and may raise errors. It is good practice to
> specify field types for each field, especially if they are not strings.
> Doing this immediately will reduce the number of bugs that occur when people
> run your extension an anything else than MySQL.

## update() {#database-connection-update}

Create an `UPDATE` statement and execute it. The example from FAL's
`ResourceStorage` sets a storage to offline:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function updateSomething(
        int $uid,
        string $someValue,
        array $someData,
    ): void {
        $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME)
            ->update(
                self::TABLE_NAME,
                [
                    'some_value' => $someValue,
                    'json_data' => $someData,
                ],
                ['uid' => $uid],
                [Connection::PARAM_INT],
            );
    }
}

```

<!-- TODO: no Markdown rendering for "versionadded" -->

This method supports the native database field declaration json,
see Native JSON database field type support.

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.
See available [parameter types](https://docs.typo3.org/permalink/t3coreapi:database-connection-parameter-types@12.4).

Arguments of the `update()` method:

1.  The name of the table to update. Required.
1.  An associative array containing field/value pairs to be updated. The key is
    a field name, the value is the value. In SQL they are mapped to the
    `SET` keyword. Required.
1.  The update criteria as an array of key/value pairs. The key is the field
    name, the value is the value. In SQL they are mapped in a `WHERE`
    keyword combined with `AND`. Required.
1.  Specify how single values are quoted. Similar to [insert()](https://docs.typo3.org/permalink/t3coreapi:database-connection-insert@12.4); if omitted, everything will be quoted
    to strings. Optional.

The method returns the number of updated rows. If something goes wrong, a
`\Doctrine\DBAL\Exception` is thrown.

> [!NOTE]
> The third argument `WHERE `foo` = 'bar'` supports only equal `=`. For more
> complex stuff the [query builder](https://docs.typo3.org/permalink/t3coreapi:database-query-builder@12.4) must be used.

## delete() {#database-connection-delete}

Execute a `DELETE` query using `equal` conditions in `WHERE`, example
from `BackendUtility`, to mark rows as no longer locked by a user:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function deleteSomething(
        int $uid,
    ): void {
        $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME)
            ->delete(
                self::TABLE_NAME,
                ['uid' => $uid],
                [Connection::PARAM_INT],
            );
    }
}

```

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.
See available [parameter types](https://docs.typo3.org/permalink/t3coreapi:database-connection-parameter-types@12.4).

Arguments of the `delete()` method:

1.  The name of the table. Required.
1.  The delete criteria as an array of key/value pairs. The key is the field
    name, the value is the value. In SQL they are mapped in a `WHERE`
    keyword combined with `AND`. Required.
1.  Specify how single values are quoted. Similar to [insert()](https://docs.typo3.org/permalink/t3coreapi:database-connection-insert@12.4); if omitted, everything will be quoted
    to strings. Optional.

The method returns the number of deleted rows. If something goes wrong, a
`\Doctrine\DBAL\Exception` is thrown.

> [!NOTE]
> TYPO3 uses a "[soft delete](https://docs.typo3.org/m/typo3/reference-tca/12.4/en-us/Ctrl/Index.html#ctrl-reference-delete)" approach for
> many tables. Instead of deleting a  row directly in the database, a field -
> often called `deleted` \- is set from 0 to 1. Executing a `DELETE`
> query circumvents this and really removes rows from a table. For most
> tables, it is better to use the [DataHandler](https://docs.typo3.org/permalink/t3coreapi:datahandler-basics@12.4) API
> to handle deletions instead of executing such low-level queries directly.

## truncate() {#database-connection-truncate}

This method empties a table, removing all rows. It is usually much faster than
a [delete()](https://docs.typo3.org/permalink/t3coreapi:database-connection-delete@12.4) of all rows. This typically
resets "auto increment primary keys" to zero. Use with care:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyCacheRepository
{
    private const TABLE_NAME = 'cache_myextension';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function truncateSomething(
        int $uid,
    ): void {
        $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME)
            ->truncate(self::TABLE_NAME);
    }
}

```

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.

The argument is the name of the table to be truncated. If something goes wrong,
a `\Doctrine\DBAL\Exception` is thrown.

## count() {#database-connection-count}

This method executes a `COUNT` query. Again, this becomes useful when very
simple `COUNT` statements are to be executed. The example below returns the
number of active rows (not hidden or deleted or disabled by time) from the
table `tx_myextension_mytable` whose
field `some_value` field set to `$something`:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function countSomething(
        int $something,
    ): int {
        $connection = $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME);
        return $connection->count(
            '*',
            self::TABLE_NAME,
            ['some_value' => $something],
        );
    }
}

```

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.

Arguments of the `count()` method:

1.  The field to count, usually `*` or `uid`. Required.
1.  The name of the table. Required.
1.  The select criteria as an array of key/value pairs. The key is the field
    name, the value is the value. In SQL they are mapped in a `WHERE`
    keyword combined with `AND`. Required.

The method returns the counted rows.

Remarks:

-   `Connection::count()` returns the number directly as an integer, unlike
    the method of the [query builder](https://docs.typo3.org/permalink/t3coreapi:database-query-builder@12.4) it is not
    necessary to call `->fetchColumns(0)` or similar.
-   The third argument expects all `WHERE` values to be strings, each
    single expression is combined with `AND`.
-   The [restriction builder](https://docs.typo3.org/permalink/t3coreapi:database-restriction-builder@12.4) kicks in and
    adds additional `WHERE` conditions based on TCA settings.
-   Field names and values are quoted automatically.
-   If anything more complex than a simple `equal` condition on `WHERE` is
    needed, the [query builder](https://docs.typo3.org/permalink/t3coreapi:database-query-builder@12.4) methods are the
    better choice: next to [select()](https://docs.typo3.org/permalink/t3coreapi:database-connection-select@12.4),
    the `->count()` query is often the least useful method of the
    `Connection` object.

## select() {#database-connection-select}

This method creates and executes a simple `SELECT` query based on `equal`
conditions. Its usage is limited, the [restriction builder](https://docs.typo3.org/permalink/t3coreapi:database-restriction-builder@12.4) kicks in and key/value pairs are automatically
quoted:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use Doctrine\DBAL\Result;
use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function countSomething(
        int $something,
    ): Result {
        return $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME)
            ->select(
                ['*'],
                self::TABLE_NAME,
                ['some_value' => $something],
            );
    }
}

```

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.

Arguments of the `select()` method:

1.  The columns of the table which to select as an array. Required.
1.  The name of the table. Required.
1.  The select criteria as an array of key/value pairs. The key is the field
    name, the value is the value. In SQL they are mapped in a `WHERE`
    keyword combined with `AND`. Optional.
1.  The columns to group the results by as an array. In SQL they are mapped
    in a `GROUP BY` keyword. Optional.
1.  An associative array of column name/sort directions pairs. In SQL they are
    mapped in an `ORDER BY` keyword. Optional.
1.  The maximum number of rows to return. In SQL it is mapped in a `LIMIT`
    keyword. Optional.
1.  The first result row to select (when used the maximum number of rows). In
    SQL it is mapped in an `OFFSET` keyword. Optional.

In contrast to the other short-hand methods, `->select()` returns a
[Result](https://docs.typo3.org/permalink/t3coreapi:database-result@12.4) object ready for `->fetchAssociative()` to
get single rows or for `->fetchAllAssociative()` to get all rows at once.

Remarks:

-   For non-trivial `SELECT` queries it is often better to switch to the
    according method of the [query builder](https://docs.typo3.org/permalink/t3coreapi:database-query-builder@12.4)
    object.
-   The [restriction builder](https://docs.typo3.org/permalink/t3coreapi:database-restriction-builder@12.4) adds default
    `WHERE` restrictions. If these restrictions do not match the query
    requirements, it is necessary to switch to the `QueryBuilder->select()`
    method for fine-grained `WHERE` manipulation.

## lastInsertId() {#database-connection-last-insert-id}

This method returns the `uid` of the last [insert()](https://docs.typo3.org/permalink/t3coreapi:database-connection-insert@12.4) statement. This is useful if the id is to be used
directly afterwards:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function insertSomething(
        array $someData,
    ): int {
        $connection = $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME);
        $connection
            ->insert(
                self::TABLE_NAME,
                $someData,
            );
        return (int)$connection->lastInsertId(self::TABLE_NAME);
    }
}

```

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.

Remarks:

-   `->lastInsertId($tableName)` takes the table name as first argument.
    Although it is optional, you should always specify the table name for
    Doctrine DBAL compatibility with engines like PostgreSQL.
-   If the name of the auto increment field is not `uid`, the second
    argument must be specified with the name of that field. For simple TYPO3
    tables, `uid` is fine and the argument can be omitted.

## createQueryBuilder() {#database-connection-create-query-builder}

The [query builder](https://docs.typo3.org/permalink/t3coreapi:database-query-builder@12.4) should not be reused for
multiple different queries. However, sometimes it is convenient to first fetch a
connection object for a specific table and execute a simple query, and later
create a query builder for a more complex query from that connection object. The
usefulness of this method is limited, however, and at the time of writing no
good example could be found in the Core.

The method can also be useful in loops to save some precious code characters:

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function useQueryBuilder(
        array $someData,
    ): void {
        $connection = $this->connectionPool->getConnectionForTable(self::TABLE_NAME);
        foreach ($someData as $value) {
            $queryBuilder = $connection->createQueryBuilder();
            $myResult = $queryBuilder
                ->select('*')
                ->from(self::TABLE_NAME)
                ->where(
                    $queryBuilder->expr()->eq(
                        'some_field',
                        $queryBuilder->createNamedParameter($value),
                    ),
                )
                ->executeQuery()
                ->fetchAllAssociative();
            // do something
        }
    }
}

```

Read [how to instantiate](https://docs.typo3.org/permalink/t3coreapi:database-connection-instantiation@12.4) a connection
with the connection pool.

## Native JSON database field type support {#json-database-type}

<!-- TODO: no Markdown rendering for "versionadded" -->

TYPO3 Core's Database API based on Doctrine DBAL supports the native
database field type json, which is already available for all supported DBMS
of TYPO3 v12.

JSON-like objects or arrays are automatically serialized during writing a
dataset to the database, when the native JSON type was used in the database
schema definition.

By using the native database field declaration `json` in [`ext_tables.sql`](../../../ExtensionArchitecture/FileStructure/ExtTablesSql.md#file-extension-ext-tables-sql)
file within an extension, TYPO3 converts arrays or objects of type
`\JsonSerializable` into a serialized JSON value in the database when
persisting such values via `Connection->insert()` or
`Connection->update()` if no explicit DB types are handed in as additional
method argument.

TYPO3 now utilizes the native type mapping of Doctrine to convert special types
such as JSON database field types automatically for writing.

Example:

**EXT:my_extension/ext_tables.sql**

```php
CREATE TABLE tx_myextension_mytable
(
    some_string varchar(200) DEFAULT '',
    json_field  json
);

```

**EXT:my_extension/Classes/Domain/Repository/MyTableRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyTableRepository
{
    private const TABLE_NAME = 'tx_myextension_mytable';

    public function __construct(
        private readonly ConnectionPool $connectionPool,
    ) {}

    public function insertSomething(
        int $pid,
        string $someString,
        array $someData,
    ): void {
        $this->connectionPool
            ->getConnectionForTable(self::TABLE_NAME)
            ->insert(
                self::TABLE_NAME,
                [
                    'pid' => $pid,
                    'some_string' => $someString,
                    'json_field' => $someData,
                ],
            );
    }
}

```

> [!NOTE]
> When reading a record from the database via QueryBuilder, it is still necessary
> though to transfer the serialized value to an array or object doing custom
> serialization.
