MikArt

Entities and Queries

Insert, page, update, and delete typed records or raw entity records.

The entity API has two layers:

LayerUse it when
Entity<T>You have a Java record type and want typed reads and writes
RecordsYou need dynamic fields, transfers, migrations, or runtime schemas

Both layers use the same query, predicate, mutation, and paging types.

Typed Entities

TypedEntity.java
var accounts = tava.entity(Account.class);

Account saved = accounts.insert(new Account(
        UUID.randomUUID(),
        "ada@example.com",
        7,
        null
));

var page = accounts.find(Query.builder()
        .where(Predicate.eq("email", saved.email()))
        .limit(1)
        .build());

Entity<T> maps between the record type and EntityRecord internally. Insert calls return the stored record, which is useful when the backend generates fields.

Dynamic Records

DynamicRecords.java
import eu.mikart.tava.data.EntityRecord;

var records = tava.records("accounts");

records.insert(EntityRecord.builder()
        .set("id", UUID.randomUUID())
        .set("email", "grace@example.com")
        .set("score", 11)
        .build());

Use Records for migration code, data transfer transforms, admin tools, or schemas that are not represented by Java record classes.

Predicates

Predicates are composable and backend-neutral.

Predicates.java
Predicate activeHighValue = Predicate.and(
        Predicate.eq("status", "active"),
        Predicate.gte("score", 100)
);

Predicate named = Predicate.or(
        Predicate.startsWith("email", "ops@"),
        Predicate.contains("tags", "internal")
);
PredicatePurpose
all()Match every record
eq(field, value)Equality
ne(field, value)Inequality
lt, lte, gt, gteOrdered comparisons
in(field, values)Field is one of several values
contains(field, value)Backend-supported containment
startsWith(field, value)Prefix matching
and(...), or(...)Junctions

Sorting, Projection, And Limits

QueryBuilder.java
var query = Query.builder()
        .where(Predicate.eq("status", "active"))
        .project("id", "email", "createdAt")
        .sort(Sort.desc("createdAt"))
        .limit(50)
        .build();

project(...) narrows the returned fields. sort(...) accepts one or more Sort.asc(...) or Sort.desc(...) values. limit(0) means the adapter default is used.

Cursor Paging

Find calls return a Page<T> with the current items and an optional cursor for the next page.

Paging.java
String cursor = null;

do {
    var page = accounts.find(Query.builder()
            .where(Predicate.eq("status", "active"))
            .sort(Sort.asc("email"))
            .limit(500)
            .cursor(cursor)
            .build());

    for (Account account : page.items()) {
        export(account);
    }

    cursor = page.nextCursor();
} while (cursor != null);

Updates And Deletes

Updates use Mutation. Deletes use a predicate directly.

Mutate.java
long updated = accounts.update(
        Predicate.eq("email", "ada@example.com"),
        Mutation.builder()
                .set("score", 8)
                .build()
);

long deleted = accounts.delete(Predicate.eq("status", "disabled"));

Predicate scope matters

update and delete operate on every record matched by the predicate. Build admin workflows so broad predicates are deliberate and visible.

Last updated on

Last updated on

On this page