Fetching latest headlines…
PetStore: Pure Java for UI and Safe SQL Mapping
NORTH AMERICA
πŸ‡ΊπŸ‡Έ United Statesβ€’April 2, 2026

PetStore: Pure Java for UI and Safe SQL Mapping

10 views0 likes0 comments
Originally published byDev.to

I would like to introduce the PetStore sample application, which demonstrates a pure "Java-first" approach to web interface development. The entire project is built on the idea of maximum type safety and clarity, achieved through two modules of the Ujorm 3 library. These effectively eliminate common abstraction layers that often complicate development and debugging. The PetStore is built on the lightweight Ujorm3 framework.

Screenshot

πŸ› οΈ Two Pillars of Ujorm3

1. UI Creation without Templates (ujo-web)

We have replaced traditional engines like Thymeleaf or JSP with pure Java code.

  • Type-safe rendering: HTML is generated using the HtmlElement builder and try-with-resources blocks. This approach allows writing Java code in a natural tree structure that faithfully mirrors the HTML structure.
  • Refactoring with full IDE support: Since the UI is defined in Java, everything you are used to works – autocomplete (IntelliSense), instant refactoring (e.g., extracting a table into a renderTable() method), and correctness checking while writing.
  • No more parameter errors: The HttpParameter interface uses enums to define web parameters. This practically eliminates typos in form field names, which in standard solutions only manifest at runtime.

2. Modern Database Handling (ujo-orm)

Forget about complex XML mapping or runtime errors in SQL queries.

  • Using Java Records: Standard Java records serve as domain objects (Pet, Category). They are naturally immutable, clean, and fully compatible with @Table and @Column annotations.
  • Type-Safe SQL Builder: An annotation processor generates metamodels (e.g., MetaPet) during compilation. The compiler catches an error in a column name, not an application crash in production.
  • SQL under control: No unexpected LazyInitializationException or hidden N+1 problems. You have absolute control over every SqlQuery. Moreover, you can easily map results from native SQL back to records using the label() method.

πŸ“ Code Sample (PetServlet)

The project is designed with an emphasis on straightforwardness.
The following example from a stateless servlet demonstrates how elegantly logic, parameters, and HTML generation can be connected:

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
    var ctx = HttpContext.ofServlet(req, resp);
    var contextPath = req.getContextPath();
    var action = ctx.parameter(ACTION, Action::paramValueOf);
    var petId = ctx.parameter(PET_ID, Long::parseLong);

    var pets = services.getPets();
    var categories = services.getCategories();
    var petToEdit = (Action.EDIT.equals(action) && petId != null)
            ? services.getPetById(petId).orElse(null)
            : null;

    try (var html = HtmlElement.of(ctx, BOOTSTRAP_CSS)) {
        try (var body = html.addBody(Css.container, Css.mt5)) {
            renderHeader(body, contextPath);
            renderTable(body, pets);
            renderForm(body, petToEdit, categories);
        }
    }
}

Here is what a native SQL query looks like in pure Java:

static final EntityManager<Pet, Long> PET_EM = 
             EntityManager.of(Pet.class);

public List<Pet> findAll() {
    var sql = """
        SELECT p.id AS ${p.id}
        , p.name    AS ${p.name}
        , p.status  AS ${p.status}
        , c.id      AS ${c.id}
        , c.name    AS ${c.name}
        FROM pet p
        LEFT JOIN category c ON c.id = p.category_id
        WHERE p.id >= :id
        ORDER BY p.id
        """;

    return SqlQuery.run(connection.get(), query -> query
        .sql(sql)
        .label("p.id", MetaPet.id)
        .label("p.name", MetaPet.name)
        .label("p.status", MetaPet.status)
        .label("c.id", MetaPet.category, MetaCategory.id)
        .label("c.name", MetaPet.category, MetaCategory.name)
        .bind("id", 1L)
        .streamMap(PET_EM.mapper())
        .toList());
}

πŸ’‘ Why Choose This Approach?

This architecture represents an interesting alternative for developers who are tired of heavy JPA frameworks or bloated frontend technologies.

Where Ujorm PetStore shines most:

  • B2B and administrative applications: Where development speed and long-term maintainability are important.
  • Microservices: Thanks to minimal overhead and fast startup.
  • Projects with HTMX: It perfectly complements modern trends of returning to server-side rendering.

The "Java-First" philosophy drastically reduces context switching between Java, SQL, XML, and various templating languages.
Everything you need is under the protection of the compiler.

πŸš€ Try It Locally

The application utilizes the best of the current ecosystem:

  • Java 25
  • Spring Boot 3.5.0
  • H2 Database (In-memory)

All you need is JDK 25 and Maven installed, then just run:

mvn spring-boot:run

The application will start at http://localhost:8080.

Resources and Links:

  • PetServlet.java – A stateless Servlet acting as both Controller and View. It handles HTTP communication and builds the HTML.
  • Dao.java – Data access layer integrating Spring JDBC with Ujorm EntityManager.
  • Ujorm 3 Library on GitHub – Official library repository.
  • ORM Benchmarks – How this approach compares to the competition.

Does it make sense to you to have the UI and DB layers so tightly coupled with the compiler? I will be glad for any technical feedback!

Comments (0)

Sign in to join the discussion

Be the first to comment!