Ujorm PetStore is a practical showcase of a web application built on Avaje Inject and Ujorm 3. The project serves as an inspiration for developing web applications with an emphasis on straightforwardness, maximum type safety, and zero hidden "magic".
Just pure Java, full control over generated SQL, compile-time dependency injection, and HTML rendered safely straight from the code.
The project is designed with an extreme focus on minimalism and zero bloat. The dependency tree consists only of essential, lightweight libraries:
- Avaje Inject: Compile-time dependency injection with no runtime reflection overhead.
- HikariCP: Lightning-fast database connection pooling.
- Ujorm: (
ujo-core,ujo-orm,ujo-web) for database operations and HTML rendering. - Jakarta APIs: (
jakarta.inject,jakarta.persistence,jakarta.servlet) for standardized interfaces.
Because of this lean architecture, the application has an incredibly small memory and storage footprint.
If you configure the H2 database (or any other database driver) as a provided dependency so that it is supplied by the application server (e.g., Tomcat or Jetty), the total size of the compiled WAR file shrinks to a mere ~850 KB (869,458 bytes).
The application demonstrates the power of Ujorm3 modules combined with modern compile-time DI, streamlining development by eliminating common abstractions:
- Immutable Records: Uses modern Java
records as domain objects (Pet,Category), ensuring clean code and absolute immutability while maintaining compatibility with@Tableand@Columnannotations. - Type-Safe SQL Builder: An annotation processor generates metamodels (e.g.,
QPet) at compile-time. This eliminates typos in column names and allows the compiler to catch errors before the app even runs. - SQL Transparency: Unlike heavy JPA frameworks, there are no
LazyInitializationExceptionor hidden N+1 issues. You have full control over theSelectQuery. - The Mapping Advantage: Ujorm bridges the gap between raw SQL and object mapping. You can write native SQL and easily map results to Java records, keeping the SQL debuggable in any DB client.
- Pure Java HTML Rendering: Replaces traditional engines like Thymeleaf or JSP. HTML is rendered directly from Java using the
HtmlElementbuilder andtry-with-resourcesblocks. - Refactoring Power: Since the UI is just Java code, you get full IDE support. Complex UI blocks can be instantly refactored into smaller, reusable methods (e.g.,
renderTable()) without the overhead of fragment files or context passing. - Type Safety: The page structure is verified at compile-time. No more runtime errors caused by a typo in a template variable.
- HttpParameter Interface: Uses
enumimplementations to centralize web parameter definitions, protecting the application from mapping errors or form-name typos.
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:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
var contextPath = req.getContextPath();
var ctx = HttpContext.ofServlet(req, resp);
var action = ctx.parameter(ACTION, Action::paramValueOf, Action.UNKNOWN);
var petId = ctx.parameter(PET_ID, Long::parseLong);
var pets = services.getPets();
var categories = services.getCategories();
var petToEdit = switch(action) {
case EDIT -> services.getPetById(petId);
default -> Optional.<Pet>empty();
};
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 using the generated metamodel:
public List<Pet> findAll(long fromId) {
return SelectQuery.run(connection.get(), PET_EM, query -> query
.columnsOfDomain(true)
.column(QPet.category, QCategory.name)
.where(QPet.id.whereGe(fromId))
.tail("ORDER BY", QPet.id)
.toList());
}- Java: 25
- DI Framework: Avaje Inject 10.4
- ORM and Web: Ujorm 3.0.3 (
ujo-orm,ujo-web) - Database: H2 (In-memory)
- Server: Jetty (via Maven Plugin) / Tomcat compatible
- UI Styling: Bootstrap 5.3.3 (CDN)
AppPetStore.java– Main application logic and transactional service layer.DaoFactory.java– Data access factory containing theDaoFacadeand internal DAOs interacting with UjormEntityManager.Entities.java– Database schema definitions using Java records.PetServlet.java– A stateless Servlet acting as both Controller and View. It handles HTTP communication (PRG pattern) and builds the HTML.Constants.java– Shared enums (Status) and CSS classes.
- Ensure you have JDK 25 .
- Run in the root directory:
./mvnw jetty:run
- Open your browser at: http://localhost:8080
This "rebellious" architecture is ideal for developers seeking a simpler alternative to heavy JPA, Reflection-based DI containers, or complex SPA frontends.
- Use Cases: Perfect for microservices, B2B tools, internal apps, or HTMX-driven projects where productivity, fast startup times, and maintainability are priorities.
- The "Java-First" Philosophy: By keeping everything (SQL mapping, Dependency Injection, UI structure, Logic) within the Java compiler's reach, you minimize context switching and maximize reliability.
Alternative Comparison:
- ORM: MyBatis, Jdbi.
- Web: j2html, Wicket, Vaadin.
- DI: Dagger, Micronaut Inject, Spring Boot
For more technical details and performance metrics, please refer to:
- Ujorm ORM Library – The official project page for the
ORMmodule. - Ujorm Element Library – The official project page for the
UImodule. - Benchmark for Java ORM frameworks – Compare the performance of different
ORMframeworks. - Benchmark for Java WEB frameworks – Compare the performance of different
HTMLrendering engines.
