Skip to content

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vev

Vev 1.0.0 is the first stable release of the documented native persistence API. It targets JDK 27 and PostgreSQL 18. The optional Jakarta 4 facade remains nonconforming; full Hibernate replacement in ReAI is ongoing. See the release scope and evidence and installation instructions.

Vev provides an ahead-of-time, PostgreSQL-first persistence model for modern JVM applications. It is deliberately not a general ORM or a drop-in Hibernate implementation. Instead of recreating a stateful ORM session, Vev interprets a small selection of Jakarta Persistence annotations as source metadata and compiles them into immutable mapping metadata, closed typed query tokens, and direct bind/read plans used by a stateless persistence API. Unsupported mappings are intended to fail compilation rather than acquire approximate runtime behavior.

The native API is stable within its documented profile. Safety and performance claims remain limited to the published evidence; this release does not establish a general advantage over Hibernate or another persistence implementation.

The current baseline is:

  • JDK 27, with no compatibility target for older JDKs;
  • exactly PostgreSQL major 18; the current CI fixture is PostgreSQL 18.6;
  • the preview jakarta.persistence:jakarta.persistence-api:4.0.0-M6 contract;
  • a closed, documented selection of Jakarta Persistence annotations reused as Vev metadata, not a conforming subset of the provider specification.

Jakarta Persistence 4.0.0-M6 is a milestone release. Vev's annotation profile and API may change as the specification changes. Do not infer compatibility with a final Jakarta Persistence 4 release.

The Jakarta Persistence 4 @Entity contract forbids records as entities. Vev deliberately requires immutable records and therefore uses selected Jakarta annotations as nonconforming source metadata. A Vev record is not a Jakarta entity, cannot simultaneously be managed by Hibernate or another Jakarta provider, and requires a separate or rewritten record model during migration.

Design direction

The optional Jakarta-facing facade is shaped like the preview EntityAgent, which performs operations without a persistence context and returns detached entities. The current vev-jakarta4 adapter is deliberately nonconforming: it implements only selected operations and does not yet honor every inherited option, property, lifecycle, or exception contract. It must not be treated as a Jakarta provider implementation. Its selected surface maps onto the native TransactionExecutor, lexical ReadTx/WriteTx, and explicit ReadEntities/WriteEntities contracts. Generated per-entity plans contain binders, row readers, and mapping metadata; the PostgreSQL runtime constructs and caches the only permitted SQL shapes from that validated metadata.

The current immutable-record facade supports detached find/get, ordered multiple lookups, assigned-value insert, and homogeneous insert batches. Jakarta's void update and refresh operations are rejected before SQL because an immutable snapshot cannot be synchronized in place. The native API supports assigned insertion, generated identity creation, optimistic updates, and opt-in version-checked physical deletion for generated identities. The facade continues to reject delete; create-capable upsert remains unavailable.

insertMultiple validates every input and rejects duplicate entity keys before SQL, then sends one fixed PostgreSQL statement containing one typed array per column. PostgreSQL expands the arrays with ordinality, inserts the batch, and returns snapshots in input order; every returned column must equal its validated input or the transaction is poisoned. updateMultiple uses the same typed-array/ordinality shape in one guarded statement. It rejects duplicate keys before SQL and is all-or-nothing: every tenant, identifier, and expected version must match before any row is changed, while one stale or missing row poisons and rolls back the complete lexical transaction. Every returned non-version value must equal the requested input and every version must advance exactly once.

The first generated query family beyond identifiers is deliberately narrow. @VevIndex on an ordinary scalar component generates an exact typed index token for bounded equality pages, with IS NULL available only for a nullable component. Pages use primary-key order by default. An explicit ordered index adds a non-null scalar ordering column and a typed value/ID cursor, with fixed ascending or descending direction. Vev has no arbitrary SQL, runtime query DSL, OFFSET, unbounded query, join, or projection surface yet.

Consequently, the initial design has no transparent dirty checking, lazy entity proxies, session identity map, or implicit cascade graph. Loaded entities are detached ordinary objects. Transaction scope, tenant scope, and writes remain visible in application control flow. The current adapter is available only inside a lexical transaction callback, closes automatically, and must not escape or cross a thread boundary.

@Entity
@Table(name = "catalog_item", schema = "catalog")
public record CatalogItem(
    @Id @Column(name = "id", nullable = false) UUID id,
    @TenantKey @Column(name = "tenant_id", nullable = false) Integer tenantId,
    @Version @Column(name = "version", nullable = false) long version,
    @VevIndex(name = "catalog_item_sku_vev_idx")
    @Column(name = "sku", nullable = false, length = 64) String sku
) {}

@VevModel(entities = CatalogItem.class)
public final class CatalogModel {}

var tenantAuthority = CatalogModelVev.newTenantAuthority();
var vev = new PgVev<>(dataSource, CatalogModelVev.POSTGRES, tenantAuthority);
var tenant = tenantAuthority.scope(42);
var persisted = vev.write(tenant, tx ->
    tx.entities().insert(CatalogItemVev.INSTANCE, item));

var page = vev.read(tenant, tx -> tx.entities().many(
    PgQueries.equal(CatalogItemVev.SKU, "SKU-42", new QueryLimit(100))));

TenantAuthority<Model,T> is an application capability, not a value factory to recreate at each call site. The generated factory binds its phantom model type and mapping fingerprint at compile time. PgVev exclusively reserves the capability, verifies one database endpoint, and claims it only after verification succeeds. The authority can then mint scopes for that runtime and cannot be reused for another PgVev. A cross-model scope does not type-check; erased or foreign scopes are rejected before connection acquisition.

Explicit read-only mappings expose no mutation capabilities and require SELECT-only table access, even inside write transactions. Stored identity/version metadata remains verifiable without granting writes. An explicit external incoming reference option permits unmapped tables to reference a SELECT-only target while retaining exact outgoing and within-model attestation. Explicit shared reference mappings combine @VevShared with read-only access for rows intentionally visible to every tenant. Models containing only shared records declare @VevModel(tenantType = ...) and retain real tenant scopes and the same verified authority.

Tenant-registry references attach to the actual tenant key and preserve its database foreign key without exposing registry data or administration through Vev.

Declared database defaults use @Column(options = "DEFAULT …") as exact bootstrap metadata on VALUE columns. Vev never evaluates these expressions or substitutes them for explicit application values; reads and mutation SQL retain their typed behavior.

Every mapped component must spell out @Column(nullable = true) or @Column(nullable = false); Vev never inherits Jakarta's nullable default. For tenant-owned mappings, the migration must provide the matching schema-qualified table, declared physical primary key, every declared non-unique B-tree index in its exact generated key order and direction, forced row-level security policy, exact column-level INSERT/UPDATE grants and table-level DELETE only for @VevDelete entities, and a fingerprint row matching the generated model identity. Named tenant-scoped @Table(uniqueConstraints = @UniqueConstraint(...)) constraints are verified with immediate NULLS DISTINCT enforcement; named check constraints require exact definitions and approved builtin expression dependencies; undeclared unique indexes remain outside the profile. Explicit @VevReference components require verified foreign keys within the closed model, with immediate non-cascading enforcement: tenant-composite keys for tenant-owned targets, scalar keys for explicitly shared targets. The processor packages a deterministic schema manifest with each compiled model; Vev does not generate or apply migrations.

Java records and Kotlin @JvmRecord data classes may also come from separately compiled class-path dependencies. Their constructor and accessor bytecode is verified at build time without loading application classes.

Larger bounded snapshots can declare a smaller batch/page ceiling with @VevRows, while retaining the compile-verified 64 MiB result estimate.

Binary provides immutable PostgreSQL bytea values with explicit @VevBinary byte bounds and verified database length checks. Large values use smaller row limits; small digests support typed indexes and tenant-scoped uniqueness.

@VevText maps bounded String values to PostgreSQL text, using explicit Jakarta column lengths and verified character-length checks.

Compilation proves the closed mapping model, not a live database. PgVev performs catalog and privilege attestation at startup. It requires a dedicated pgjdbc DataSource whose connections already report the exact pg_catalog search path, UTF-8, DateStyle = ISO, MDY, and IntervalStyle = postgres baseline; Vev rejects retained temporary schemas instead of repairing pooled state. Avoiding per-transaction search_path changes also preserves pgjdbc's prepared-query cache. Applications must preserve this connection and schema contract during deployment and upgrades.

Repository layout

Module Responsibility
vev-core Dialect-neutral transaction and entity contracts
vev-postgres PostgreSQL 18 execution and schema behavior (18.6 currently verified)
vev-processor Ahead-of-time mapping validation and source generation
vev-jakarta4 Deliberately nonconforming Jakarta Persistence 4.0 milestone EntityAgent-shaped facade and annotation adapter
vev-integration-tests Synthetic PostgreSQL integration fixtures
vev-benchmark-vev Isolated Vev JMH workloads
vev-benchmark-hibernate Isolated Hibernate ORM 8.0.0.Beta1 JMH baseline

No artifact is currently published. To verify the source tree with JDK 27:

./gradlew clean check integrationTest

Integration verification requires an administrator connection to disposable PostgreSQL 18. Tests and examples use synthetic data only.

The current reviewed A–B–B–A evidence bundle covers generated indexed reads and the guarded 32-row update against prerelease Hibernate ORM 8.0.0.Beta1. Its latency comparison is explicitly rejected: post-A2 telemetry showed severe unrelated CPU and storage activity, and without comparable pre-run or in-run telemetry host interference cannot be excluded. The unfiltered raw results remain public; only the narrowly scoped, repeatable normalized-allocation observations are retained. An earlier read-only bundle is also preserved. Neither campaign is a general performance claim.

Verification runs locally with the checked-in wrapper; this repository has no GitHub Actions CI. The JDK 27 baseline is currently verified on Oracle OpenJDK release candidate 27+35-2325 with PostgreSQL 18.6. JDK 27 general availability is scheduled for September 15, 2026; final-distribution verification remains a follow-up before the ReAI production rollout. Preview APIs are permitted only where measured performance or a simpler, safer implementation justifies their use.

Safety boundary

Vev is not a drop-in Hibernate replacement. Some annotation spellings are reusable, but a Hibernate/Jakarta entity class is not: the current Vev profile requires a separate immutable record that no Jakarta provider may manage as an entity. Annotation reuse does not imply entity-model, lifecycle, transaction, query, locking, or caching compatibility. Hibernate-specific annotations are not part of the initial public profile unless a document names them explicitly.

Read the supported contract and migration boundaries:

Security reports should follow SECURITY.md. Contributions should follow CONTRIBUTING.md.

License

Apache License 2.0. See LICENSE.

About

EXPERIMENTAL: JDK 26 AOT ORM for Jakarta Persistence 4 and PostgreSQL

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages