Astral Realms Documentation Help

Database (ORM)

AstralCore ships a lightweight annotation-based ORM on top of HikariCP. It auto-generates SQL for standard CRUD operations; no XML or query strings required.

Connection Configuration

See database.properties for the full connection pool reference.

Defining an Entity

Annotate a class with @Entity and mark fields with the ORM annotations:

import com.astralrealms.core.storage.annotation.*; import com.astralrealms.core.model.Unique; @Entity("players") // table name public class MinecraftPlayer implements Unique { @Id private UUID uniqueId; // primary key @Column("player_name") private String name; @Column("server_id") @NonUpdatable // never included in UPDATE statements private String serverId; @CreatedAt private long createdAt; // auto-set on INSERT @UpdatedAt private long updatedAt; // auto-set on INSERT and UPDATE @Transient // not persisted private transient boolean online; // getters, constructors … }

Annotation Reference

Annotation

Target

Description

@Entity(value)

Class

Marks a class as a persistent entity. value is the table name.

@Id

Field

Primary key column. Should be UUID.

@Column(value)

Field

Maps the field to a column name. Omit to use the field name as-is.

@CreatedAt

Field (long)

Automatically set to System.currentTimeMillis() on INSERT.

@UpdatedAt

Field (long)

Automatically set on INSERT and every UPDATE.

@NonUpdatable

Field

Excluded from generated UPDATE statements.

@Transient

Field

Excluded from all SQL (not stored).

CrudRepository

CrudRepository<T extends Unique> provides auto-generated async SQL for the annotated entity:

CrudRepository<MinecraftPlayer> repo = new CrudRepository<>(databaseService, MinecraftPlayer.class);

Basic CRUD

// Find by primary key repo.findById(uuid).thenAccept(opt -> opt.ifPresent(this::cache)); // Find all rows repo.findAll().thenAccept(players -> { }); // Insert repo.insert(player); // Update repo.update(player); // Upsert (insert or update) repo.upsert(player); // Delete repo.delete(uuid); // Existence check repo.exists(uuid).thenAccept(exists -> { }); // Count repo.count().thenAccept(n -> { });

Query by Column

// Find one by a specific column value repo.findByColumn("player_name", "Steve") .thenAccept(opt -> { }); // Find all matching repo.findAllByColumn("server_id", "lobby-1") .thenAccept(list -> { }); // Multiple column conditions (AND) repo.findByColumns(Map.of("server_id", "lobby-1", "player_name", "Steve")) .thenAccept(opt -> { }); // Delete by column repo.deleteByColumn("server_id", "lobby-1");

Custom SQL

repo.findOne("SELECT * FROM players WHERE lower(player_name) = ?", List.of("steve")) .thenAccept(opt -> { }); repo.findMany("SELECT * FROM players WHERE server_id = ? ORDER BY created_at", List.of("lobby-1")) .thenAccept(list -> { });

Batch Operations

// Batch insert repo.insertBatch(List.of(player1, player2, player3)); // Batch update repo.updateBatch(players); // Batch upsert repo.upsertBatch(players);

Pagination

Pageable pageable = Pageable.of(0, 20); // page 0, 20 rows per page repo.findAll(pageable).thenAccept(page -> { List<MinecraftPlayer> content = page.content(); int total = page.totalElements(); int pages = page.totalPages(); });

DatabaseService

DatabaseService provides the underlying connection pool and transaction support:

DatabaseService db = AstralCore.get().database(); // Async query returning a result db.supply(connection -> { PreparedStatement stmt = connection.prepareStatement("SELECT COUNT(*) FROM players"); ResultSet rs = stmt.executeQuery(); rs.next(); return rs.getInt(1); }).thenAccept(count -> { }); // Async void operation db.run(connection -> { connection.createStatement().execute("TRUNCATE temp_data"); }); // Transaction — auto-rollback on exception db.transaction(connection -> { // multiple operations in one transaction insertSomething(connection); updateSomething(connection); }); // Synchronous (blocking) int count = db.runSync(connection -> { // … });

CachedRepository

CachedRepository<T> combines CrudRepository and CacheRepository into a two-tier read-through pattern:

public class PlayerRepository extends CachedRepository<MinecraftPlayer> { public PlayerRepository(DatabaseService db, CacheService cache) { super(db, cache, MinecraftPlayer.class, "player", Duration.ofMinutes(10)); } }

On findById: checks Redis first; on cache miss, queries the database and caches the result automatically. On save: writes to the database and updates the cache. On delete: removes from both layers.

Supported Databases

Upsert behaviour is automatically adapted to the detected database engine:

Database

Upsert SQL

MariaDB / MySQL

INSERT … ON DUPLICATE KEY UPDATE

PostgreSQL / H2

INSERT … ON CONFLICT DO UPDATE

23 April 2026