MikArt

Introduction

A portable Java 21 entity layer for relational, document, and key-value databases.

Tava is a modular Java 21 database toolkit built around one canonical entity model. You describe records once, then run the same shape against PostgreSQL, MySQL, MariaDB, SQLite, H2, SQL Server, Oracle, MongoDB, or DynamoDB.

It is intentionally small. Tava does not try to hide every backend feature behind a lowest-common-denominator API. The portable layer covers schemas, records, queries, schema planning, migrations, and transfers. Backend-specific work stays available through typed native handles.

Design rule

The logical schema is authoritative. Adapter hints can change how a field is stored, but they should not change what the field means in the application model.

What Tava Gives You

AreaWhat you use
Entity modelJava records, EntityRecord, Schema.builder()
Schema controltava.plan(schema), SchemaPlan, ApplyOptions
Data accessEntity<T>, Records, Query, Predicate, Sort
Migration historytava.migrations(), Migration, _tava_migrations
Data movementDataTransfer.copy(...), transforms, progress callbacks
Escape hatchnativeHandle(...), nativeAccess().withNative(...)

A Complete Pass

AccountExample.java
import eu.mikart.tava.Tava;
import eu.mikart.tava.data.Page;
import eu.mikart.tava.postgres.Postgres;
import eu.mikart.tava.query.Predicate;
import eu.mikart.tava.query.Query;
import eu.mikart.tava.query.Sort;
import eu.mikart.tava.schema.GeneratedValue;
import eu.mikart.tava.schema.Schema;
import eu.mikart.tava.schema.annotation.Entity;
import eu.mikart.tava.schema.annotation.Field;
import eu.mikart.tava.schema.annotation.Generated;
import eu.mikart.tava.schema.annotation.Identity;
import eu.mikart.tava.schema.annotation.Required;
import eu.mikart.tava.schema.annotation.Unique;

import java.time.Instant;
import java.util.UUID;

@Entity("accounts")
record Account(
        @Identity UUID id,
        @Required @Unique @Field(length = 255) String email,
        int score,
        @Generated(GeneratedValue.NOW) Instant createdAt
) {}

Schema schema = Schema.builder()
        .record(Account.class)
        .build();

try (Tava tava = Tava.open(Postgres.connect(jdbcUrl, user, password))) {
    tava.plan(schema).apply();

    var accounts = tava.entity(Account.class);
    accounts.insert(new Account(UUID.randomUUID(), "ada@example.com", 7, null));

    Page<Account> page = accounts.find(Query.builder()
            .where(Predicate.eq("email", "ada@example.com"))
            .sort(Sort.desc("createdAt"))
            .limit(50)
            .build());
}

Where To Go Next

Read Getting Started first if you are wiring Tava into an application. Jump to Schema if you already know which adapter you want, or Adapters if the storage target is still open.

Last updated on

Last updated on

On this page