Annotation Reference
PojoQuery uses annotations to map POJOs to database structures and customize query generation.
Quick Reference
| Annotation | Target | Purpose |
|---|---|---|
|
Class |
Maps class to database table |
|
Field |
Marks primary key field(s) |
|
Field |
Custom column name mapping |
|
Field |
Excludes field from database mapping |
|
Field |
Defines entity relationship (auto JOIN) |
|
Field |
Custom condition for |
|
Field |
Custom SQL expression for field |
|
Class |
Explicit SQL JOIN clause |
|
Class |
Container for multiple |
|
Class |
Adds GROUP BY clause |
|
Class |
Default ORDER BY clause |
|
Field |
Flattens nested object into parent columns |
|
Field |
Maps prefixed columns to Map |
|
Class |
Defines table-per-subclass inheritance |
|
Field |
Excludes field from UPDATE statements |
Core Mapping Annotations
@Table
Description: Maps a POJO class to a specific database table. Essential for root entities.
Target: Class
Parameters:
value (String, required): The name of the database table.
schema (String, optional): The database schema name.
Example:
@Table("users") // Maps to 'users' table in the default schema
public class User { ... }
@Table(value="orders", schema="sales") // Maps to 'sales.orders' table
public class Order { ... }
@Id
Description: Marks one or more fields as the primary key component(s) of the entity. Used for findById, updates, and relationship joining by default.
Target: Field
Example:
@Table("products")
public class Product {
@Id
Long productId; // Single primary key
String name;
}
@Table("order_items")
public class OrderItem {
@Id Long orderId; // Composite key part 1
@Id Long itemId; // Composite key part 2
int quantity;
}
@FieldName
Description: Specifies the exact database column name for a POJO field when it differs from the field name (or the convention-based name).
Target: Field
Parameters:
* value (String, required): The name of the database column.
*Example:
@Table("customers")
public class Customer {
@Id Long id;
@FieldName("first_name") String firstName;
@FieldName("email_addr") String emailAddress;
}
@Transient
Description: Marks a field that should be ignored by PojoQuery during database mapping (SELECT, INSERT, UPDATE). Useful for calculated fields or temporary state.
Target: Field
Example:
@Table("products")
public class Product {
@Id Long id;
BigDecimal price;
BigDecimal taxRate;
@Transient
BigDecimal priceIncludingTax; // Calculated in Java, not stored in DB
}
Relationship Annotations
@Link
Description: Explicitly defines a relationship to another entity. For simple relationships following naming conventions (e.g., author field with author_id column), @Link is optional — PojoQuery will infer the join automatically. Use @Link when you need to:
-
Specify a many-to-many relationship with a link table
-
Override the default foreign key column name
-
Fetch simple values from a link table
-
Change the join type (e.g., INNER instead of LEFT)
Target: Field
Parameters:
linkfield (String, optional): The foreign key column name.
* For one-to-one/many-to-one: Column in the current entity’s table referencing the target entity’s ID (Default: {fieldName}_id, e.g., author_id).
* For many-to-many: Column in the linktable referencing the current entity’s ID (Default: {currentTableName}_id, e.g., article_id).
foreignlinkfield (String, optional): Used only for many-to-many. The foreign key column name in the linktable referencing the target entity’s ID (Default: {fieldName}_id, e.g., tag_id).
linktable (String, optional): The name of the intermediate join table for many-to-many relationships. If present, indicates a many-to-many link. (Default: Link.NONE).
fetchColumn (String, optional): For collections of simple types (e.g., List<String>, Set<Role>), specifies the column in the linktable holding the collection element values. (Default: Link.NONE).
* type (JoinType, optional): The type of join to generate (LEFT, INNER, RIGHT). (Default: JoinType.LEFT).
*Examples:
@Table("article")
public class Article {
@Id Long id;
// Convention-based joins - @Link is OPTIONAL for these:
User author; // Joins on 'author_id' column automatically
List<Comment> comments; // Joins on 'article_id' in comment table automatically
// Many-to-Many: @Link IS REQUIRED with linktable parameter
@Link(linktable="article_tag", linkfield="article_id", foreignlinkfield="tag_id")
List<Tag> tags;
// Collection of Enums: @Link IS REQUIRED with fetchColumn parameter
@Link(linktable="article_flags", linkfield="article_id", fetchColumn="flag_value")
Set<ArticleFlag> flags;
// Non-conventional FK name: @Link IS REQUIRED with linkfield parameter
@Link(linkfield="writer_id")
User writer;
}
@JoinCondition
Description: Specifies a custom join condition for a @Link relationship. This is particularly useful when you have multiple relationships to the same table that need to be distinguished by an additional condition (e.g., role-based relationships), or when the default join convention doesn’t apply.
Target: Field (used alongside @Link)
Parameters:
value (String, required): The SQL join condition. Supports placeholders:
* {this} - refers to the current entity’s table alias
* {linktable} - refers to the link table alias (for many-to-many)
* {fieldName} - refers to the target entity’s table alias
Examples:
@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 Customization Annotations
@Select
Description: Specifies a custom SQL expression or function call to populate a field. The result of the expression will be mapped to this field.
Target: Field
Parameters:
* value (String, required): The SQL expression. Use database-specific syntax.
*Example:
@Table("users")
public class UserStats {
@Id Long id;
@FieldName("first_name") String firstName;
@FieldName("last_name") String lastName;
@Select("CONCAT(first_name, ' ', last_name)") // Database CONCAT function
String fullName;
@Select("YEAR(registration_date)") // Database YEAR function
Integer registrationYear;
}
@Join
Description: Adds a custom, explicit SQL JOIN clause to the query for this entity. Can be used instead of or alongside @Link.
Target: Class
Parameters:
value (String, required if other parameters are not set): The full SQL JOIN clause (e.g., "LEFT JOIN user u ON a.user_id = u.id").
type (JoinType, optional): The join type (LEFT, INNER, RIGHT).
schemaName (String, optional): Schema of the table to join.
tableName (String, optional): Name of the table to join.
alias (String, optional): Alias for the joined table.
joinCondition (String, optional): The ON condition for the join.
Example:
@Table("article")
// Add a specific join manually
@Join("LEFT JOIN user_profile p ON article.author_id = p.user_id AND p.active = true")
public class ArticleWithProfile {
@Id Long id;
// ... other fields
// You might need @Select or @FieldName to map fields from 'p' if not using @Link
}
@Joins
Description: Container annotation to allow multiple @Join annotations on a single class.
Target: Class
Parameters:
* value (Join[], required): An array of @Join annotations.
*Example:
@Table("order")
@Joins({
@Join("LEFT JOIN customer c ON order.customer_id = c.id"),
@Join("LEFT JOIN address shipping ON order.shipping_address_id = shipping.id")
})
public class OrderDetails { ... }
@GroupBy
Description: Adds a GROUP BY clause to the generated SQL query. Typically used with @Select annotations containing aggregate functions.
Target: Class
Parameters:
* value (String[], required): An array of SQL expressions to group by (e.g., {"article.id", "author.id"}).
*Example:
@Table("comment")
@GroupBy("comment.article_id") // Group comments by article
public class ArticleCommentSummary {
@FieldName("article_id") Long articleId;
@Select("COUNT(*)")
Integer commentCount;
@Select("MAX(submitdate)")
Date lastCommentDate;
}
@OrderBy
Description: Adds an ORDER BY clause to the generated SQL query for default sorting.
Target: Class
Parameters:
* value (String, required): The SQL ORDER BY expression (e.g., "publishDate DESC, title ASC").
*Example:
@Table("products")
@OrderBy("category ASC, price DESC")
public class Product { ... }
Advanced Mapping Annotations
@Embedded
Description: Maps fields from a nested Java object (which is not an entity itself) into columns of the parent entity’s table. Flattens the structure.
Target: Field
Parameters:
* prefix (String, optional): A prefix to be added to the column names generated from the embedded object’s fields. (Default: {fieldName}_, e.g., address_).
*Example:
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; }
}
@Other
Description: Maps multiple database columns starting with a specific prefix into a single Map<String, Object> field. Useful for dynamic or EAV-like structures.
Target: Field
Parameters:
* prefix (String, required): The prefix identifying columns that belong to this map. The map keys will be the column names *without the prefix.
Example:
@Table("settings")
public class UserSettings {
@Id Long userId;
// Maps columns like 'pref_theme', 'pref_language' into the map
// Map keys will be 'theme', 'language'
@Other(prefix="pref_")
Map<String, Object> preferences;
}
@SubClasses
Description: Defines a table-per-subclass inheritance mapping for a base class. Each subclass has its own table that shares the same primary key with the base table.
Target: Class (on the base class)
Parameters:
* value (Class<?>[], required): An array of subclass types that extend this base class.
*Example:
// Base class mapped to the 'person' table
@Table("person")
@SubClasses({Employee.class, Customer.class})
public static class Person {
@Id Long id;
String name;
public Person() {}
public Person(String name) {
this.name = name;
}
public Long getId() { return id; }
public String getName() { return name; }
}
// Employee subclass - has its own 'employee' table
@Table("employee")
public static class Employee extends Person {
String department;
BigDecimal salary;
public Employee() {}
public Employee(String name, String department, BigDecimal salary) {
super(name);
this.department = department;
this.salary = salary;
}
public String getDepartment() { return department; }
public BigDecimal getSalary() { return salary; }
}
// Customer subclass - has its own 'customer' table
@Table("customer")
public static class Customer extends Person {
Integer loyaltyPoints;
public Customer() {}
public Customer(String name, Integer loyaltyPoints) {
super(name);
this.loyaltyPoints = loyaltyPoints;
}
public Integer getLoyaltyPoints() { return loyaltyPoints; }
}
@NoUpdate
Description: Marks a field that should not be included in UPDATE statements generated by PojoQuery.update(). Useful for created timestamps or immutable fields.
Target: Field
Example:
@Table("records")
public class Record {
@Id Long id;
String data;
@NoUpdate // This field will not be updated
LocalDateTime createdAt;
}
JPA Annotation Compatibility
PojoQuery supports standard JPA annotations from both jakarta.persistence and javax.persistence namespaces as alternatives to its native annotations. This allows you to use existing JPA-annotated entities with PojoQuery without modification.
Supported JPA Annotations
| PojoQuery Annotation | JPA Equivalent | Notes |
|---|---|---|
|
|
Table name ( |
|
|
Primary key field |
|
|
Custom column name |
|
|
length, precision, scale, nullable, unique |
|
|
Exclude field from persistence |
|
|
Embed value object (note: JPA |
|
|
Large object (CLOB/BLOB) |
|
|
Foreign key column name |
|
(none) |
No JPA equivalent—see note below |
| PojoQuery annotations always take precedence when both PojoQuery and JPA annotations are present on the same element. |
No JPA Equivalent: @SubClasses
JPA uses @Inheritance(strategy=…) combined with classpath scanning to discover subclasses automatically. PojoQuery takes a different approach: subclasses must be explicitly declared using @SubClasses. This design choice keeps PojoQuery simple and avoids the complexity of classpath scanning, reflection-based discovery, and the associated startup overhead.
Example with JPA Annotations
@Table(name = "customers")
public static class Customer {
@Id
Long id;
@Column(name = "full_name", length = 100, nullable = false)
String name;
@Column(unique = true)
String email;
String biography;
@Transient
String temporaryData;
@JoinColumn(name = "primary_address_id")
Address primaryAddress;
public Customer() {}
public Customer(String name, String email, String biography) {
this.name = name;
this.email = email;
this.biography = biography;
}
// Getters
public Long getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
public String getBiography() { return biography; }
public Address getPrimaryAddress() { return primaryAddress; }
}
This entity works identically to one using PojoQuery’s native annotations:
@org.pojoquery.annotations.Table("customers")
public static class CustomerNative {
@org.pojoquery.annotations.Id
Long id;
@org.pojoquery.annotations.FieldName("full_name")
@org.pojoquery.annotations.Column(length = 100, nullable = false)
String name;
@org.pojoquery.annotations.Column(unique = true)
String email;
String biography;
@org.pojoquery.annotations.Transient
String temporaryData;
@org.pojoquery.annotations.Link(linkfield = "primary_address_id")
Address primaryAddress;
// Getters
public Long getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
public String getBiography() { return biography; }
public Address getPrimaryAddress() { return primaryAddress; }
}