Jinx analyzes your JPA annotations at compile time, generates schema snapshots (JSON), and produces DDL SQL by diffing schema changes over time.
Liquibase YAML is supported as an output dialect, but SQL is the primary and most thoroughly validated output format.
MySQL & PostgreSQL | JDK 21+ Required | Latest Version: 0.1.4 | JPA 3.2.0 Supported
Jinx exists to make database schema evolution explicit, reviewable, and automation‑friendly.
DDL is generated from JPA metadata instead of being handwritten. Typos, missing columns, and inconsistent constraints are eliminated before they reach production.
Jinx outputs plain SQL files. Schema changes can be reviewed, discussed, and approved just like application code.
Because Jinx produces SQL files, migrations integrate naturally into existing CI/CD pipelines without requiring a live database connection.
Schema analysis and diffing operate purely on snapshot files. You can generate and validate migrations without connecting to an actual database.
Generated SQL files can be committed to Git. If you do not want to introduce a dedicated migration runtime, Git itself becomes your schema history and audit trail.
Jinx performs schema analysis using annotation processing at compile time. It does not rely on runtime reflection and does not strictly follow the reflection‑based JPA specification model.
This design is intentional:
- Deterministic schema generation
- Zero runtime metadata requirements
- Compatibility with AOT‑oriented build pipelines
As the Java ecosystem continues to reduce reflection usage, Jinx remains naturally aligned with static and reproducible builds.
Jinx does not replace JPA runtimes such as Hibernate. It focuses exclusively on schema analysis and migration generation.
DDL SQL is Jinx’s first‑class output and receives the most validation.
Liquibase output is provided as a compatible dialect for teams that already rely on Liquibase for execution and tracking.
Liquibase support is not the core model, but a translation layer on top of SQL generation.
dependencies {
annotationProcessor("io.github.yyubin:jinx-processor:0.1.4")
implementation("io.github.yyubin:jinx-core:0.1.4")
}@Entity
public class Bird {
@Id @GeneratedValue
private Long id;
private String name;
private Long zooId;
}Snapshots are generated automatically during compilation:
build/classes/java/main/jinx/
Snapshot naming format:
schema-<yyyyMMddHHmmss>.json
Example snapshot:
{
"entities": {
"org.example.Bird": {
"tableName": "Bird",
"columns": {
"bird::id": { "type": "BIGINT", "primaryKey": true, "autoIncrement": true },
"bird::name": { "type": "VARCHAR(255)" },
"bird::zoo_id": { "type": "BIGINT" }
},
"indexes": {
"ix_bird__zoo_id": { "columns": ["zoo_id"] }
}
}
}
}jinx db migrate \
-p build/classes/java/main/jinx \
-d mysql \
--out build/jinx \
--rollback \
--liquibase# PostgreSQL
jinx db migrate \
-p build/classes/java/main/jinx \
-d postgresql \
--out build/jinx \
--rollbackNote: PostgreSQL support via the CLI is currently limited. Using the Gradle plugin with
dialect.set("postgresql")is the recommended approach for PostgreSQL projects.
Example SQL output:
CREATE TABLE `Bird` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255),
`zoo_id` BIGINT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE INDEX `ix_bird__zoo_id` ON `Bird` (`zoo_id`);plugins {
id("io.github.yyubin.jinx") version "0.1.4"
}jinx {
profile.set("local")
naming {
maxLength.set(63)
strategy.set("SNAKE_CASE")
}
database {
dialect.set("mysql")
}
output {
format.set("sql")
directory.set("build/jinx")
}
}To use PostgreSQL:
database {
dialect.set("postgresql") // or "mysql"
}Settings are applied in this order (higher overrides lower):
Gradle DSL / -A compiler options > jinx.yaml > defaults
Create jinx.yaml in your project root. Jinx searches upward from the working directory, so a file at the repository root covers all subprojects.
profiles:
dev:
naming:
maxLength: 30
strategy: NO_OP
prod:
naming:
maxLength: 63
strategy: SNAKE_CASEActivate a profile (in order of precedence):
- Gradle DSL:
profile.set("prod") - Environment variable:
JINX_PROFILE=prod - Default:
dev
Maximum length for generated constraint and index names.
| Default | Recommended values |
|---|---|
30 |
PostgreSQL: 63 / MySQL: 64 |
Controls how logical names (Java field/class names) are converted to physical column names.
| Value | Behavior | Example |
|---|---|---|
NO_OP |
No conversion (default) | myColumn → myColumn |
SNAKE_CASE |
camelCase → snake_case | myColumn → my_column |
jinx {
profile.set("prod")
naming {
maxLength.set(63)
strategy.set("SNAKE_CASE")
}
}Gradle DSL values override jinx.yaml. The plugin translates them into -A compiler arguments automatically.
When using the annotation processor without the Gradle plugin:
compileJava {
options.compilerArgs += [
'-Ajinx.naming.maxLength=63',
'-Ajinx.naming.strategy=SNAKE_CASE',
'-Ajinx.profile=prod'
]
}| Key | Description |
|---|---|
jinx.naming.maxLength |
Maximum constraint/index name length |
jinx.naming.strategy |
Naming strategy (NO_OP or SNAKE_CASE) |
jinx.profile |
Active profile for jinx.yaml lookup |
| Option | Description |
|---|---|
db migrate |
Generate SQL by diffing schema snapshots |
promote-baseline |
Promote the current snapshot as the baseline |
-d, --dialect |
Database dialect (mysql, postgresql) |
--rollback |
Generate rollback SQL |
--liquibase |
Output Liquibase YAML |
--force |
Allow potentially destructive changes |
When adding or dropping multiple tables at once, Jinx automatically determines the correct execution order using a topological sort over foreign key dependencies.
No manual ordering required — Jinx analyzes the FK graph and produces CREATE/DROP statements in the correct order regardless of the order entities appear in your code.
| Operation | Order |
|---|---|
CREATE TABLE |
Parent tables before child tables |
DROP TABLE |
Child tables (FK owners) before parent tables |
Supported topologies:
- Linear chains:
A → B → C - Diamond shapes:
A → C,B → C - Join tables:
AB → A,AB → B - Multiple independent trees
If a circular FK dependency is detected, Jinx logs a warning and falls back to the original order safely.
- Table, column, primary key, index, and constraint diffing
- FK dependency-based table ordering — CREATE and DROP order is automatically determined from foreign key relationships
- Rollback SQL generation
- Liquibase YAML output
- MySQL dialect (default)
- PostgreSQL dialect — double-quoted identifiers, BIGSERIAL/SERIAL, SEQUENCE,
BOOLEAN,uuid,BYTEA,DOUBLE PRECISION,TIMESTAMP WITH TIME ZONE
https://github.com/yyubin/jinx-test
- New database dialects
- Improved DDL or Liquibase mappings
- Tests and documentation
Pull requests and issues are welcome.