RathORM
RathORM is a lightweight ORM and schema migration helper for PostgreSQL. It scans annotated entities, builds table metadata, and applies schema changes in a safe order (create tables, then add columns, then apply constraints).
This repository is intended to be consumed as a Maven library and published manually to GitHub Packages using the provided scripts.
Table of contents
- Overview
- Requirements
- Install as a dependency
- Configuration
- Instrumentation
- Defining entities
- Composite keys
- Schema migration
- Database helpers
- Logging
- Build from source
- Publish to GitHub Packages (manual)
- Consume from GitHub Packages
- Versioning
- Troubleshooting
Overview
Key capabilities:
- Annotation-driven schema metadata using
@DbColumn,@CombinedPrimaryKey, and@CombinedUniqueKey. - Package-based scanning (
rathorm.entity.packages) to find ActiveJDBC models or any class with annotated fields. - Automatic schema synchronization (create tables, add columns, optionally drop extras, then apply constraints for new columns).
- Foreign key, unique, check, default, and not-null constraints on newly created columns and tables.
- Connection utilities for simple read/write flows and batched prepared statements.
Requirements
- Java 21
- PostgreSQL database
- Maven 3.9+ recommended
Install as a dependency
Once published, add RathORM to your project:
<dependency>
<groupId>com.tranztechnologies</groupId>
<artifactId>rathorm</artifactId>
<version>1.0.8</version>
</dependency>
You also need to add the GitHub Packages repository (see "Consume from GitHub Packages").
Configuration
RathORM reads configuration from rathorm.properties on the classpath.
Required keys (as used by the code):
rathorm.db.urlrathorm.db.usernamerathorm.db.passwordrathorm.db.bootstrap(optional, defaults topostgres)rathorm.entity.packages(comma-separated list of packages to scan; required forEntityScanner)
Example src/main/resources/rathorm.properties:
rathorm.db.url=jdbc:postgresql://localhost:5432/rathorm
rathorm.db.username=postgres
rathorm.db.password=postgres
rathorm.db.bootstrap=postgres
rathorm.entity.packages=com.example.domain
Instrumentation
ActiveJDBC models still require bytecode instrumentation. Use the standard ActiveJDBC entry point provided by the activejdbc-instrumentation dependency:
<plugin>
<groupId>org.javalite</groupId>
<artifactId>activejdbc-instrumentation</artifactId>
<version>${activejdbc.version}</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>instrument</goal>
</goals>
</execution>
</executions>
</plugin>
Adjust the arguments as needed for your build system.
Defining entities
Entities are defined with @DbColumn on fields. RathORM supports ActiveJDBC models and plain POJOs; EntityScanner will pick up either as long as the class lives in a configured package.
Basic example
import org.javalite.activejdbc.Model;
import com.tranztechnologies.rathorm.DbColumn;
import com.tranztechnologies.rathorm.SqlType;
public class Customer extends Model {
@DbColumn(name = "id", type = SqlType.BIGINT, primaryKey = true, autoIncrement = true)
private Long id;
@DbColumn(name = "email", type = SqlType.VARCHAR, length = 160, nullable = false, unique = true)
private String email;
}
Supported column metadata
@DbColumn supports:
name(defaults to the field name)type(SqlTypeenum)length,precision,scalenullableprimaryKeyuniqueautoIncrementdefaultValuecheck- Foreign key:
referencesTable,referencesColumn,onUpdate,onDelete
Table naming
By default, table names are generated using ActiveJDBC's Inflector.tableize (e.g., Customer -> customers). You can override the table name using ActiveJDBC's @Table annotation:
@org.javalite.activejdbc.annotations.Table("custom_table")
public class Customer extends Model { /* ... */ }
Tables default to the public schema.
Composite keys
RathORM supports composite primary keys and composite unique keys at the class level.
Composite primary key
import com.tranztechnologies.rathorm.CombinedPrimaryKey;
@CombinedPrimaryKey(columns = {"order_id", "line_no"})
public class OrderLine {
@DbColumn(name = "order_id", type = SqlType.BIGINT)
private Long orderId;
@DbColumn(name = "line_no", type = SqlType.INTEGER)
private Integer lineNo;
}
You may not mix @CombinedPrimaryKey with @DbColumn(primaryKey = true) in the same entity.
Composite unique key
import com.tranztechnologies.rathorm.CombinedUniqueKey;
@CombinedUniqueKey(columns = {"tenant_id", "order_no"})
public class Order {
@DbColumn(name = "tenant_id", type = SqlType.UUID)
private java.util.UUID tenantId;
@DbColumn(name = "order_no", type = SqlType.VARCHAR, length = 40)
private String orderNo;
}
Multiple composite unique keys can be declared by repeating the annotation.
Schema migration
Programmatic use
DbProperties properties = DbProperties.load();
new DatabaseBootstrapper().ensureDatabaseExists(properties);
List<Class<?>> entities = EntityScanner.findConfiguredEntities();
// create tables, add columns, then constraints for newly created columns
SchemaMigrator migrator = new SchemaMigrator();
migrator.syncTables(entities, false);
// drop tables in reverse order with cascade if needed
// migrator.dropTables(entities, true);
Convenience runner
RathORM.create(dropOnly, dropFirst, dropExtraColumns) wraps bootstrapping, entity scanning, and schema sync:
RathORM.create(false, false, false);
- Set
dropFirst=trueto drop tables before recreating them. - Set both
dropOnly=trueanddropFirst=trueto drop tables without re-creating. - Set
dropExtraColumns=trueto remove columns that are not present in your models.
Notes:
- Constraints (defaults, checks, unique keys, primary keys, foreign keys) are applied when the table or column is created during the current run.
- Default values are applied by the database; inserts skip
nullvalues when a default exists.
Database helpers
DbRead<T>andDbTransaction<T>open and close the ActiveJDBC connection for you. Implementmethod()and callread()for non-transactional work orwrite()for transactional work; both returnDbResponse<T>.RathPreparedStatementwrapsBase.startBatch(...)so you can build batches even when some parameters are missing; callfreezeBatches()beforeexecuteBatch()to fill missing placeholders withnull.
Example transaction wrapper:
DbResponse<Integer> result = new DbTransaction<Integer>() {
@Override
public DbResponse<Integer> method() {
// perform JDBC/ActiveJDBC work here
return new DbResponse<>(true, 1, null);
}
}.write();
Logging
RathORM uses SLF4J. This project includes slf4j-simple as the default provider, which logs to stdout. You can replace it with another SLF4J backend in your consuming project (e.g., Logback).
Build from source
./mvnw -DskipTests package
The jar will be created under target/.
Publish to GitHub Packages (manual)
This repo includes manual deployment scripts so you can publish without GitHub Actions.
One-time setup
- Create a GitHub Personal Access Token from your personal account:
- Classic PAT (recommended): https://github.com/settings/tokens/new?scopes=write:packages,read:packages,repo&description=RATH_ORM
- Scopes required:
write:packages,read:packages(recommended),repo - Fine-grained PAT: select the repo under the org and enable
Packages: Read and write - If the org uses SSO, authorize the token for the org after creation
- Ensure your Maven coordinates are correct in
pom.xml:
<dependency>
<groupId>com.tranztechnologies</groupId>
<artifactId>rathorm</artifactId>
<version>1.0.8</version>
</dependency>
- Bump the version before each new publish.
Using a .env file
For manual deployments, you can store the environment variables in .env at the repo root. The deploy scripts will load it automatically.
Example .env:
GITHUB_TOKEN=YOUR_TOKEN
GITHUB_ACTOR=YOUR_GITHUB_USERNAME
GITHUB_REPOSITORY=tranztech/rathorm
# Optional
# MAVEN_ARGS=-DskipTests
Notes:
- This repo ignores
.envin version control. - Use plain
KEY=VALUElines; lines starting with#are treated as comments.
Manual deploy (macOS/Linux)
export GITHUB_TOKEN=YOUR_TOKEN
export GITHUB_ACTOR=YOUR_GITHUB_USERNAME
export GITHUB_REPOSITORY=tranztech/rathorm
./scripts/deploy.sh
Manual deploy (Windows)
set GITHUB_TOKEN=YOUR_TOKEN
set GITHUB_ACTOR=YOUR_GITHUB_USERNAME
set GITHUB_REPOSITORY=tranztech/rathorm
scripts\\deploy.bat
The scripts create a temporary Maven settings file and run:
mvn -DaltDeploymentRepository=github::default::https://maven.pkg.github.com/tranztech/rathorm deploy
Consume from GitHub Packages
In your consumer project:
- Add the repository to
pom.xml:
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/tranztech/rathorm</url>
</repository>
</repositories>
- Add credentials to
~/.m2/settings.xml:
<settings>
<servers>
<server>
<id>github</id>
<username>YOUR_GITHUB_USERNAME</username>
<password>YOUR_GITHUB_TOKEN</password>
</server>
</servers>
</settings>
- Add the dependency:
<dependency>
<groupId>com.tranztechnologies</groupId>
<artifactId>rathorm</artifactId>
<version>1.0.8</version>
</dependency>
Versioning
- Update
<version>inpom.xmlbefore publishing. - GitHub Packages will store each version permanently.
- Avoid re-publishing the same version.
Troubleshooting
Problem: "Could not find rathorm.properties"
- Ensure
rathorm.propertiesis on the classpath (e.g.,src/main/resources).
Problem: No entities found
- Check
rathorm.entity.packagesmatches your package names. - Ensure entities are compiled and available on the classpath.
Problem: Foreign key creation fails
- RathORM applies constraints after all tables/columns are created. If it still fails, confirm the referenced table/column names are correct.
Problem: SLF4J warnings
- Ensure a logging backend is on the runtime classpath. This repo includes
slf4j-simpleby default.
Problem: GitHub Packages auth fails
- Verify token scopes and that the username matches the token owner.
- For private repos, include the
reposcope.