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
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