A lightweight, fully asynchronous multi-database API for Minecraft plugins.
CentralDatabase removes JDBC boilerplate, provides a safe async query layer,
and exposes a clean fluent API used across your entire plugin ecosystem.
- Async query execution using
CompletableFuture - Unified database access layer shared across all plugins
- HikariCP connection pooling with full config.yml control
- Fluent query builder (
api.query().sql(...).params(...).firstIntegerAsync()) - Immutable credentials loader + clean API surface
- Consistent result + error handling via
ResponseCode&ResponseData
YourPlugin → CentralDatabase API → HikariCP → JDBC Driver → Database
CentralDatabase is distributed through JitPack, making it easy to include in any plugin.
Add JitPack to your pom.xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>Add the CentralDatabase dependency
<dependency>
<groupId>com.github.devspexx</groupId>
<artifactId>CentralDatabase</artifactId>
<version>2.0</version>
</dependency>Plugins should never store the DatabaseManager permanently. Always access it through the public API:
private CentralDatabaseAPI db;
@Override
public void onEnable() {
CentralDatabase plugin = (CentralDatabase) getServer()
.getPluginManager()
.getPlugin("CentralDatabase");
if (plugin == null || !plugin.isEnabled()) {
getLogger().severe("CentralDatabase not available!");
getServer().getPluginManager().disablePlugin(this);
return;
}
db = plugin.api();
}- Never call
.sync()or.get()on the main thread. - All DB operations run on a dedicated executor created by CentralDatabase.
- The plugin ensures safe shutdown of the connection pool.
| Feature | Fluent Query API (db.query()) |
DatabaseManager API |
|---|---|---|
| Convenience | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| Custom SQL | Full | Full |
| Method Chaining | Yes | No |
| Typed Shortcuts | Yes (firstIntegerAsync()) |
No |
| Manual SELECT/UPDATE handling | No | Yes (via getAsync, updateAsync) |
Recommendation:
Use Fluent API for 95% of queries.
Use DatabaseManager only for highly specialized operations.
CentralDatabase exposes:
CentralDatabaseAPI- Entry point for all plugin queriesQueryExecutor- Fluent builder for SQL operationsDatabaseManager- Lower-level async SELECT/UPDATE runnerResponseData- Encapsulated SELECT resultResponseCode- Enum describing operation outcome
SELECT Example:
db.query()
.sql("SELECT coins FROM bank WHERE uuid=?")
.param(playerUuid)
.firstIntegerAsync()
.thenAccept(coins -> {
if (coins == null) {
getLogger().info("No data found.");
return;
}
getLogger().info("Player coins: " + coins);
});UPDATE Example
db.query()
.sql("UPDATE bank SET coins = coins + ? WHERE uuid=?")
.params(50, playerUuid)
.updateAsync()
.thenAccept(code -> {
switch (code) {
case SUCCESS -> getLogger().info("Coins updated.");
case NO_RESULTS -> getLogger().warning("Player not found.");
case ERROR -> getLogger().severe("SQL error!");
case FAILED -> getLogger().severe("Unexpected failure.");
}
});INSERT Example
db.query()
.sql("INSERT INTO bank (uuid, coins) VALUES (?, ?)")
.params(playerUuid, 100)
.setAsync()
.thenAccept(code -> {
switch (code) {
case SUCCESS -> getLogger().info("Account created.");
case NO_RESULTS -> getLogger().warning("Insert failed — no rows affected.");
case ERROR -> getLogger().severe("SQL error!");
case FAILED -> getLogger().severe("Unexpected failure.");
}
})
.exceptionally(ex -> {
getLogger().severe("Async insert failed: " + ex.getMessage());
return null;
});DELETE Example
db.query()
.sql("DELETE FROM bank WHERE uuid=?")
.param(playerUuid)
.setAsync()
.thenAccept(code -> {
switch (code) {
case SUCCESS -> getLogger().info("Player account removed.");
case NO_RESULTS -> getLogger().warning("No account found.");
case ERROR -> getLogger().severe("SQL error!");
case FAILED -> getLogger().severe("Unexpected failure.");
}
});ERROR HANDLING EXAMPLE
db.query()
.sql("SELECT * FROM logs WHERE id=?")
.param(id)
.async()
.thenAccept(result -> {
if (result.isError()) {
getLogger().severe("SQL error: " + result.status());
return;
}
// handle result
})
.exceptionally(ex -> {
getLogger().severe("Async execution failed: " + ex.getMessage());
return null;
});Every query uses two high-level response types:
ResponseData:
Used for SELECT queries.
Contains:
ResponseCodestatus()List<Map<String,Object>>rows()- Typed getters (
getFirstString,getFirstInteger, etc.)
ResponseCode:
Enum describing the result of any query:
SUCCESS- Query ran successfully & returned/updated rowsNO_RESULTS- Query succeeded but returned/updated nothingERROR- SQL or driver-level errorFAILED- Unexpected internal failureNOT_INITIALIZED- Database not ready or manager shut down
Example:
db.query()
.sql("SELECT * FROM players WHERE uuid=?")
.param(uuid)
.async()
.thenAccept(result -> {
switch (result.status()) {
case SUCCESS -> { /* handle rows */ }
case NO_RESULTS -> getLogger().info("Player not found.");
case ERROR -> getLogger().severe("SQL error.");
case FAILED -> getLogger().severe("Unexpected failure.");
}
});hostname: "localhost"
port: 3306
username: "root"
password: ""
database: "minecraft"
thread-pool-size: 4
# HikariCP
maximum-pool-size: 8
minimum-idle: 2
connection-timeout: 3000
idle-timeout: 300000
max-lifetime: 1800000
leak-detection-threshold: 0
keepalive-time: 300000
# JDBC URL template:
jdbc-url: "jdbc:mysql://{hostname}:{port}/{database}?user={username}&useSSL=false"| Key | Description |
|---|---|
| maximum-pool-size | Max concurrent DB connections |
| minimum-idle | Min idle connections kept alive |
| connection-timeout | Max wait time before a connection request fails |
| idle-timeout | How long idle connections live |
| max-lifetime | Max lifetime before a connection is recycled |
| leak-detection-threshold | Logs slow connections (debugging) |
| keepalive-time | Periodic keepalive queries |
You can freely change the jdbc-url template to match any JDBC-compatible driver:
- MySQL
- MariaDB
- PostgreSQL (
jdbc:postgresql://{hostname}:{port}/{database}?user={username}) - SQLite (
jdbc:sqlite:{database}.db) - SQLServer (
jdbc:sqlserver://{hostname}:{port};databaseName={database}) - H2 (
jdbc:h2:./{database})
If you have questions or ideas for improvements, feel free to open an issue or reach out to me. Your feedback helps make CentralDatabase better for everyone.
MIT — free to use, modify, and distribute.
Thanks for checking out CentralDatabase!
If you have suggestions, find a bug, or want to contribute, feel free to open an issue or pull request.