---
title: "What’s new in MariaDB Connector/J 4.0"
publish_date: 2026-10-22
author: "Diego Dupin"
channel:
  - name: "Product"
    url: "/ja/resources/blog/channel/product.md"
tags:
  - name: "Connector releases"
    url: "/resources/blog/tag/connector-releases.md"
  - name: "MariaDB Connector/Java"
    url: "/resources/blog/tag/mariadb-connector-java.md"
  - name: "MariaDB Releases"
    url: "/resources/blog/tag/mariadb-releases.md"
---

# What’s new in MariaDB Connector/J 4.0

MariaDB Connector/J 4.0.0 is now generally available. Connector/J is the JDBC driver for MariaDB Community Server and MariaDB Enterprise Server, released under the LGPL 2.1 license. It is the first major version since 3.0 in 2021.

4.0 requires Java 17 or later. If you are still on Java 8 or 11, the 3.5 branch stays maintained and receives the same fixes in 3.5.11, released alongside 4.0.0.

What 4.0 brings to your application:

- Faster queries and inserts, with no change to your code: 5 to 15% more throughput on typical workloads
- Encrypted and verified connections by default, with nothing to configure
- Large `BLOB` and `TEXT` columns can be streamed from the network, so the heap no longer has to fit your biggest document
- A lighter dependency tree: no more JNA, no more AWS SDK, with AWS IAM authentication in its own artifact
- Stricter JDBC behavior where 3.x silently diverged from the specification
- Ready for virtual threads, Kerberos single sign-on, the MariaDB 12.3 XML type, and more

## Faster out of the box

Upgrading the dependency is all it takes. Moving to Java 17 let us rebuild the parts of the driver every statement goes through, the packet reader and writer, string encoding and the lookup of the right converter for each column, so the same application does more work per second on the same server. On the connector’s own benchmark suite, 4.0.0 is between 0 and 15% faster than 3.5.10 depending on the workload, and no workload regresses:

![]()The more the driver has to do per exchange, the bigger the gain: batch inserts (+14.5% on a 100-row batch with server-side prepared statements), result sets with many rows (+6 to 9% on 1,000 rows) and wide rows (+6% on 100 columns in the text protocol). A single SELECT 1 is dominated by the network round trip and does not move.

## Encrypted by default

A connection with no `sslMode` option is now encrypted, the server certificate is validated and the hostname is checked. With MariaDB Server 11.4 or later nothing has to be installed on the client: the server generates its own certificate and the connector validates it through the password exchange. Older servers with a certificate from a public or company authority work the same way, for a self-signed certificate on an older server, point `serverSslCert` at it as before.

| **sslMode** | **What you get** |
|---|---|
| `verify-full` (new default) | Encryption, certificate validation and hostname verification |
| `verify-ca` | Encryption and certificate validation, no hostname verification |
| `trust` | Encryption only, no validation (development only) |
| `allow` (new) | verify-full when the server has TLS, no encryption otherwise |
| `disable` | No encryption |

Two cases need a one-word change in the connection string. A server without TLS needs `sslMode=disable`:

```
jdbc:mariadb://localhost:3306/db?user=app&password=secret&sslMode=disable
```

A migration where only some servers have TLS yet can use the new `sslMode=allow`, which encrypts and verifies when the server offers TLS and connects in clear otherwise:

```
jdbc:mariadb:sequential://db1,db2,db3/db?user=app&password=secret&sslMode=allow
```

Treat `allow` as a migration setting. The decision is based on what the server announces before encryption starts, so an attacker on the network path could make a server look like it has no TLS. Move to `verify-full` once every server is ready. Only TLS 1.3 and 1.2 are enabled by default now, whatever the JVM’s own defaults.

## Large objects without the heap

Until now, reading a `BLOB` or `TEXT` column loaded it entirely in memory before `getBinaryStream()`, `getBlob()` or `getCharacterStream()` returned, so a 1 GB document needed 1 GB of heap. 4.0 can stream it instead: with a fetch size of `Integer.MIN_VALUE` on a forward-only, read-only statement, the stream you get reads straight from the network. It is the equivalent of ADO.NET’s `CommandBehavior.SequentialAccess`.

```
try (PreparedStatement ps = con.prepareStatement(        "SELECT name, content FROM documents WHERE id = ?")) {  ps.setFetchSize(Integer.MIN_VALUE);          // sequential access  ps.setLong(1, 42);  try (ResultSet rs = ps.executeQuery()) {    if (rs.next()) {      String name = rs.getString(1);           // read columns in index order      try (InputStream in = rs.getBinaryStream(2)) {        Files.copy(in, Path.of("/tmp", name));  // bytes flow from the socket to the file      }    }  }}
```

The rules follow from reading the network in order: read columns by increasing index, consume a stream before moving to the next column or row, and read a streamed column once. If you run another statement on the connection while the result set is open, the driver loads the remaining rows in memory so nothing breaks.

A quick check with a 15 MiB `LONGBLOB` read through `getBinaryStream()` on a JVM limited to a 12 MiB heap:

| **Driver** | **Fetch mode** | **12 MiB heap** | **Time with a large heap** |
|---|---|---|---|
| 3.5.10 | default | `OutOfMemoryError` | 12 to 15 ms |
| 4.0.0 | default | `OutOfMemoryError` | 13 to 15 ms |
| 4.0.0 | `setFetchSize(Integer.MIN_VALUE)` | reads all 15 MiB | 8 to 10 ms |

The heap requirement no longer depends on the data, and skipping the intermediate copy makes the read about 30% faster even when memory is not the problem.

## Java 17 required

4.0 runs on Java 17 or later and is tested on Java 17, 21 and 25. Connector/J 3.x was compiled for Java 8 with a few Java 11 extras in a multi-release jar; 4.0 is a single Java 17 release, which is what made the performance work above possible and let us retire the Java 8 compatibility code. For applications nothing else changes: the module is still org.mariadb.jdbc with the same packages and services, and the GraalVM native-image metadata is refreshed. If you cannot move to Java 17 yet, stay on 3.5, which keeps receiving fixes.

## AWS IAM authentication in its own artifact

If you authenticate to Amazon RDS or Aurora with an IAM token (`credentialType=AWS-IAM`), add one dependency. The plugin now lives in `mariadb-java-client-aws`, published on Maven Central with the connector, and the connector itself no longer references the AWS SDK, so everyone else gets a smaller dependency tree.

```
<dependency>  <groupId>org.mariadb.jdbc</groupId>  <artifactId>mariadb-java-client-aws</artifactId>  <version>4.0.0</version></dependency>
```

Two things come with the move. The plugin bundles the RDS root certificates, so the new default `sslMode=verify-full` validates an RDS endpoint with no bundle to download and no `serverSslCert` to set. RDS Proxy endpoints, which use public certificates, work too. It can now assume an IAM role through STS before signing the token, for cross-account access or a role with narrower permissions:

```
jdbc:mariadb://mydb.123456789012.eu-west-1.rds.amazonaws.com/db?user=iam_user&credentialType=AWS-IAM&roleArn=arn:aws:iam::123456789012:role/db-reader
```

TLS is mandatory with IAM authentication: the plugin refuses `sslMode=disable`.

## Stricter JDBC behavior

Two places where 3.x silently did something other than what the JDBC specification says now behave as every other driver does. Both can show up as new exceptions after the upgrade, and both have a simple fix.

`executeQuery()` on a statement that returns no result set, an `INSERT`, an `UPDATE` or a procedure that returns nothing, now throws an `SQLException` instead of returning an empty result set. Call `execute()` or `executeUpdate()` for such statements. If you need time to find them, `permitNoResults=true` restores the old behavior:

```
jdbc:mariadb://localhost/db?user=app&permitNoResults=true
```

`Connection.setSavepoint()` now throws in auto-commit mode instead of creating a savepoint that the next statement silently discards. Call `setAutoCommit(false)` before using savepoints.

## Unix domain sockets without JNA

Connecting through a Unix domain socket (`localSocket=/var/run/mysqld/mysqld.sock`) no longer needs the JNA library. 4.0 uses the socket support built into the JDK, which also makes `localSocket` work on Windows 10 1803+ and Windows Server 2019+, for example at the end of an SSH tunnel:

```
jdbc:mariadb:///db?user=app&localSocket=C:\\Users\\app\\tunnel.sock
```

The JDK path is also faster: about 2.5 µs less per round trip, which is most of the cost of a small packet, and connections 1.5 times faster. Measured with the two socket classes alone, against an in-process server:

![]()In the full driver, where the server’s work is unchanged, the gain over a socket ranges from 5% on `SELECT 1` to 20% on a 100-row batch insert.

## Also in this release

**Virtual threads.** The driver no longer holds a `synchronized` monitor anywhere it may block on the network, so virtual threads waiting on a query park instead of pinning their carrier on Java 21. A test runs the driver’s main paths on virtual threads under a JFR recording of `jdk.VirtualThreadPinned` to keep it that way. No option is needed.

**Kerberos single sign-on without configuration.** Start the JVM with `-Dsun.security.jgss.native=true` and GSSAPI authentication uses the operating system’s credentials, the logged-on Windows user or the `kinit` ticket cache, with no JAAS file and no third-party library. Without the property, the pure-Java Kerberos implementation is used as before.

**MariaDB 12.3 XML type.** `XMLTYPE` columns come back as `java.sql.SQLXML` from `getObject()`, work with `getSQLXML()` and `setSQLXML()`, and are reported by `DatabaseMetaData`.

**Every address of a hostname is tried.** When a hostname resolves to several addresses, typically IPv4 and IPv6, the connector tries each in turn within `connectTimeout` instead of failing on the first, and remembers which one worked.

**New defaults and removed options.** `cachedCodecs` is now true, so the converter list is loaded once per JVM rather than per connection; set it to `false` only if you register converters after the first connection. The `useReadAheadInput` option and its read-ahead stream are gone, replaced by the new packet reader.

**Software bill of materials.** Each release now ships a CycloneDX 1.6 SBOM meeting the CISA 2026 minimum elements, published on Maven Central next to the jar as the `cyclonedx` classifier (`mariadb-java-client-4.0.0-cyclonedx.json`), for supply-chain tooling and compliance reviews. The 3.5 branch has it too since 3.5.11.

**Fixes.** 4.0.0 includes every fix of 3.5.11, among them session tracking set explicitly, connection setup no longer discarding a session-level `last_gtid` set by MaxScale, server-declared lengths bounded before allocation, and `DatabaseMetaData` imported and exported keys honoring `lower_case_table_names`. The complete list is in the[ changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md).

## Upgrading from 3.x

Most applications only need to bump the version. Five changes can break an existing setup, and each has a one-line way back to the 3.x behavior.

| **Change** | **What breaks** | **What to do** |
|---|---|---|
| Java 17 minimum | Build or startup on Java 8 or 11 | Stay on the 3.5 branch, or move to Java 17+ |
| sslMode defaults to verify-full | Connections to servers without TLS, or with a certificate the JVM does not trust | Add sslMode=disable for plain connections, sslMode=allow during a TLS rollout, or serverSslCert= to trust a specific certificate |
| executeQuery() on a statement returning no result set throws | Code that calls executeQuery with INSERT, UPDATE or a procedure returning nothing | Use execute or executeUpdate, or set permitNoResults=true |
| setSavepoint() throws in auto-commit mode | Code that set savepoints without setAutoCommit(false) | Disable auto-commit before using savepoints |
| AWS IAM moves to mariadb-java-client-aws | credentialType=AWS-IAM with only the main artifact on the classpath | Add the org.mariadb.jdbc:mariadb-java-client-aws:4.0.0 dependency; the AWS SDK is no longer pulled in by the driver |

## Resources

The driver is on Maven Central and on the [MariaDB download page](https://mariadb.com/downloads/#connectors).

```
<dependency>  <groupId>org.mariadb.jdbc</groupId>  <artifactId>mariadb-java-client</artifactId>  <version>4.0.0</version></dependency>
```

- Documentation:[ About MariaDB Connector/J](https://mariadb.com/kb/en/about-mariadb-connector-j/) and[ installation](https://mariadb.com/kb/en/installing-mariadb-connectorj/)
- Source and changelog:[ github.com/mariadb-corporation/mariadb-connector-j](https://github.com/mariadb-corporation/mariadb-connector-j)
- Bugs and feature requests:[ jira.mariadb.org, project CONJ](https://jira.mariadb.org/projects/CONJ/issues/)

Thanks to everyone who reported issues and contributed fixes in this cycle. The connector is LGPL 2.1, and pull requests are welcome.