Skip to main content

6 posts tagged with "java"

View All Tags

TeaQL 1.0.0 Release Notes

· 3 min read
TeaQL Code Gen
Core Contributor

We are thrilled to announce the release of TeaQL 1.0.0! This milestone marks the stabilization of our core APIs, major performance improvements, and a completely redefined model-driven development experience across both Rust and Java (Spring Boot) ecosystems.

TeaQL 1.0.0 focuses on API Elegance, Developer Ergonomics, AI-Native Workflows, and Transaction Safety.

TeaQL Rust: From Java Feature Parity to Multi-database Runtime

· 3 min read
TeaQL Code Gen
Core Contributor

TeaQL started with a mature Java implementation. The Rust work does not try to clone every Java framework feature. It carries over the high-level programming model and rebuilds the runtime in Rust.

That shift matters.

Java proved the generated business API style. Rust turns that style into a runtime direction for PostgreSQL, MySQL, SQLite, embedded SQLite, and memory-backed tests.

What Carries Over from Java

The useful Java ideas are not Spring-specific. They are TeaQL-specific:

  • generated Q APIs;
  • readable query builders;
  • field and relation selectors;
  • SmartList style result metadata;
  • runtime context;
  • checker infrastructure;
  • translated validation messages;
  • mutation events;
  • graph writes;
  • relation enhancement;
  • relation aggregate enhancement;
  • safe value access.

Rust implements those ideas with Rust types, traits, crates, and provider registration.

What Does Not Carry Over Directly

Some Java features should not be copied blindly:

  • Spring Boot autoconfiguration;
  • Java GraphQL generation;
  • Java BaseService/controller conventions;
  • reflection-style runtime mechanisms;
  • the full Java database dialect matrix.

Rust needs a Rust-native shape. The runtime should be explicit, crate-based, and provider-backed.

The Rust Workspace

The Rust runtime is split by responsibility:

  • teaql-core for metadata, values, query model, entity traits, and SmartList<T>;
  • teaql-sql for SQL compilation and dialect behavior;
  • teaql-runtime for UserContext, repositories, checkers, events, relation enhancement, graph writes, and memory execution;
  • teaql-macros for #[derive(TeaqlEntity)];
  • provider crates for SQLx PostgreSQL, SQLx MySQL, SQLx SQLite, and rusqlite SQLite.

This keeps the core runtime separate from database adapters.

Generated Rust Crates

teaql-code-gen can emit Rust service crates. A generated crate can include:

  • entity structs;
  • Q facade;
  • generated request builders;
  • behavior skeletons;
  • checker skeletons;
  • runtime module registration;
  • repository and behavior registries;
  • provider-backed runtime helpers.

Application code then uses generated APIs:

let platforms = Q::platforms()
.select_merchant_list_with(
Q::merchants()
.select_name()
.which_names_contain("TeaQL"),
)
.execute_for_list(&ctx)
.await?;

Multi-database Runtime

The Rust direction is provider-based:

  • SQLx PostgreSQL for production-grade PostgreSQL backends;
  • SQLx MySQL for common enterprise MySQL systems;
  • SQLx SQLite for local-first apps, tests, and small services;
  • rusqlite SQLite for embedded and multi-architecture deployments;
  • MemoryRepository for no-database tests and demos.

The generated API should stay stable while the provider changes below it.

Current State

The Rust runtime is already useful for core generated-domain paths:

  • typed queries;
  • relation loading;
  • grouped aggregates;
  • graph writes;
  • checkers;
  • events;
  • schema bootstrap;
  • SQL debug logs;
  • generated high-level Q API validation against SQLite and PostgreSQL paths.

Full Java feature parity is not the only metric. The better metric is whether Rust has a coherent generated business API runtime.

That is now the direction.

TeaQL vs MyBatis: an order page example

· 3 min read
TeaQL Code Gen
Core Contributor

MyBatis is a productive and familiar tool for Java teams. It gives developers direct control over SQL and mapping. For many simple services, that is enough.

The pressure appears when a business page needs more than one table and more than one query shape.

Consider an order page.

The Order Page Shape

A typical order page may need:

  • the order list;
  • customer name;
  • order status;
  • line item preview;
  • line item count;
  • status counts for tabs;
  • pagination;
  • merchant or tenant filtering;
  • permission-aware visibility.

With mapper-oriented persistence, this often becomes several mapper methods, XML fragments, DTO assembly, and post-query stitching.

The MyBatis Shape

A MyBatis implementation might involve:

  • one SQL query for the order list;
  • one query or join for customer fields;
  • one query for line item previews;
  • one query for counts;
  • one query for grouped status statistics;
  • result maps or manual DTO mapping;
  • service code to assemble the final response.

That is explicit and controllable, but the business intent is spread across files.

The TeaQL Shape

TeaQL tries to express the page as one generated business request:

User userOrderInfo = Q.users()
.filterWithId(userId)
.countOrder()
.facetByOrderStatus("statusWithCount", Q.orderStatus().countOrders())
.selectOrderList(
Q.ordersWithId()
.selectOrderId()
.selectDate()
.offset(0, 10)
.selectLineItemList(
Q.lineItemsWithId().selectImageURL().limit(3)
)
.countLineItems()
)
.execute(context);

The API is still explicit, but the explicitness is at the business level:

  • user;
  • orders;
  • status counts;
  • order list;
  • line items;
  • pagination;
  • line item count.

What TeaQL Reduces

TeaQL does not eliminate thinking. It reduces repetitive plumbing:

  • mapper XML;
  • duplicated DTO assembly;
  • repeated relation-loading code;
  • repeated count/statistics query fragments;
  • stringly-typed field references;
  • one-off page query services.

The generated model becomes the shared vocabulary.

Where MyBatis Still Wins

MyBatis remains a good choice when:

  • a query must be hand-tuned;
  • SQL is the clearest expression of the business requirement;
  • the domain model is small;
  • teams need exact control over vendor-specific SQL;
  • generated APIs would add weight without reducing repetition.

TeaQL and MyBatis do not need to be ideological opposites. TeaQL is valuable when the business model is large enough that generated APIs reduce repeated work.

The Review Difference

In a TeaQL code review, the reviewer can ask:

  • does this page select the correct relations?
  • are the counts grouped correctly?
  • is pagination applied at the right level?
  • does the runtime context apply tenant and permission policy?

That is a higher-level review than checking XML fragments and DTO stitching.

The Short Version

MyBatis gives direct SQL control. TeaQL gives generated business APIs.

For complex business pages, TeaQL keeps page intent closer to the domain model.

What is TeaQL?

· 2 min read
TeaQL Code Gen
Core Contributor

TeaQL is a generated business API platform for systems where the domain model is more important than the storage plumbing around it.

Instead of asking developers to repeatedly write repositories, SQL fragments, DTO stitching, relation loading code, and validation glue, TeaQL starts from a domain model and generates APIs that read like business intent.

The current positioning is simple:

Generated Business APIs. Java-proven. Rust-powered. Multi-database ready.

The Core Idea

TeaQL separates the business API from the runtime provider.

Domain Model
-> TeaQL Generator
-> Generated Q API
-> Runtime Provider
-> MySQL / PostgreSQL / SQLite / Memory / Edge / Agent

Application code should talk to generated APIs. Runtime code should decide how those APIs execute against a database, memory repository, embedded store, or service runtime.

What TeaQL Generates

Depending on the target stack, TeaQL can generate:

  • entity types;
  • query builders;
  • safe expression helpers;
  • relation loading helpers;
  • checker and behavior skeletons;
  • graph save entrypoints;
  • runtime module registration;
  • agent guidance for generated APIs.

In Java, this appears as fluent generated request APIs. In Rust, generated crates expose Q::entities() style query builders over teaql-rs.

Why It Matters

Large business systems repeat the same concepts across many screens and services:

  • users and roles;
  • orders and line items;
  • customers and accounts;
  • status counts and dashboards;
  • parent-child graph saves;
  • tenant, permission, audit, and localization policies.

Without a generated business API layer, those concepts spread across SQL, mapper XML, repositories, service methods, DTOs, validators, and frontend-specific response shaping.

TeaQL keeps the model vocabulary visible in the code path.

Java and Rust

Java TeaQL is the mature, proven path for enterprise systems and Spring Boot services.

Rust TeaQL is the runtime direction for generated domain APIs across PostgreSQL, MySQL, SQLite, embedded SQLite, and memory-backed tests.

The two stacks are not identical, and they do not need to be. They share the programming model: generated APIs over a domain model, executed through a runtime boundary.

A Small Example

User userOrderInfo = Q.users()
.filterWithId(userId)
.countOrder()
.statsFromOrder("statusWithCount", Q.orders().count().groupByOrderStatus())
.selectOrderList(
Q.ordersWithId()
.selectOrderId()
.selectDate()
.offset(0, 10)
.countLineItems()
)
.execute(context);

This is not just a query. It is the shape of a business page expressed through generated APIs.

TeaQL in One Sentence

TeaQL turns domain models into stable business APIs that humans and AI tools can read, compose, and run across Java, Rust, and multiple database providers.

Multi-Stack Code Generation

· One min read
TeaQL Code Gen
Core Contributor

TeaQL Code Gen learned Rust. One model definition now produces both Java and Rust artifacts.

Separate Stack Templates

templates/
java/
entity.java.vm
repository.java.vm
rust/
entity.rs.vm
query.rs.vm

Each stack has isolated templates. No cross-language leakage.

Rust Lib Generators

// Generated from the same .teaql model
pub struct User {
pub id: u64,
pub name: String,
}

impl TeaQLEntity for User {
const TABLE: &str = "user_t";
}

Typed Query Return Types

let names: Vec<String> = db.query(User::name())
.filter(User::active().eq(true))
.list()
.await?;

The generator produces typed projections. User::name() returns String, User::class() returns User.

Explicit Aggregation APIs

let stats = db.query(Order::class())
.group_by(Order::status())
.agg(Order::total().sum())
.agg(Order::id().count())
.list()
.await?;

Group-by and rollup DSLs are fully typed. The macro validates aggregation compatibility at compile time.

Relation Generation

let user = db.load(User::class(), id)
.with(User::orders())
.await?;

User::orders() is generated from the model's relation definition. No hand-written joins.

From XML Models to Java Persistence Code

· 3 min read
TeaQL Code Gen
Core Contributor

Historical note: This article documents an earlier TeaQL generation target. Early versions emitted backend templates, UI pages, mobile clients, and service scaffolding directly into application workspaces. Modern TeaQL has moved to a clearer boundary: generation produces versioned libraries, deterministic business APIs, query DSLs, and runtime capabilities that applications consume as artifacts. This is more friendly to AI-assisted coding and DevOps because AI agents can focus on business workflows, integrations, tests, and product behavior, while generated capabilities are reviewed, tested, published, upgraded, and rolled back through normal dependency and release pipelines.

The first useful shape of the generator was not a UI scaffold. It was a persistence scaffold.

Looking at the code changes, the core work concentrated around FieldDescriptor, ObjectDescriptor, CMRField, Java POJO templates, JDBC mapper templates, DAO implementations, SQL templates, and manager methods. That tells a clear story: the generator was learning to convert a domain description into an executable Java data layer.

Generated Java Object Model

The object templates started to produce more than plain fields:

  • typed Java members from model fields
  • constants for field names and table columns
  • generated toString, JSON serialization, and null-safe formatting
  • parent references and child reference lists
  • version fields for optimistic updates

The important shift was that the generated entity became a stable contract. DAO, mapper, manager, serializer, and JSP snippets could all depend on the same metadata.

DAO, Mapper, and Manager Layers

The generator added separate templates for:

  • DAO interface
  • JDBC template implementation
  • row mapper
  • load and save helpers
  • manager interface
  • manager implementation
  • manager exceptions

This separation mattered because it made generated code patchable by layer. SQL mapping problems could be fixed in mapper templates. Business entry points could evolve in manager templates. Persistence behavior could change without rewriting the domain model.

Multi-Database SQL Output

The early templates included MySQL, then added MSSQL support. Even at this stage, SQL was treated as generated infrastructure, not handwritten project code.

That design became a recurring theme: database differences should live behind generator templates and runtime helpers, while application code talks to generated typed APIs.

Relationship Handling

The generator also started handling parent and child relationships:

  • loading referenced objects
  • saving object graphs without duplicating unchanged rows
  • ordering fields deterministically
  • producing mapper code for references

This was the beginning of TeaQL's later object-graph save model. The early code was still template-heavy, but the direction was already visible: describe the domain once, then generate the repetitive persistence surface.