Annotation Reference

PojoQuery uses annotations to map POJOs to database structures and customize query generation.

Quick Reference

Annotation Target Purpose

@Table

Class

Maps class to database table

@Id

Field

Marks primary key field(s)

@FieldName

Field

Custom column name mapping

@Transient

Field

Excludes field from database mapping

@Link

Field

Defines entity relationship (auto JOIN)

@JoinCondition

Field

Custom condition for @Link joins

@Select

Field

Custom SQL expression for field

@Join

Class

Explicit SQL JOIN clause

@Joins

Class

Container for multiple @Join

@GroupBy

Class

Adds GROUP BY clause

@OrderBy

Class

Default ORDER BY clause

@Embedded

Field

Flattens nested object into parent columns

@Other

Field

Maps prefixed columns to Map

@SubClasses

Class

Defines table-per-subclass inheritance

@NoUpdate

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

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

@Table

Table name (name) and schema

@Id

@Id

Primary key field

@FieldName

@Column(name=…​)

Custom column name

@Column

@Column

length, precision, scale, nullable, unique

@Transient

@Transient

Exclude field from persistence

@Embedded

@Embedded

Embed value object (note: JPA @Embedded has no prefix support)

@Lob

@Lob

Large object (CLOB/BLOB)

@Link(linkfield=…​)

@JoinColumn(name=…​)

Foreign key column name

@SubClasses

(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; }
}