CentralDatabase is a lightweight, centralized database API for Minecraft plugins.
It abstracts away connection management, simplifies queries, and provides a clean, developer-friendly way to work with databases.
This plugin is supported by Spigot and Paper.
MySQL / MariaDB extendableAdd CentralDatabase to your project using one of the following methods:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.devspexx</groupId>
<artifactId>CentralDatabase</artifactId>
<version>v1.2.1</version>
</dependency>
If you prefer building the project yourself:
git clone https://github.com/devspexx/CentralDatabase.git
cd CentralDatabase
mvn clean install
This installs the artifact into your local Maven repository.
You can then add it as a dependency using the version defined in the projectβs pom.xml (e.g. 1.2.1).
CentralDatabase must be available at runtime. You can choose one of the following:
π‘ If you do not shade it, make sure the CentralDatabase plugin is present on the server.
How do I integrate CentralDatabase into my plugin?
You should initialize the API inside your pluginβs onEnable() method.
import dev.spexx.centralDatabase.config.DatabaseConfigLoader;
import dev.spexx.centralDatabase.query.QueryRunner;
public final class MyPlugin extends JavaPlugin {
private QueryRunner queryRunner;
@Override
public void onEnable() {
// Save the default config first
saveDefaultConfig();
// Set up the api
setupCentralDatabase();
}
public void setupCentralDatabase() {
// That means the config from this plugin will be used - fromPlugin(this)
// Usually, you don't want that, so use the config from CentralDatabase plugin instead!
// --->
// DatabaseConfigLoader configLoader = DatabaseConfigLoader.fromPlugin(
// CentralDatabase.getProvidingPlugin(CentralDatabase.class));
DatabaseConfigLoader configLoader = DatabaseConfigLoader.fromPlugin(this);
queryRunner = new QueryRunner(configLoader);
// Order is important! Keep it like that.
queryRunner.configureHikari();
queryRunner.initializeHikari();
queryRunner.initializeThreadPool();
}
// Always shutdown the query runner in your JavaPlugin class
@Override
public void onDisable() {
if (queryRunner != null) {
queryRunner.shutdown();
}
}
public QueryRunner getQueryRunner() {
return queryRunner;
}
}
π‘ CentralDatabase does not auto-start anything. You have full control over lifecycle.
All queries are executed asynchronously and return a CompletableFuture.
queryRunner.selectAsync(
"SELECT * FROM players WHERE uuid = ?",
uuid.toString() // The amount of parameters equals to amount of `?`
).thenAccept(result -> {
if (result.isSuccess()) {
Row row = result.firstRow();
if (row != null) {
String name = row.asString("username");
int coins = row.asInt("coins");
}
} else {
getLogger().warning("Query failed: " + result.error());
}
});
queryRunner.insertAsync(
"INSERT INTO players (uuid, username) VALUES (?, ?)",
uuid.toString(), name
).thenAccept(result -> {
if (result.isSuccess()) {
long id = result.insertId();
}
});
queryRunner.updateAsync(
"UPDATE players SET coins = ? WHERE uuid = ?",
100,
uuid.toString()
);
queryRunner.deleteAsync(
"DELETE FROM players WHERE uuid = ?",
uuid.toString()
);
CentralDatabase provides a type-safe result system.
Example:
queryRunner.selectAsync("SELECT * FROM players")
.thenAccept(result -> {
if (result.isSuccess()) {
for (Row row : result.value()) {
UUID uuid = row.asUUID("uuid");
String name = row.asString("username");
}
} else {
QueryError error = result.error();
getLogger().warning("SQL Error: " + error);
}
});
result.isSuccess() : Query succeededresult.isError() : Query failedresult.isEmpty() : No rows / no changesresult.requireValue() : Throws if nullEvery query returns a structured QueryResultCode. These codes allow you to reliably handle database outcomes
without parsing exception messages or relying on driver-specific behavior.
π‘ Result codes are grouped by origin:
- Success β everything worked
- Client errors β fix your query
- Database failures β handle or retry
- System states β check initialization / environment
These codes represent successful query execution.
They indicate that the database operation completed correctly, even if no rows were affected or returned.
| Code | Description |
|---|---|
SELECTED |
Query returned one or more rows |
NO_RESULTS |
Query executed successfully but returned no rows |
UPDATED_ZERO_ROWS |
Update/Delete affected zero rows |
INSERTED |
Insert operation succeeded |
UPDATED |
Update operation succeeded |
DELETED |
Delete operation succeeded |
SUCCESS_WITH_WARNING |
Query succeeded but produced warnings |
These errors are caused by incorrect queries or invalid input.
They usually indicate a mistake in your code, schema usage, or query structure, and should be fixed rather than retried.
| Code | Description |
|---|---|
SYNTAX_ERROR |
Invalid SQL syntax |
MISSING_OBJECT |
Table/column does not exist |
PERMISSION_DENIED |
Database user lacks required permissions |
INVALID_DATA |
Invalid value for column |
NULL_CONSTRAINT_VIOLATION |
NULL value in NOT NULL column |
TYPE_MISMATCH |
Value type does not match column type |
These errors originate from the database engine or environment.
They may be temporary (e.g. connection issues, deadlocks) and can often be handled with retries or fallback logic.
| Code | Description |
|---|---|
CONNECTION_ERROR |
Connection failed or lost |
TIMEOUT |
Query exceeded execution timeout |
DEADLOCK |
Query aborted due to deadlock |
LOCK_WAIT_TIMEOUT |
Lock wait exceeded timeout |
DUPLICATE_KEY |
Unique/primary key constraint violation |
FOREIGN_KEY_VIOLATION |
Foreign key constraint violation |
DATA_TRUNCATED |
Data was truncated |
TRANSACTION_ROLLBACK |
Transaction was rolled back |
These codes represent internal or lifecycle-related conditions.
They are not caused by the database itself, but by the state of the CentralDatabase system or underlying driver.
| Code | Description |
|---|---|
DRIVER_ERROR |
JDBC driver-level failure |
EXECUTION_ERROR |
Unclassified execution error |
NOT_INITIALIZED |
QueryRunner not initialized |
queryRunner.insertAsync(...).thenAccept(result -> {
if (result.isSuccess()) {
// All good
return;
}
switch (result.code()) {
case <STATUS CODE> -> { }
default -> {
getLogger().warning("Unhandled DB error: " + result.code());
}
}
});
CentralDatabase uses a simple config.yml.
# CentralDatabase configuration
# Handles MySQL connection and async query execution.
# MySQL connection
hostname: localhost
port: 3306
username: root
password: myPassword
database: databaseName
# JDBC connection URL.
# Available placeholders: {hostname}, {port}, {database}
jdbc-url: "jdbc:mysql://{hostname}:{port}/{database}?useSSL=false"
# Number of threads executing SQL queries in parallel.
# Increase for busy servers, decrease for small ones.
thread-pool-size: 4
# Advanced settings for database connection pooling.
# Only change these if you know what you are doing.
hikari:
# Maximum number of open database connections.
# Small servers: 5β8
# Large servers: 10β20
maximum-pool-size: 10
# Minimum number of idle connections kept ready.
minimum-idle: 2
# How long to wait for a connection before failing (ms).
connection-timeout: 5000
# How long an unused connection stays in the pool (ms).
idle-timeout: 120000
# Maximum lifetime of a connection before replacement (ms).
# Must be lower than MySQL's wait_timeout.
max-lifetime: 1800000
# Detects leaked connections. 0 disables detection.
# Enable only when debugging.
leak-detection-threshold: 0
# Periodic keep-alive ping to prevent connection drops (ms).
keepalive-time: 300000
π‘ Placeholders like {hostname} and {database} are resolved automatically.
All queries run off the main thread.
Uses:
HikariCP (connection pool)Main Thread β QueryRunner β Worker Threads β Database
Safe for high-performance environments!
Pull requests are welcome!
If you find a bug or have a feature request, feel free to open an issue.
CentralDatabase is built to simplify database access without sacrificing control.
It removes repetitive JDBC boilerplate and gives you a clean, predictable, and performant way to interact with SQL in Minecraft plugins.
If you find it useful, consider β starring the repository β it helps a lot.