Docs
Skip to content

MySQL

Spring Boot_

Connect a Spring Boot application to an Appwrite native MySQL database with Spring Data JPA and Hibernate. Configure the JDBC datasource and HikariCP pool, map entities, and run Flyway or Liquibase migrations against MySQL.

5 min read

Raw

An Appwrite native MySQL database is a standard MySQL engine, so a Spring Boot application connects to it through MySQL Connector/J with no Appwrite-specific runtime configuration. Point spring.datasource at the JDBC URL from your database credentials, size the built-in HikariCP pool, and use Spring Data JPA, Hibernate, Flyway, or Liquibase as you would with any managed MySQL server.

Add the dependencies

A Spring Data JPA application needs the JPA starter and MySQL Connector/J. HikariCP ships with spring-boot-starter-data-jpa, and Spring Boot picks the driver class from the JDBC URL.

Plain text
org.springframework.boot:spring-boot-starter-data-jpa
com.mysql:mysql-connector-j

If you use Flyway, add org.springframework.boot:spring-boot-starter-flyway and org.flywaydb:flyway-mysql. If you use Liquibase, add org.springframework.boot:spring-boot-starter-liquibase.

Configure the datasource

Build the JDBC URL from the host and database name in the credentials returned by mysql.get(). Appwrite uses port 3306, the primary user is admin, and the database name is generated per database. Read the password from the environment:

YAML
spring:
datasource:
url: jdbc:mysql://db-<hash>.<region>.appwrite.center:3306/<database>?sslMode=REQUIRED
username: admin
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 10
minimum-idle: 2
connection-timeout: 30000
max-lifetime: 1200000
jpa:
hibernate:
ddl-auto: validate

The equivalent application.properties:

.env
spring.datasource.url=jdbc:mysql://db-<hash>.<region>.appwrite.center:3306/<database>?sslMode=REQUIRED
spring.datasource.username=admin
spring.datasource.password=${DB_PASSWORD}
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=2
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=1200000
spring.jpa.hibernate.ddl-auto=validate

sslMode=REQUIRED enables TLS for the MySQL connection. If your security policy requires host certificate validation, configure MySQL Connector/J with sslMode=VERIFY_IDENTITY and a JVM trust configuration that trusts the certificate chain. The Network security page covers TLS, mTLS, and IP allowlists.

Size the HikariCP pool

A Spring Boot server is long-running, so it holds its HikariCP pool open for the lifetime of the process. Keep maximum-pool-size modest. HikariCP guidance is that throughput usually peaks at a small pool, roughly (CPU cores x 2) + effective spindle count for the database, not hundreds of connections. A pool that exceeds what MySQL can serve only queues work inside the database and adds latency.

Each replica of your application opens its own pool, so multiply maximum-pool-size by the number of instances and keep the total under the connection budget of your native MySQL database specification. Set max-lifetime a little below your infrastructure's idle timeout so HikariCP recycles connections before they are closed.

Map an entity

Define a JPA entity and a Spring Data repository as usual. This example uses a prefixed table name so it is easy to identify in a shared database:

Java
package com.example.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "spring_boot_users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

Add a repository interface for it:

Java
package com.example.demo;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}

Inject the repository wherever you need it and call save, findById, findByEmail, and the rest of the generated query methods. HikariCP hands each transaction a pooled connection and returns it on commit.

Run migrations

Let a migration tool own the schema and set ddl-auto: validate so Hibernate checks the mapping against the live tables at startup but never alters them. Add Flyway or Liquibase to your build and Spring Boot runs pending migrations automatically on boot.

For example, a Flyway migration at src/main/resources/db/migration/V1__init.sql can create the table used by the entity above:

SQL
CREATE TABLE spring_boot_users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE
);

Flyway reads versioned scripts from src/main/resources/db/migration. Point Flyway at the direct MySQL port 3306 so migrations run on a session connection with DDL privileges. Setting spring.flyway.url gives Flyway its own datasource, independent of the runtime pool:

.env
spring.flyway.url=jdbc:mysql://db-<hash>.<region>.appwrite.center:3306/<database>?sslMode=REQUIRED
spring.flyway.user=admin
spring.flyway.password=${DB_PASSWORD}

Liquibase is equivalent: it reads a changelog from src/main/resources/db/changelog and accepts its own spring.liquibase.url, spring.liquibase.user, and spring.liquibase.password pointing at the same MySQL port.

The primary admin user owns the database and can run schema changes, so run migrations as admin.

Pooling and the connection pooler

HikariCP is already a connection pool, so a long-running Spring Boot server should connect to the direct MySQL port 3306 and let HikariCP manage connections. Routing a server's traffic through the connection pooler in transaction mode stacks HikariCP on top of a transaction pooler and can break session-level features such as server-side prepared statements, user variables, and temporary tables.

If you put the pooler in front of your server, use session mode so MySQL keeps a backend connection for the whole client session. Connect HikariCP on the pooler port 6033 and keep maximum-pool-size small. See the connection pooler page for the mode trade-offs. Always run Flyway or Liquibase against port 3306 regardless of how runtime traffic connects.

Use a branch for previews and CI

Branches are instant, isolated copies of a database with their own hostname and connection string. They are useful for running migrations against throwaway data in a pull-request preview or integration-test job.

  1. Create a branch with the API and read its connectionString.
  2. Convert the connection string to a JDBC URL and inject it into spring.datasource.url, or split it into spring.datasource.url, spring.datasource.username, and spring.datasource.password.
  3. Boot the application so Flyway or Liquibase applies migrations, then run your test suite against the branch.
  4. Delete the branch when the job finishes.

Because a branch starts from a storage snapshot, the schema and data match the source database at branch time, so migrations and @DataJpaTest integration tests run against representative data without touching production.

Was this page helpful?

Share what worked or what we should fix. Once approved, our agents automatically apply suggested updates to the docs.