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.

sslModeWhat you get
verify-full (new default)Encryption, certificate validation and hostname verification
verify-caEncryption and certificate validation, no hostname verification
trustEncryption only, no validation (development only)
allow (new)verify-full when the server has TLS, no encryption otherwise
disableNo 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:

DriverFetch mode12 MiB heapTime with a large heap
3.5.10defaultOutOfMemoryError12 to 15 ms
4.0.0defaultOutOfMemoryError13 to 15 ms
4.0.0setFetchSize(Integer.MIN_VALUE)reads all 15 MiB8 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.

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.

ChangeWhat breaksWhat to do
Java 17 minimumBuild or startup on Java 8 or 11Stay on the 3.5 branch, or move to Java 17+
sslMode defaults to verify-fullConnections to servers without TLS, or with a certificate the JVM does not trustAdd 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 throwsCode that calls executeQuery with INSERT, UPDATE or a procedure returning nothingUse execute or executeUpdate, or set permitNoResults=true
setSavepoint() throws in auto-commit modeCode that set savepoints without setAutoCommit(false)Disable auto-commit before using savepoints
AWS IAM moves to mariadb-java-client-awscredentialType=AWS-IAM with only the main artifact on the classpathAdd 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.

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

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