Advanced Topics

This page covers advanced topics and techniques for using PojoQuery effectively.

Custom Field Types

Custom Type Converters

Create custom type converters for special field types:

public class JsonTypeConverter implements FieldMapping {
    private final Field field;

    public JsonTypeConverter(Field field) {
        this.field = field;
    }

    @Override
    public void apply(Object target, Object value) {
        if (value instanceof String) {
            try {
                Object jsonValue = new ObjectMapper()
                    .readValue((String)value, field.getType());
                field.setAccessible(true);
                field.set(target, jsonValue);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

// Usage
class CustomDbContext extends DbContext.DefaultDbContext {
    @Override
    public FieldMapping getFieldMapping(Field f) {
        if (f.isAnnotationPresent(JsonField.class)) {
            return new JsonTypeConverter(f);
        }
        return super.getFieldMapping(f);
    }
}

Enum Handling

Handle enum types with custom mappings:

public enum UserStatus {
    ACTIVE("A"),
    INACTIVE("I"),
    PENDING("P");

    private final String code;

    UserStatus(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    public static UserStatus fromCode(String code) {
        for (UserStatus status : values()) {
            if (status.code.equals(code)) {
                return status;
            }
        }
        throw new IllegalArgumentException(code);
    }
}

@Table("user")
public class User {
    @Id
    Long id;

    @FieldName("status_code")
    UserStatus status;
}

Complex Joins and Relationships

Many-to-Many Relationships

Handle many-to-many relationships:

@Table("article")
public class Article {
    @Id
    Long id;
    String title;

    @Link
    List<Tag> tags;
}

@Table("tag")
public class Tag {
    @Id
    Long id;
    String name;
}

@Table("article_tag")
public class ArticleTag {
    @Id
    Long articleId;

    @Id
    Long tagId;
}

Custom Join Conditions

Define custom join conditions using the @JoinCondition annotation:

@Table("article")
public class Article {
    @Id
    Long id;
    String title;

    @JoinCondition("{this}.id = {comments}.article_id")
    List<Comment> comments;
}

@Table("comment")
public class Comment {
    @Id
    Long id;
    String content;
    Long articleId;
}

Query Optimization

Streaming Results

For large result sets, use streaming to process rows one at a time:

PojoQuery.build(Article.class)
    .executeStreaming(dataSource)
    .forEach(article -> {
        // Process each article
    });

Custom Field Selection

Optimize queries by selecting only needed fields:

PojoQuery.build(Article.class)
    .addField(SqlExpression.sql("article.id"), "id")
    .addField(SqlExpression.sql("article.title"), "title")
    .execute(dataSource);

Error Handling

Custom Exception Handling

Implement custom exception handling for database operations:

try {
    List<User> users = PojoQuery.build(User.class)
        .execute(dataSource);
} catch (DatabaseException e) {
    // Handle database-specific errors
    logger.error("Database error: " + e.getMessage());
} catch (MappingException e) {
    // Handle mapping errors
    logger.error("Mapping error: " + e.getMessage());
}

Validation

Add validation before database operations:

public class UserService {
    public void createUser(User user) {
        validateUser(user);
        PojoQuery.insert(dataSource, user);
    }

    private void validateUser(User user) {
        if (user.getName() == null || user.getName().trim().isEmpty()) {
            throw new ValidationException("User name is required");
        }
        if (user.getEmail() == null || !user.getEmail().matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
            throw new ValidationException("Invalid email address");
        }
    }
}

Inheritance

PojoQuery supports inheritance hierarchies where each class maps to its own table. This is useful for modeling specialized types that share common properties.

Basic Inheritance

Here’s an example using a room management system:

@Table("room")
@SubClasses({BedRoom.class, Kitchen.class})
public class Room {
    @Id
    Long id;
    Double area;
}

@Table("bedroom")
public class BedRoom extends Room {
    Integer numberOfBeds;
}

@Table("kitchen")
public class Kitchen extends Room {
    Boolean hasDishWasher;
}

When querying the base class, PojoQuery will automatically join with the subclass tables:

// Query all rooms
List<Room> rooms = PojoQuery.build(Room.class)
    .execute(dataSource);

// Process results
for (Room room : rooms) {
    if (room instanceof BedRoom) {
        BedRoom bedroom = (BedRoom) room;
        System.out.println("Bedroom with " + bedroom.numberOfBeds + " beds");
    } else if (room instanceof Kitchen) {
        Kitchen kitchen = (Kitchen) room;
        System.out.println("Kitchen with dishwasher: " + kitchen.hasDishWasher);
    }
}

Querying Specific Types

You can also query specific types directly:

// Query only bedrooms
List<BedRoom> bedrooms = PojoQuery.build(BedRoom.class)
    .execute(dataSource);

// Query only kitchens
List<Kitchen> kitchens = PojoQuery.build(Kitchen.class)
    .execute(dataSource);

When querying a specific type, PojoQuery will automatically include the base class fields in the query.

Nested Inheritance

PojoQuery also supports deeper inheritance hierarchies:

@Table("room")
@SubClasses({BedRoom.class, Kitchen.class})
public class Room {
    @Id
    Long id;
    Double area;
}

@Table("bedroom")
public class BedRoom extends Room {
    Integer numberOfBeds;
}

@Table("luxury_bedroom")
public class LuxuryBedRoom extends BedRoom {
    Double tvScreenSize;
}

When querying the base class, PojoQuery will automatically include all subclass tables in the query.

Best Practices

  • Use @SubClasses annotation to specify all possible subclasses

  • Keep inheritance hierarchies shallow for better performance

  • Use appropriate table names that reflect the inheritance structure

  • Consider using composition instead of inheritance for complex relationships

  • Use type-safe casting when processing results