first commit

This commit is contained in:
2026-01-15 22:38:46 +03:00
commit a70e9b7a79
58 changed files with 3980 additions and 0 deletions

33
docs/api-query.md Normal file
View File

@@ -0,0 +1,33 @@
# Query API
Raw SQL is available via `api.query()` with parameter binding.
## Execute
```java
int rows = api.query().execute(
"UPDATE players SET level = level + 1 WHERE id = ?",
java.util.Collections.singletonList(1)
);
```
## Query
```java
List<String> names = api.query().query(
"SELECT name FROM players WHERE level >= ?",
java.util.Collections.singletonList(10),
rs -> rs.getString("name")
);
```
## Transactions
```java
try (Transaction tx = api.beginTransaction()) {
tx.execute("UPDATE players SET level = level + 1 WHERE id = ?", java.util.Collections.singletonList(1));
tx.commit();
} catch (Exception ex) {
// rollback on error if needed
}
```

35
docs/api-schema.md Normal file
View File

@@ -0,0 +1,35 @@
# Schema API
Schema helpers let you create and update tables programmatically.
## Example: create table
```java
import com.andrewkydev.database.schema.ColumnSpec;
import com.andrewkydev.database.schema.IndexSpec;
import com.andrewkydev.database.schema.TableSpec;
TableSpec table = TableSpec.builder("players")
.column(ColumnSpec.builder("id", "BIGINT").primaryKey(true).autoIncrement(true).nullable(false).build())
.column(ColumnSpec.builder("name", "VARCHAR(32)").nullable(false).build())
.index(new IndexSpec("players_name_idx", java.util.Arrays.asList("name"), false))
.build();
api.schema().createTable(table);
```
## API Methods
```java
api.schema().createDatabase("primalix");
api.schema().dropDatabase("primalix");
api.schema().createTable(spec);
api.schema().dropTable("players");
api.schema().addColumn("players", ColumnSpec.builder("level", "INT").build());
api.schema().updateColumn("players", ColumnSpec.builder("level", "INT").nullable(false).build());
api.schema().dropColumn("players", "level");
api.schema().addIndex("players", new IndexSpec("players_name_idx", java.util.Arrays.asList("name"), false));
api.schema().dropIndex("players", "players_name_idx");
```
All methods also have async variants returning `CompletableFuture`.

40
docs/config.md Normal file
View File

@@ -0,0 +1,40 @@
# Configuration
The default config file is `config.yml`:
```yaml
driver: "mysql"
host: "localhost"
port: 3306
database: "primalix"
username: "root"
password: ""
adminDatabase: "postgres"
autoTransactions: true
pool:
maxPoolSize: 10
minIdle: 2
connectionTimeoutMs: 30000
idleTimeoutMs: 600000
maxLifetimeMs: 1800000
```
## Fields
- `driver`: `mysql` or `postgres`.
- `host`: database host.
- `port`: database port.
- `database`: default database name for the pool.
- `username`: login user.
- `password`: login password.
- `adminDatabase`: PostgreSQL admin database used for create/drop database.
- `autoTransactions`: wrap schema operations in a transaction when possible.
## Pool Options
- `maxPoolSize`: maximum connections in pool.
- `minIdle`: minimum idle connections.
- `connectionTimeoutMs`: connection timeout.
- `idleTimeoutMs`: idle timeout.
- `maxLifetimeMs`: maximum connection lifetime.

91
docs/examples.md Normal file
View File

@@ -0,0 +1,91 @@
# Feature Examples
## 1) Auto ID after insert
```java
PlayerModel player = new PlayerModel();
player.setName("Steve");
api.orm().insert(player);
long id = player.getId();
```
## 2) snake_case default
```java
class PlayerStats {
private int totalKills;
}
// table: player_stats, column: total_kills
```
## 3) findOneWhere & deleteWhere
```java
PlayerModel one = api.orm().findOneWhere(
PlayerModel.class,
"level >= ?",
java.util.Collections.singletonList(10)
);
int deleted = api.orm().deleteWhere(
PlayerModel.class,
"level < ?",
java.util.Collections.singletonList(1)
);
```
## 4) @DbColumn(length/unique/nullable)
```java
@DbColumn(length = 32, unique = true, nullable = false)
private String username;
```
## 5) findWhere with order/limit/offset
```java
List<PlayerModel> page = api.orm().findWhere(
PlayerModel.class,
"level >= ?",
java.util.Collections.singletonList(10),
"level DESC",
10,
0
);
```
## 6) Query Builder
```java
List<PlayerModel> top = api.orm().query(PlayerModel.class)
.where("level >= ?", 10)
.orderBy("level DESC")
.limit(10)
.list();
```
## 7) Query Builder + Fluent Conditions + Join
```java
import static com.andrewkydev.database.orm.Conditions.*;
List<PlayerModel> rows = api.orm().query(PlayerModel.class)
.select("players.*")
.join("LEFT JOIN clans ON clans.id = players.clan_id")
.where(eq("players.status", "ACTIVE").and(gt("players.level", 10)))
.groupBy("players.id")
.having("COUNT(clans.id) > ?", 0)
.orderBy("players.level DESC")
.limit(10, 0)
.list();
```
## 8) Custom types (UUID/JSON)
```java
@DbJson
private java.util.Map<String, Object> metadata;
// UUID is built-in. Custom type example:
api.orm().registerAdapter(MyType.class, new TypeAdapter<MyType>() { ... });
```

43
docs/orm-query.md Normal file
View File

@@ -0,0 +1,43 @@
# ORM Query Builder
Fluent query builder on top of the ORM.
## Basic
```java
List<PlayerModel> top = api.orm().query(PlayerModel.class)
.where("level >= ?", 10)
.orderBy("level DESC")
.limit(10)
.list();
```
## Fluent Conditions
```java
import static com.andrewkydev.database.orm.Conditions.*;
List<PlayerModel> players = api.orm().query(PlayerModel.class)
.where(eq("status", "ACTIVE").and(gt("level", 10)))
.orderBy("level DESC")
.list();
```
## Joins + Group By + Having
```java
List<PlayerModel> rows = api.orm().query(PlayerModel.class)
.select("players.*")
.join("LEFT JOIN clans ON clans.id = players.clan_id")
.where("players.level >= ?", 10)
.groupBy("players.id")
.having("COUNT(clans.id) > ?", 0)
.orderBy("players.level DESC")
.limit(10, 0)
.list();
```
## Select Columns
If you select a subset of columns, only those are mapped.
Missing fields keep default values.

30
docs/orm-schema.md Normal file
View File

@@ -0,0 +1,30 @@
# ORM Schema Generation
`OrmSchema` builds a `TableSpec` from annotated entities.
## Example
```java
import com.andrewkydev.database.orm.OrmSchema;
import com.andrewkydev.database.schema.SqlDialect;
import com.andrewkydev.database.schema.TableSpec;
TableSpec spec = OrmSchema.fromEntity(PlayerModel.class, SqlDialect.MYSQL);
api.schema().createTable(spec);
```
## Type Mapping
Default mapping (when `@DbColumn(type = "...")` is not specified):
- `String` -> `VARCHAR(length)`
- `int`/`Integer` -> `INT`
- `long`/`Long` -> `BIGINT`
- `short`/`Short` -> `SMALLINT`
- `boolean`/`Boolean` -> `BOOLEAN` (PostgreSQL) or `TINYINT(1)` (MySQL)
- `float`/`Float` -> `FLOAT`
- `double`/`Double` -> `DOUBLE`
- `UUID` -> `UUID` (PostgreSQL) or `CHAR(36)` (MySQL)
- `@DbJson` -> `JSON` (MySQL) or `JSONB` (PostgreSQL)
`@DbColumn(length, unique, nullable)` is also applied.

119
docs/orm.md Normal file
View File

@@ -0,0 +1,119 @@
# ORM
The ORM is lightweight: it maps fields to columns and does not manage complex
relationships. It supports sync and async operations.
## Annotations
- `@DbEntity(table = "players")` sets table name. Default: snake_case class name.
- `@DbColumn(name = "username")` overrides column name.
- `@DbColumn(type = "VARCHAR(32)")` overrides SQL type for schema generation.
- `@DbColumn(length = 32, unique = true, nullable = false)` influences schema generation.
- `@DbId(autoIncrement = true)` marks the primary key.
- `@DbJson` stores the field as JSON.
- `@DbTransient` ignores the field.
## Example entity
```java
@DbEntity(table = "players")
public class PlayerModel {
@DbId(autoIncrement = true)
private long id;
@DbColumn(name = "username", nullable = false, unique = true, length = 32)
private String name;
@DbColumn
private int level;
@DbJson
private java.util.Map<String, Object> metadata;
@DbTransient
private String temp;
public PlayerModel() {
}
}
```
## CRUD
```java
EntityManager orm = api.orm();
orm.insert(entity);
orm.update(entity);
orm.delete(entity);
PlayerModel player = orm.findById(PlayerModel.class, 1L);
List<PlayerModel> all = orm.findAll(PlayerModel.class);
PlayerModel one = orm.findOneWhere(PlayerModel.class, "level >= ?", java.util.Collections.singletonList(10));
List<PlayerModel> many = orm.findWhere(PlayerModel.class, "level >= ?", java.util.Collections.singletonList(10));
List<PlayerModel> paged = orm.findWhere(PlayerModel.class, "level >= ?", java.util.Collections.singletonList(10), "level DESC", 10, 0);
long count = orm.count(PlayerModel.class, "level >= ?", java.util.Collections.singletonList(10));
boolean exists = orm.exists(PlayerModel.class, "username = ?", java.util.Collections.singletonList("Steve"));
int deleted = orm.deleteWhere(PlayerModel.class, "level < ?", java.util.Collections.singletonList(1));
```
## Query Builder
```java
EntityManager orm = api.orm();
List<PlayerModel> top = orm.query(PlayerModel.class)
.where("level >= ?", 10)
.orderBy("level DESC")
.limit(10)
.list();
boolean exists = orm.query(PlayerModel.class)
.where("username = ?", "Steve")
.exists();
```
## Fluent Conditions
```java
import static com.andrewkydev.database.orm.Conditions.*;
List<PlayerModel> players = orm.query(PlayerModel.class)
.where(eq("status", "ACTIVE").and(gt("level", 10)))
.list();
```
## Joins, Group By, Having
```java
List<PlayerModel> rows = orm.query(PlayerModel.class)
.select("players.*")
.join("LEFT JOIN clans ON clans.id = players.clan_id")
.groupBy("players.id")
.having("COUNT(clans.id) > ?", 0)
.list();
```
Selecting a subset of columns maps only those fields; missing fields keep default values.
## Auto ID on insert
If `@DbId(autoIncrement = true)` is used and the id is empty (0 or null),
`insert()` will read generated keys and set the id back on the entity.
## Custom Type Adapters
UUID mapping is built-in. For other types:
```java
orm.registerAdapter(SomeType.class, new TypeAdapter<SomeType>() {
@Override
public Object toDatabase(SomeType value) {
return value == null ? null : value.toString();
}
@Override
public SomeType fromDatabase(Object value) {
return value == null ? null : SomeType.parse(value.toString());
}
});
```

40
docs/overview.md Normal file
View File

@@ -0,0 +1,40 @@
# Overview
Database is a Lumi plugin that provides:
- HikariCP-backed MySQL/PostgreSQL connections.
- Schema helpers for creating/updating tables and indexes.
- Raw SQL queries with parameter binding.
- Lightweight ORM with annotations, fluent query builder, and async support.
## Install
1. Build the jar with Gradle.
2. Put the jar into your server plugins folder.
3. Start the server once to generate `config.yml`.
## Accessing the API
```java
import com.andrewkydev.database.DatabaseProvider;
import com.andrewkydev.database.DatabaseApi;
DatabaseApi api = DatabaseProvider.get();
```
## Feature Highlights
### Auto ID on insert
If an entity uses `@DbId(autoIncrement = true)` and the id value is empty (0 or null),
`insert()` will fetch generated keys and assign the id back to the object.
### Snake case by default
If `@DbEntity` and `@DbColumn` names are not set, class and field names are converted to
snake_case.
### Query helpers
`findOneWhere`, `deleteWhere`, and `findWhere` with sorting/limit/offset are available
in the ORM.

33
docs/ru/api-query.md Normal file
View File

@@ -0,0 +1,33 @@
# Query API (RU)
Raw SQL через `api.query()` с биндингом параметров.
## Execute
```java
int rows = api.query().execute(
"UPDATE players SET level = level + 1 WHERE id = ?",
java.util.Collections.singletonList(1)
);
```
## Query
```java
List<String> names = api.query().query(
"SELECT name FROM players WHERE level >= ?",
java.util.Collections.singletonList(10),
rs -> rs.getString("name")
);
```
## Транзакции
```java
try (Transaction tx = api.beginTransaction()) {
tx.execute("UPDATE players SET level = level + 1 WHERE id = ?", java.util.Collections.singletonList(1));
tx.commit();
} catch (Exception ex) {
// rollback при ошибке
}
```

35
docs/ru/api-schema.md Normal file
View File

@@ -0,0 +1,35 @@
# Schema API (RU)
Помощники для создания и обновления таблиц.
## Пример: create table
```java
import com.andrewkydev.database.schema.ColumnSpec;
import com.andrewkydev.database.schema.IndexSpec;
import com.andrewkydev.database.schema.TableSpec;
TableSpec table = TableSpec.builder("players")
.column(ColumnSpec.builder("id", "BIGINT").primaryKey(true).autoIncrement(true).nullable(false).build())
.column(ColumnSpec.builder("name", "VARCHAR(32)").nullable(false).build())
.index(new IndexSpec("players_name_idx", java.util.Arrays.asList("name"), false))
.build();
api.schema().createTable(table);
```
## Методы
```java
api.schema().createDatabase("primalix");
api.schema().dropDatabase("primalix");
api.schema().createTable(spec);
api.schema().dropTable("players");
api.schema().addColumn("players", ColumnSpec.builder("level", "INT").build());
api.schema().updateColumn("players", ColumnSpec.builder("level", "INT").nullable(false).build());
api.schema().dropColumn("players", "level");
api.schema().addIndex("players", new IndexSpec("players_name_idx", java.util.Arrays.asList("name"), false));
api.schema().dropIndex("players", "players_name_idx");
```
У всех методов есть async версии с `CompletableFuture`.

40
docs/ru/config.md Normal file
View File

@@ -0,0 +1,40 @@
# Конфигурация
Файл `config.yml`:
```yaml
driver: "mysql"
host: "localhost"
port: 3306
database: "primalix"
username: "root"
password: ""
adminDatabase: "postgres"
autoTransactions: true
pool:
maxPoolSize: 10
minIdle: 2
connectionTimeoutMs: 30000
idleTimeoutMs: 600000
maxLifetimeMs: 1800000
```
## Поля
- `driver`: `mysql` или `postgres`.
- `host`: адрес БД.
- `port`: порт БД.
- `database`: основная база.
- `username`: логин.
- `password`: пароль.
- `adminDatabase`: PostgreSQL админ‑база для create/drop database.
- `autoTransactions`: оборачивать schema операции в транзакцию.
## Pool
- `maxPoolSize`: максимум соединений.
- `minIdle`: минимум idle.
- `connectionTimeoutMs`: таймаут подключения.
- `idleTimeoutMs`: таймаут простоя.
- `maxLifetimeMs`: максимальная жизнь соединения.

43
docs/ru/orm-query.md Normal file
View File

@@ -0,0 +1,43 @@
# ORM Query Builder (RU)
Fluent builder над ORM.
## База
```java
List<PlayerModel> top = api.orm().query(PlayerModel.class)
.where("level >= ?", 10)
.orderBy("level DESC")
.limit(10)
.list();
```
## Fluent Conditions
```java
import static com.andrewkydev.database.orm.Conditions.*;
List<PlayerModel> players = api.orm().query(PlayerModel.class)
.where(eq("status", "ACTIVE").and(gt("level", 10)))
.orderBy("level DESC")
.list();
```
## Join + Group By + Having
```java
List<PlayerModel> rows = api.orm().query(PlayerModel.class)
.select("players.*")
.join("LEFT JOIN clans ON clans.id = players.clan_id")
.where("players.level >= ?", 10)
.groupBy("players.id")
.having("COUNT(clans.id) > ?", 0)
.orderBy("players.level DESC")
.limit(10, 0)
.list();
```
## Select Columns
Если выбираете часть колонок, маппятся только они.
Остальные поля остаются дефолтными.

30
docs/ru/orm-schema.md Normal file
View File

@@ -0,0 +1,30 @@
# ORM Schema (RU)
`OrmSchema` строит `TableSpec` на основе аннотаций.
## Пример
```java
import com.andrewkydev.database.orm.OrmSchema;
import com.andrewkydev.database.schema.SqlDialect;
import com.andrewkydev.database.schema.TableSpec;
TableSpec spec = OrmSchema.fromEntity(PlayerModel.class, SqlDialect.MYSQL);
api.schema().createTable(spec);
```
## Маппинг типов
По умолчанию:
- `String` -> `VARCHAR(length)`
- `int`/`Integer` -> `INT`
- `long`/`Long` -> `BIGINT`
- `short`/`Short` -> `SMALLINT`
- `boolean`/`Boolean` -> `BOOLEAN` (PostgreSQL) или `TINYINT(1)` (MySQL)
- `float`/`Float` -> `FLOAT`
- `double`/`Double` -> `DOUBLE`
- `UUID` -> `UUID` (PostgreSQL) или `CHAR(36)` (MySQL)
- `@DbJson` -> `JSON` (MySQL) или `JSONB` (PostgreSQL)
`@DbColumn(length, unique, nullable)` применяется автоматически.

119
docs/ru/orm.md Normal file
View File

@@ -0,0 +1,119 @@
# ORM (RU)
Легковесный ORM: поля мапятся в колонки, без сложных связей.
Есть sync и async версии.
## Аннотации
- `@DbEntity(table = "players")` - имя таблицы. По умолчанию snake_case.
- `@DbColumn(name = "username")` - имя колонки.
- `@DbColumn(type = "VARCHAR(32)")` - SQL тип для генерации схемы.
- `@DbColumn(length = 32, unique = true, nullable = false)` - влияет на schema генерацию.
- `@DbId(autoIncrement = true)` - primary key.
- `@DbJson` - JSON поле.
- `@DbTransient` - игнорировать поле.
## Пример сущности
```java
@DbEntity(table = "players")
public class PlayerModel {
@DbId(autoIncrement = true)
private long id;
@DbColumn(name = "username", nullable = false, unique = true, length = 32)
private String name;
@DbColumn
private int level;
@DbJson
private java.util.Map<String, Object> metadata;
@DbTransient
private String temp;
public PlayerModel() {
}
}
```
## CRUD
```java
EntityManager orm = api.orm();
orm.insert(entity);
orm.update(entity);
orm.delete(entity);
PlayerModel player = orm.findById(PlayerModel.class, 1L);
List<PlayerModel> all = orm.findAll(PlayerModel.class);
PlayerModel one = orm.findOneWhere(PlayerModel.class, "level >= ?", java.util.Collections.singletonList(10));
List<PlayerModel> many = orm.findWhere(PlayerModel.class, "level >= ?", java.util.Collections.singletonList(10));
List<PlayerModel> paged = orm.findWhere(PlayerModel.class, "level >= ?", java.util.Collections.singletonList(10), "level DESC", 10, 0);
long count = orm.count(PlayerModel.class, "level >= ?", java.util.Collections.singletonList(10));
boolean exists = orm.exists(PlayerModel.class, "username = ?", java.util.Collections.singletonList("Steve"));
int deleted = orm.deleteWhere(PlayerModel.class, "level < ?", java.util.Collections.singletonList(1));
```
## Query Builder
```java
EntityManager orm = api.orm();
List<PlayerModel> top = orm.query(PlayerModel.class)
.where("level >= ?", 10)
.orderBy("level DESC")
.limit(10)
.list();
boolean exists = orm.query(PlayerModel.class)
.where("username = ?", "Steve")
.exists();
```
## Fluent Conditions
```java
import static com.andrewkydev.database.orm.Conditions.*;
List<PlayerModel> players = orm.query(PlayerModel.class)
.where(eq("status", "ACTIVE").and(gt("level", 10)))
.list();
```
## Join, Group By, Having
```java
List<PlayerModel> rows = orm.query(PlayerModel.class)
.select("players.*")
.join("LEFT JOIN clans ON clans.id = players.clan_id")
.groupBy("players.id")
.having("COUNT(clans.id) > ?", 0)
.list();
```
Если выбрать часть колонок, маппятся только они, остальные остаются дефолтными.
## Auto ID
Если `@DbId(autoIncrement = true)` и id пустой (0/null),
`insert()` получает generated keys и прописывает id в объект.
## Type Adapters
UUID уже поддержан. Для других типов:
```java
orm.registerAdapter(SomeType.class, new TypeAdapter<SomeType>() {
@Override
public Object toDatabase(SomeType value) {
return value == null ? null : value.toString();
}
@Override
public SomeType fromDatabase(Object value) {
return value == null ? null : SomeType.parse(value.toString());
}
});
```

34
docs/ru/overview.md Normal file
View File

@@ -0,0 +1,34 @@
# Overview (RU)
Database - SQL plugin for Lumi with MySQL/PostgreSQL, HikariCP pooling,
schema helpers, raw SQL, and a lightweight ORM with fluent query builder.
## Установка
1. Соберите jar через Gradle.
2. Поместите jar в папку plugins сервера.
3. Запустите сервер один раз для генерации `config.yml`.
## Доступ к API
```java
import com.andrewkydev.database.DatabaseProvider;
import com.andrewkydev.database.DatabaseApi;
DatabaseApi api = DatabaseProvider.get();
```
## Основные возможности
### Auto ID после insert
Если у поля есть `@DbId(autoIncrement = true)` и id пустой (0 или null),
`insert()` получает generated keys и прописывает id в объект.
### Snake case по умолчанию
Если не задано `@DbEntity`/`@DbColumn`, имена берутся в snake_case.
### Удобные ORM методы
Доступны `findOneWhere`, `deleteWhere` и `findWhere` с сортировкой/лимитом/offset.