PojoQuery Examples
This page provides practical examples of using PojoQuery.
Example 1: Basic Select
Fetch all users ordered by last name.
POJO:
@Table("user")
public class User {
@Id Long id;
String firstName;
String lastName;
String email;
// Getters and setters omitted for brevity
}
Query:
List<User> users = PojoQuery.build(User.class)
.addOrderBy("user.lastName ASC")
.execute(dataSource);
Example 2: Select with Join
Fetch an article including its author details.
POJOs:
@Table("article")
public class Article {
@Id Long id;
String title;
// Getters and setters omitted for brevity
}
public class ArticleWithAuthor extends Article {
User author; // Automatically joins on 'author_id' column
// Getters and setters omitted for brevity
}
@Table("user")
public class User {
@Id Long id;
String firstName;
String lastName;
// Getters and setters omitted for brevity
}
Query:
ArticleWithAuthor article = PojoQuery.build(ArticleWithAuthor.class)
.addWhere("article.id = ?", 123L)
.execute(dataSource)
.stream().findFirst().orElse(null);
if (article != null) {
System.out.println("Author: " + article.getAuthor().getFirstName());
}
Example 3: One-to-Many Relationship
Fetch an article including a list of its comments.
POJOs:
@Table("article")
public class Article {
@Id Long id;
String title;
// Getters and setters omitted for brevity
}
public class ArticleWithComments extends Article {
List<Comment> comments; // Automatically joins on 'article_id' in comment table
// Getters and setters omitted for brevity
}
@Table("comment")
public class Comment {
@Id Long id;
String text;
Date submitDate;
// Getters and setters omitted for brevity
}
Query:
ArticleWithComments article = PojoQuery.build(ArticleWithComments.class)
.addWhere("article.id = ?", 456L)
.addOrderBy("comments.submitDate DESC") // Order comments
.execute(dataSource)
.stream().findFirst().orElse(null);
if (article != null) {
System.out.println("Comments count: " + article.getComments().size());
article.getComments().forEach(c -> System.out.println("- " + c.getText()));
}
Example 4: Aggregation (@GroupBy, @Select)
Get the count and latest comment date for each article.
POJO:
@Table("comment") // Query starts from comment table
@GroupBy("comment.article_id") // Group by article
public class ArticleCommentStats {
@FieldName("article_id") // Map the grouping key
Long articleId;
@Select("COUNT(*)")
Integer commentCount;
@Select("MAX(submitDate)")
Date lastCommentDate;
// Getters and setters omitted for brevity
}
Query:
List<ArticleCommentStats> stats = PojoQuery.build(ArticleCommentStats.class)
.execute(dataSource);
stats.forEach(s -> System.out.println(
"Article " + s.articleId + ": " + s.commentCount + " comments, last on " + s.lastCommentDate
));
Example 5: Custom SQL Field (@Select)
Fetch user with a calculated full name field.
POJO:
@Table("user")
public class UserWithFullName {
@Id Long id;
String firstName;
String lastName;
@Select("CONCAT(firstName, ' ', lastName)") // Database-specific function
String fullName;
// Getters and setters omitted for brevity
}
Query:
UserWithFullName user = PojoQuery.build(UserWithFullName.class)
.addWhere("user.id = ?", 789L)
.execute(dataSource)
.stream().findFirst().orElse(null);
if (user != null) {
System.out.println("Full Name: " + user.getFullName());
}
Example 6: Insert Operation
Insert a new user record.
POJO: (Same as Example 1)
Operation:
// Use runInTransaction for all write operations
DB.runInTransaction(dataSource, (Connection c) -> {
User newUser = new User();
newUser.setFirstName("Alice");
newUser.setLastName("Wonder");
newUser.setEmail("alice@example.com");
PojoQuery.insert(c, newUser);
// newUser.getId() is now populated if it was auto-generated
System.out.println("New user ID: " + newUser.getId());
});
Example 7: Update Operation
Update an existing user’s email.
POJO: (Same as Example 1)
Operation:
// Use runInTransaction for all write operations
DB.runInTransaction(dataSource, (Connection c) -> {
// Assume 'userToUpdate' was fetched previously
userToUpdate.setEmail("alice.w@example.com");
int updatedRows = PojoQuery.update(c, userToUpdate);
System.out.println("Rows updated: " + updatedRows);
});
Example 8: Many-to-Many Relationship
Fetch articles with their associated tags using a link table.
Database Schema:
-- article table
CREATE TABLE article (id BIGINT PRIMARY KEY, title VARCHAR(255));
-- tag table
CREATE TABLE tag (id BIGINT PRIMARY KEY, name VARCHAR(100));
-- link table (many-to-many)
CREATE TABLE article_tag (
article_id BIGINT REFERENCES article(id),
tag_id BIGINT REFERENCES tag(id),
PRIMARY KEY (article_id, tag_id)
);
POJOs:
@Table("article")
public class ArticleWithTags {
@Id Long id;
String title;
@Link(linktable = "article_tag", linkfield = "article_id", foreignlinkfield = "tag_id")
List<Tag> tags;
// Getters and setters omitted for brevity
}
@Table("tag")
public class Tag {
@Id Long id;
String name;
// Getters and setters omitted for brevity
}
Query:
List<ArticleWithTags> articles = PojoQuery.build(ArticleWithTags.class)
.execute(dataSource);
for (ArticleWithTags article : articles) {
System.out.println(article.getTitle() + " - Tags: " +
article.getTags().stream().map(Tag::getName).collect(Collectors.joining(", ")));
}
Example 9: Filtered Relationships with @JoinCondition
Fetch events with separate lists for visitors and organizers from the same person table.
Database Schema:
POJOs:
@Table("person")
public static class Person {
@Id Long id;
String firstname;
String lastname;
public Person() {}
public Person(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public Long getId() { return id; }
public String getFirstname() { return firstname; }
public String getLastname() { return lastname; }
public String getFullName() { return firstname + " " + lastname; }
}
@Table("event")
public static class EventWithParticipants {
@Id Long id;
String title;
@Link(linktable = "event_person", linkfield = "event_id", foreignlinkfield = "person_id")
@JoinCondition("{this}.id = {linktable}.event_id AND {linktable}.role = 'visitor'")
List<Person> visitors;
@Link(linktable = "event_person", linkfield = "event_id", foreignlinkfield = "person_id")
@JoinCondition("{this}.id = {linktable}.event_id AND {linktable}.role = 'organizer'")
List<Person> organizers;
public Long getId() { return id; }
public String getTitle() { return title; }
public List<Person> getVisitors() { return visitors; }
public List<Person> getOrganizers() { return organizers; }
}
Query:
EventWithParticipants event = PojoQuery.build(EventWithParticipants.class)
.execute(dataSource)
.stream().findFirst().orElse(null);
if (event != null) {
System.out.println("Event: " + event.getTitle());
System.out.println("Visitors (" + event.getVisitors().size() + "):");
for (Person visitor : event.getVisitors()) {
System.out.println(" - " + visitor.getFullName());
}
System.out.println("Organizers (" + event.getOrganizers().size() + "):");
for (Person organizer : event.getOrganizers()) {
System.out.println(" - " + organizer.getFullName());
}
}
Example 10: Embedded Objects
Map address fields from the same table into a nested object.
Database Schema:
POJOs:
public static class Address {
String street;
String city;
String zip;
public Address() {}
public Address(String street, String city, String zip) {
this.street = street;
this.city = city;
this.zip = zip;
}
public String getStreet() { return street; }
public String getCity() { return city; }
public String getZip() { return zip; }
}
@Table("customer")
public static class Customer {
@Id Long id;
String name;
@Embedded(prefix = "ship_")
Address shippingAddress;
@Embedded(prefix = "bill_")
Address billingAddress;
public Customer() {}
public Customer(String name, Address shippingAddress, Address billingAddress) {
this.name = name;
this.shippingAddress = shippingAddress;
this.billingAddress = billingAddress;
}
public Long getId() { return id; }
public String getName() { return name; }
public Address getShippingAddress() { return shippingAddress; }
public Address getBillingAddress() { return billingAddress; }
}
Query:
Customer customer = PojoQuery.build(Customer.class)
.addWhere("{customer}.id = ?", 1L)
.execute(c)
.stream().findFirst().orElse(null);
if (customer != null) {
System.out.println("Customer: " + customer.getName());
System.out.println("Ship to: " + customer.getShippingAddress().getStreet()
+ ", " + customer.getShippingAddress().getCity());
System.out.println("Bill to: " + customer.getBillingAddress().getStreet()
+ ", " + customer.getBillingAddress().getCity());
}
Example 11: Streaming Large Result Sets
Process millions of log entries without loading all into memory.
POJO:
@Table("access_log")
public class AccessLog {
@Id Long id;
String ipAddress;
String requestPath;
Integer statusCode;
LocalDate accessDate;
// Getters and setters omitted for brevity
}
Streaming Query:
LocalDate yesterday = LocalDate.now().minusDays(1);
// Using try-with-resources to ensure proper cleanup
try (Stream<AccessLog> stream = PojoQuery.build(AccessLog.class)
.addWhere("access_log.accessDate = ?", yesterday)
.executeStreaming(dataSource)
.stream()) {
// Count 404 errors without loading all rows
long notFoundCount = stream
.filter(log -> log.getStatusCode() == 404)
.count();
System.out.println("404 errors yesterday: " + notFoundCount);
}
// Alternative: process each row with a callback
DB.queryRowsStreaming(dataSource,
SqlExpression.sql("SELECT * FROM access_log WHERE status_code >= ?", 500),
row -> {
System.out.println("Error on path: " + row.get("request_path"));
});
Example 12: Handling Optional Relationships
Gracefully handle nullable foreign keys.
POJOs:
@Table("task")
public class Task {
@Id Long id;
String title;
User assignee; // Will be null if assignee_id is null (LEFT JOIN by default)
// Getters and setters omitted for brevity
}
@Table("user")
public class User {
@Id Long id;
String name;
// Getters and setters omitted for brevity
}
Query with Null Handling:
List<Task> tasks = PojoQuery.build(Task.class)
.execute(dataSource);
for (Task task : tasks) {
String assigneeName = task.getAssignee() != null
? task.getAssignee().getName()
: "Unassigned";
System.out.println(task.getTitle() + " - " + assigneeName);
}
// Query only unassigned tasks
List<Task> unassigned = PojoQuery.build(Task.class)
.addWhere("task.assignee_id IS NULL")
.execute(dataSource);
Example 13: Delete Operation
Delete a record by entity or by ID.
Operations:
// Use runInTransaction for all write operations
DB.runInTransaction(dataSource, (Connection c) -> {
// Delete by entity instance
User userToDelete = PojoQuery.build(User.class).findById(c, 123L);
if (userToDelete != null) {
PojoQuery.delete(c, userToDelete);
System.out.println("User deleted");
}
// Delete by ID directly (more efficient - no need to fetch first)
PojoQuery.deleteById(c, User.class, 456L);
// Delete with raw SQL for bulk operations
int deleted = DB.update(c,
SqlExpression.sql("DELETE FROM session WHERE last_activity < ?",
LocalDate.now().minusDays(30)));
System.out.println("Cleaned up " + deleted + " old sessions");
});
Example 14: Dynamic Columns with @Other
Handle tables with dynamic/schemaless column patterns.
Database Schema:
CREATE TABLE product (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
-- Dynamic attribute columns with prefix
attr_color VARCHAR(50),
attr_size VARCHAR(20),
attr_weight DECIMAL(10,2),
attr_material VARCHAR(100)
);
POJO:
@Table("product")
public class Product {
@Id Long id;
String name;
// All columns starting with 'attr_' will be collected here
// Map keys will be 'color', 'size', 'weight', 'material' (without prefix)
@Other(prefix = "attr_")
Map<String, Object> attributes;
// Getters and setters omitted for brevity
}
Query:
Product product = PojoQuery.build(Product.class)
.addWhere("product.id = ?", 1L)
.execute(dataSource)
.stream().findFirst().orElse(null);
if (product != null) {
System.out.println("Product: " + product.getName());
System.out.println("Color: " + product.getAttributes().get("color"));
System.out.println("Size: " + product.getAttributes().get("size"));
// Iterate all dynamic attributes
product.getAttributes().forEach((key, value) ->
System.out.println(" " + key + ": " + value));
}