Skip to main content

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.url
  • rathorm.db.username
  • rathorm.db.password
  • rathorm.db.bootstrap (optional, defaults to postgres)
  • rathorm.entity.packages (comma-separated list of packages to scan; required for EntityScanner)

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 (SqlType enum)
  • length, precision, scale
  • nullable
  • primaryKey
  • unique
  • autoIncrement
  • defaultValue
  • check
  • 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=true to drop tables before recreating them.
  • Set both dropOnly=true and dropFirst=true to drop tables without re-creating.
  • Set dropExtraColumns=true to 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 null values when a default exists.

Database helpers

  • DbRead<T> and DbTransaction<T> open and close the ActiveJDBC connection for you. Implement method() and call read() for non-transactional work or write() for transactional work; both return DbResponse<T>.
  • RathPreparedStatement wraps Base.startBatch(...) so you can build batches even when some parameters are missing; call freezeBatches() before executeBatch() to fill missing placeholders with null.

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

  1. Create a GitHub Personal Access Token from your personal account:
  2. Ensure your Maven coordinates are correct in pom.xml:
<dependency>
<groupId>com.tranztechnologies</groupId>
<artifactId>rathorm</artifactId>
<version>1.0.8</version>
</dependency>
  1. 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 .env in version control.
  • Use plain KEY=VALUE lines; 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:

  1. Add the repository to pom.xml:
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/tranztech/rathorm</url>
</repository>
</repositories>
  1. 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>
  1. Add the dependency:
<dependency>
<groupId>com.tranztechnologies</groupId>
<artifactId>rathorm</artifactId>
<version>1.0.8</version>
</dependency>

Versioning

  • Update <version> in pom.xml before publishing.
  • GitHub Packages will store each version permanently.
  • Avoid re-publishing the same version.

Troubleshooting

Problem: "Could not find rathorm.properties"

  • Ensure rathorm.properties is on the classpath (e.g., src/main/resources).

Problem: No entities found

  • Check rathorm.entity.packages matches 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-simple by default.

Problem: GitHub Packages auth fails

  • Verify token scopes and that the username matches the token owner.
  • For private repos, include the repo scope.