PojoQuery Documentation

What is PojoQuery?
PojoQuery is a lightweight Java library for working with relational databases that takes a different approach to Object-Relational Mapping (ORM). Instead of using Java classes as table definitions, PojoQuery uses them as query definitions that shape the result set. This simple yet powerful paradigm eliminates many common ORM problems while keeping your database access code clean and maintainable.
The PojoQuery Difference: Your Java classes define what you want to get from the database, not how data is stored, freeing you from the object-relational impedance mismatch.
Key Features
| Feature | Description |
|---|---|
Type-safe queries |
Define queries using Java POJOs with full type safety and IDE support |
Smart SQL generation |
Automatic SQL generation with optimized JOINs based on your POJO structure |
Relationship mapping |
Easily map one-to-one, one-to-many, and many-to-many relationships |
No session management |
No lazy loading, no proxies, no complex session tracking |
Complete control |
Full access to customize SQL when needed while keeping the ORM advantages |
Clean POJO model |
No need to extend base classes or implement interfaces |
Multi-database support |
Works with MySQL, PostgreSQL, HSQLDB, and most JDBC-compliant databases |
Inheritance mapping |
Support for table-per-subclass inheritance patterns |
Embedded objects |
Map complex object structures to database tables with |
Dynamic columns |
Handle schemaless patterns with |
Rationale
The main difference with conventional Object Relational Mapping (ORM) is that types (Java classes) do not double as table definitions but rather as query definitions. More precisely, the POJO defines the shape of the resultset. This implies that type definitions must be cycle-free.
This principle is the key to avoiding lazy loading and other complexities of conventional ORM. See this article about model-driven ORM.
The hardest part of building a software system is deciding what to build […], not the actual implementation difficulties.
PojoQuery allows you to focus on the data you need for each use case rather than trying to shoehorn your domain model into database tables.
Quick Example
This self-contained example demonstrates PojoQuery’s core philosophy: POJOs define what you want to retrieve, not how data is stored. The SchemaGenerator creates tables from your POJOs, and then you can query them with automatic joins.
| Code examples use public fields for brevity. PojoQuery works equally well with private fields and JavaBean-style getters/setters. |
package examples.bookstore;
import java.util.List;
import javax.sql.DataSource;
import org.hsqldb.jdbc.JDBCDataSource;
import org.pojoquery.DB;
import org.pojoquery.DbContext;
import org.pojoquery.PojoQuery;
import org.pojoquery.annotations.Id;
import org.pojoquery.annotations.Table;
import org.pojoquery.schema.SchemaGenerator;
/**
* Self-contained example demonstrating PojoQuery's core philosophy:
* POJOs define *what you want to retrieve*, not how data is stored.
*/
public class BookstoreExample {
@Table("author")
static class Author {
@Id
Long id;
String name;
String country;
}
@Table("book")
static class Book {
@Id
Long id;
String title;
Integer year;
Author author;
}
@Table("review")
static class Review {
@Id
Long id;
Book book;
Integer rating;
String comment;
}
static class BookDetail extends Book {
List<Review> reviews;
}
public static void main(String[] args) {
// 1. Create an in-memory database
DataSource db = createDatabase();
// 2. Generate tables (and foreign keys) from the joint set of all
// fields and associations of the POJOs
SchemaGenerator.createTables(db, Author.class, Book.class, BookDetail.class, Review.class);
DB.withConnection(db, c -> {
// 3. Insert test data
Author tolkien = new Author();
tolkien.name = "J.R.R. Tolkien";
tolkien.country = "UK";
PojoQuery.insert(c, tolkien);
Book lotr = new Book();
lotr.title = "The Lord of the Rings";
lotr.year = 1954;
lotr.author = tolkien;
PojoQuery.insert(c, lotr);
Review r1 = new Review();
r1.book = lotr;
r1.rating = 5;
r1.comment = "A masterpiece!";
PojoQuery.insert(c, r1);
Review r2 = new Review();
r2.book = lotr;
r2.rating = 5;
r2.comment = "Epic fantasy at its finest.";
PojoQuery.insert(c, r2);
// 4. Query with automatic joins - the POJO shape defines what you get!
List<BookDetail> books = PojoQuery.build(BookDetail.class)
.addWhere("{author}.country = ?", "UK")
.execute(c);
for (BookDetail book : books) {
System.out.println(book.title + " by " + book.author.name);
System.out.println("Reviews: " + book.reviews.size());
for (Review review : book.reviews) {
System.out.println(" ★" + review.rating + " - " + review.comment);
}
}
});
}
static DataSource createDatabase() {
JDBCDataSource ds = new JDBCDataSource();
ds.setUrl("jdbc:hsqldb:mem:bookstore");
ds.setUser("SA");
ds.setPassword("");
DbContext.setDefault(DbContext.forDialect(DbContext.Dialect.HSQLDB));
return ds;
}
}
Key insight: BookDetail and BookWithAuthor both map to the same book table, but produce different result shapes. The POJO structure tells PojoQuery which JOINs to generate—no XML configuration, no lazy loading surprises.
When to Use PojoQuery
-
When you need to fetch complex, nested data structures with a single query
-
When you want direct control over the SQL being generated
-
When you need to create different "views" or projections of your data
-
When you want to avoid the complexity of ORM session management
-
In applications with complex read/query patterns but simple write operations
-
When performance and predictability are priorities
Getting Started
To begin using PojoQuery in your project, see our Getting Started guide.
Documentation Structure
-
Getting Started - Setup and basic usage
-
Features - Overview of PojoQuery capabilities
-
Annotations - Detailed reference for all annotations
-
Query Building - Using the fluent API to construct queries
-
Database Support - Information on supported databases and configuration
-
Advanced Topics - Inheritance, custom joins, streaming, and more
-
Examples - Practical code examples
Building from Source
To build PojoQuery from the source code:
-
Prerequisites: Ensure you have JDK 17 or later installed.
-
Clone the repository:
git clone https://github.com/martijnvogten/pojoquery.git -
Navigate to the project directory:
cd pojoquery -
Build with Maven Wrapper:
-
On Linux/macOS:
./mvnw clean install -
On Windows:
mvnw.cmd clean install
-
This will compile the code, run tests, and install the artifact into your local Maven repository.