Database operations
DB plugin provides two layers for interacting with your data: the Query API for building and executing queries, and the Database interface for lower-level operations like index management, batch reads, and utilities. For most use cases, prefer the Query API.
Practical examples
Search with filters
1Query<Article> q = Query.from(Article.class);23// Full-text search4if (query != null && !query.isEmpty()) {5q.where("_any matches ?", query);6}78// Filter by author9if (author != null) {10q.and("author = ?", author);11}1213// Date range14if (startDate != null) {15q.and("publishDate >= ?", startDate);16}17if (endDate != null) {18q.and("publishDate <= ?", endDate);19}2021// Pagination22int offset = (page - 1) * pageSize;23return q.sortDescending("publishDate")24.select(offset, pageSize);
Dynamic query building
1Query<Article> query = Query.from(Article.class);23for (Map.Entry<String, Object> entry : filters.entrySet()) {4String field = entry.getKey();5Object value = entry.getValue();67query.and(field + " = ?", value);8}910List<Article> results = query.selectAll();
Related content
1// Find articles with overlapping tags, same author, or similar title2PaginatedResult<Article> related = Query.from(Article.class)3.where("_id != ?", article.getId()) // Exclude current article4.and("tags = ?", article.getTags()) // Matching tags5.or("author = ?", article.getAuthor()) // Same author6.sortDescending("publishDate")7.select(0, limit);
Faceted search
1Query<Article> baseQuery = Query.from(Article.class)2.where("_any matches ?", searchQuery);34// Get articles5PaginatedResult<Article> articles = baseQuery6.sortRelevant(100, "_any matches ?", searchQuery)7.select(0, 20);89// Get author facets10Map<String, Long> authorCounts = new HashMap<>();11List<Grouping<Article>> authorGroups = baseQuery12.groupBy("author");13for (Grouping<Article> group : authorGroups) {14Author author = (Author) group.getKeys().get(0);15authorCounts.put(author.getName(), group.getCount());16}1718// Get tag facets19Map<String, Long> tagCounts = new HashMap<>();20List<Grouping<Article>> tagGroups = baseQuery21.groupBy("tags");22for (Grouping<Article> group : tagGroups) {23String tag = (String) group.getKeys().get(0);24tagCounts.put(tag, group.getCount());25}
Soft delete pattern
1public class SoftDeleteArticle extends Record {23private String title;45@Indexed(visibility = true)6private Date deletedDate;78public boolean isDeleted() {9return deletedDate != null;10}1112public void softDelete() {13this.deletedDate = new Date(Database.Static.getDefault().now());14this.save();15}1617public void restore() {18this.deletedDate = null;19this.save();20}2122public String getTitle() {23return title;24}2526public void setTitle(String title) {27this.title = title;28}2930public Date getDeletedDate() {31return deletedDate;32}3334public void setDeletedDate(Date deletedDate) {35this.deletedDate = deletedDate;36}37}
When querying, records with populated visibility-indexed fields are automatically hidden:
1// Query hides articles with populated visibility indexes2List<SoftDeleteArticle> activeArticles = Query.from(SoftDeleteArticle.class)3.selectAll();
Cascading delete
1public class CascadingDeleteAuthor extends Record {23private String name;45@Override6protected void beforeDelete() {7Database db = Database.Static.getDefault();89// Delete all articles by this author10db.deleteByQuery(11Query.from(Article.class)12.where("author = ?", this)13);14}1516public String getName() {17return name;18}1920public void setName(String name) {21this.name = name;22}23}
Cache invalidation with notifiers
1public class CacheInvalidatorExample {23private final Cache<UUID, Article> cache;45public CacheInvalidatorExample(Database db, Cache<UUID, Article> cache) {6this.cache = cache;78// Invalidate cache on updates9db.addUpdateNotifier(new UpdateNotifier<Article>() {1011@Override12public void onUpdate(Article article) {13cache.remove(article.getId());14}15});1617// Invalidate cache on deletes18db.addDeleteNotifier(new DeleteNotifier<Article>() {1920@Override21public void onDelete(Article article) {22cache.remove(article.getId());23}24});25}2627public Article get(UUID id) {28return cache.get(29id, key -> {30return Database.Static.getDefault().readFirst(31Query.from(Article.class).where("_id = ?", key)32);33});34}3536// Simple cache interface for example purposes37public interface Cache<K, V> {3839V get(K key, java.util.function.Function<K, V> loader);4041void remove(K key);42}43}
Query API
The Query class provides a fluent API for building and executing database queries. Inspired by Apple's Cocoa Predicates and LINQ, it offers a clean, readable syntax for expressing complex query logic.
Query construction
1// Query a specific type2Query<Article> query = Query.from(Article.class);34// Query all types5Query<Object> allQuery = Query.fromAll();67// Query by ObjectType8ObjectType articleType = ObjectType.getInstance(Article.class);9Query<Object> typeQuery = Query.fromType(articleType);1011// Query by type group (multiple related types)12Query<Object> contentQuery = Query.fromGroup("content");
To query against a specific database:
1Database searchDb = Database.Static.getInstance("search");23Query<Article> query = Query.from(Article.class)4.using(searchDb);
Predicates and filtering
where - initial predicate
Set the initial query predicate:
1// Simple equality2Query.from(Article.class)3.where("title = ?", "Getting Started");45// Comparison6Query.from(Article.class)7.where("publishDate > ?", cutoffDate);89// Multiple parameters10Query.from(Article.class)11.where("title = ? and author = ?", "Getting Started", author);
and - add AND conditions
Chain additional conditions with AND:
1Query.from(Article.class)2.where("publishDate > ?", cutoffDate)3.and("author/name = ?", "John Doe")4.and("tags = ?", "java");
or - add OR conditions
Chain conditions with OR:
1Query.from(Article.class)2.where("author/name = ?", "John Doe")3.or("author/name = ?", "Jane Smith");45// Complex combinations6// (publishDate > cutoff) OR (featured = true AND author = john)7Query.from(Article.class)8.where("publishDate > ?", cutoffDate)9.or("featured = ? and author = ?", true, john);
not - negate conditions
1// Articles NOT by John Doe2Query.from(Article.class)3.where("author/name != ?", "John Doe");45// Or using not()6Query.from(Article.class)7.not("author/name = ?", "John Doe");
having - filter without affecting ranking
Use having to apply predicates that filter results without affecting ranking in full-text search databases (like Solr). Unlike and or or, which can influence the relevance score, having applies the predicate purely as a filter:
1// Search with full-text ranking, but filter by category without affecting relevance scores2// Unlike and() or or(), having() applies the predicate purely as a filter3Query.from(Article.class)4.where("_any matches ?", "database performance") // Affects ranking5.having("category = ?", "technology"); // Filters only, no ranking impact
Predicate operators
Equality operators
1// Equals2Query.from(Article.class)3.where("status = ?", "published");45// Not equals6Query.from(Article.class)7.where("status != ?", "draft");89// Null/missing check10Query.from(Article.class)11.where("deletedDate = missing");1213Query.from(Article.class)14.where("deletedDate != missing");
Comparison operators
1// Greater than2Query.from(Article.class)3.where("publishDate > ?", date);45// Greater than or equal6Query.from(Article.class)7.where("publishDate >= ?", date);89// Less than10Query.from(Article.class)11.where("publishDate < ?", date);1213// Less than or equal14Query.from(Article.class)15.where("publishDate <= ?", date);
String operators
1// Starts with2Query.from(Article.class)3.where("title ^= ?", "Getting");45// Contains6Query.from(Article.class)7.where("content contains ?", "database");
Collection operators
1// Contains any of the values2Query.from(Article.class)3.where("tags = ?", Arrays.asList("java", "database"));45// Contains all values6Query.from(Article.class)7.where("tags = ?", tag1)8.and("tags = ?", tag2); // Both tags must be present
Range queries
1// Between dates2Query.from(Article.class)3.where("publishDate > ? and publishDate < ?", startDate, endDate);
Full-text search
1// Search across all indexed fields2Query.from(Article.class)3.where("_any matches ?", "search terms");45// Search specific field6Query.from(Article.class)7.where("content matches ?", "search terms");
Special field keys
1// _id - Object ID2Query.from(Article.class)3.where("_id = ?", articleId);45// Multiple IDs6Query.from(Article.class)7.where("_id = ?", Arrays.asList(id1, id2, id3));89// _type - Object Type10Query.from(Article.class)11.where("_type = ?", Article.class);1213// _any - Search All Fields14Query.from(Article.class)15.where("_any matches ?", "search query");1617// _label - Display Label18Query.from(Article.class)19.where("_label matches ?", "Article");
Path-based field access
Query nested fields using path notation:
1// Author's name2Query.from(Article.class)3.where("author/name = ?", "John Doe");45// Nested objects6Query.from(Article.class)7.where("author/company/name = ?", "Acme Corp");89// Collection items10Query.from(Article.class)11.where("comments/author/name = ?", "Jane Smith");1213// Map values14Query.from(Article.class)15.where("metadata/category = ?", "technology");
Sorting
sortAscending
1Query.from(Article.class)2.sortAscending("title");34// Multiple fields5Query.from(Article.class)6.sortAscending("author/name")7.sortAscending("publishDate");
sortDescending
1Query.from(Article.class)2.sortDescending("publishDate");
Sort by relevance
Sort by relevance to a predicate (requires search-capable database):
1Query.from(Article.class)2.where("_any matches ?", "java database")3.sortRelevant(100, "_any matches ?", "java database");
Sort by newest/oldest
1// Newest first2Query.from(Article.class)3.sortNewest(100, "publishDate");45// Oldest first6Query.from(Article.class)7.sortOldest(100, "publishDate");
Multiple sort criteria
1Query.from(Article.class)2.sortDescending("featured") // Featured articles first3.sortDescending("publishDate") // Then by date4.sortAscending("title"); // Then by title
Query execution
selectAll
Execute query and return all results:
1List<Article> articles = Query.from(Article.class)2.where("publishDate > ?", cutoffDate)3.selectAll();
select (pagination)
Execute with offset and limit:
1// First page (items 0-9)2PaginatedResult<Article> page1 = Query.from(Article.class)3.sortDescending("publishDate")4.select(0, 10);56// Second page (items 10-19)7PaginatedResult<Article> page2 = Query.from(Article.class)8.sortDescending("publishDate")9.select(10, 10);1011// Calculate offset from page number12int page = 3;13int pageSize = 10;14int offset = (page - 1) * pageSize;15Query<Article> query = Query.from(Article.class);16PaginatedResult<Article> pageN = query.select(offset, pageSize);
first
Get the first result (or null):
1Article article = Query.from(Article.class)2.where("slug = ?", "getting-started")3.first();45if (article == null) {6// Not found7}
findFirst (Optional)
Get the first result as an Optional:
1Optional<Article> optional = Query.from(Article.class)2.where("slug = ?", "getting-started")3.findFirst();45optional.ifPresent(article -> {6System.out.println("Found: " + article.getTitle());7});89// Or with default10Article article = Query.from(Article.class)11.where("slug = ?", "getting-started")12.findFirst()13.orElse(defaultArticle);
count
Count matching objects:
1long total = Query.from(Article.class).count();23long published = Query.from(Article.class)4.where("publishDate != missing")5.count();
iterable
Get a streaming iterable for large result sets:
1Query<Article> query = Query.from(Article.class)2.where("publishDate > ?", cutoffDate);34for (Article article : query.iterable(100)) { // Fetch size 1005// Process each article6// Memory efficient for large datasets7}
deleteAll
Delete all matching objects:
1// Delete old draft articles2Query.from(Article.class)3.where("status = ? and createdDate < ?", "draft", cutoffDate)4.deleteAll();
Grouping and aggregation
groupBy
Group results by one or more fields:
1// Group by author2List<Grouping<Article>> byAuthor = Query.from(Article.class)3.groupBy("author"); // Returns List<Grouping<Article>>45for (Grouping<Article> group : byAuthor) {6Author author = (Author) group.getKeys().get(0);7long count = group.getCount();8System.out.println(author.getName() + ": " + count + " articles");9}
1// Group by multiple fields2List<Grouping<Article>> groups = Query.from(Article.class)3.groupBy("author", "publishYear");45for (Grouping<Article> group : groups) {6Author author = (Author) group.getKeys().get(0);7Integer year = (Integer) group.getKeys().get(1);8long count = group.getCount();9System.out.println(author.getName() + " in " + year + ": " + count);10}
Filtering grouped results
1// Authors with more than 5 articles2List<Grouping<Article>> prolificAuthors = Query.from(Article.class)3.groupBy("author")4.stream()5.filter(group -> group.getCount() > 5)6.collect(Collectors.toList());
Aggregation functions
1// Get count for each group2for (Grouping<Article> group : groups) {3long count = group.getCount();4// Use count...5}67// Access first item in group8for (Grouping<Article> group : groups) {9Article firstArticle = group.createItemsQuery().first();10// Use firstArticle...11}1213// Get all items in group14for (Grouping<Article> group : groups) {15List<Article> articles = group.createItemsQuery().selectAll();16// Process all articles in this group...17}
Query modifiers
timeout
Set query timeout in seconds:
1Query.from(Article.class)2.timeout(30.0) // 30 second timeout3.selectAll();
noCache
Bypass query result cache:
1Query.from(Article.class)2.noCache()3.selectAll();
master
Execute on master/primary database (force read from write source):
1// Ensure we read from master, not replica2Query.from(Article.class)3.master()4.where("_id = ?", articleId)5.first();
Useful when:
- You just wrote data and need to read it immediately
- Avoiding replication lag
- Critical reads that must be up-to-date
resolveToReferenceOnly
Load only reference information, not full objects:
1List<Article> refs = Query.from(Article.class)2.resolveToReferenceOnly()3.selectAll();45// refs contains lightweight reference objects6// Good for getting IDs, types, but not full data
resolveInvisible
Include records marked as invisible:
1Query.from(Article.class)2.resolveInvisible()3.selectAll();
option
Set database-specific options:
1Query.from(Article.class)2.option("hint", "USE INDEX (idx_author)") // SQL hint3.option("readPreference", "secondary") // MongoDB option4.selectAll();
comment
Add an informational comment to the query:
1Query.from(Article.class)2.comment("Dashboard recent articles widget")3.selectAll();
Useful for:
- Debugging slow queries
- Monitoring and logging
- Tracking query origins in database logs
Subqueries
Use queries as predicate values for powerful filtering:
1// Find articles by authors in California2List<Article> articles = Query.from(Article.class)3.where(4"author = ?",5Query.from(Author.class)6.where("state = ?", "CA")7)8.selectAll();910// Find articles NOT by specific authors11List<Article> filtered = Query.from(Article.class)12.where(13"author != ?",14Query.from(Author.class)15.where("blacklisted = ?", true)16)17.selectAll();
1// Complex subquery2Query<Article> articles = Query.from(Article.class)3.where(4"author = ?",5Query.from(Author.class)6.where(7"company = ?",8Query.from(Company.class)9.where("name = ?", "Acme Corp")10)11);
Write operations
save
Saves an object with full validation:
1Article article = new Article();2article.setTitle("New Article");3article.setAuthor(author);4article.setPublishDate(new Date());56try {7db.save(article.getState());8// or simply: article.save();9} catch (ValidationException e) {10// Handle validation errors11for (ObjectField field : e.getStates()12.stream()13.map(State::getErrorFields)14.flatMap(Collection::stream)15.collect(Collectors.toList())) {16System.out.println("Error in " + field.getInternalName() + ": "17+ field.getState().getFieldErrors(field));18}19}
The save process:
- Validates all fields (annotations + custom validation)
- Fires
beforeSave()hooks - Writes to database
- Updates indexes
- Fires
afterSave()hooks
saveUnsafely
Saves without validation (use with caution):
1// Skip validation for performance-critical paths2db.saveUnsafely(article.getState());
When to use:
- Importing trusted data
- System-level operations
- Performance-critical batch operations where you've pre-validated
When NOT to use:
- User input
- External data sources
- When data integrity is critical
delete
Deletes an object:
1db.delete(article.getState());2// or: article.delete();
The delete process:
- Fires
beforeDelete()hooks - Removes from database
- Updates indexes
- Fires
afterDelete()hooks
deleteByQuery
Deletes all objects matching a query:
1// Delete old articles2Date cutoff = DateUtils.addMonths(new Date(), -12);3db.deleteByQuery(4Query.from(Article.class)5.where("publishDate < ?", cutoff)6);
This bypasses lifecycle hooks for performance. If you need hooks to fire, load and delete objects individually:
1List<Article> articles = Query.from(Article.class)2.where("publishDate < ?", cutoff)3.selectAll();45db.beginWrites();6try {7for (Article article : articles) {8article.delete(); // Fires hooks9}10db.commitWrites();11} finally {12db.endWrites();13}
Database interface
The Database interface provides lower-level methods for operations not covered by the Query API. For read operations, prefer Query unless you need direct database access.
Getting a database instance
Default database
1Database db = Database.Static.getDefault();
Named database
1Database searchDb = Database.Static.getInstance("search");
All databases
1List<Database> allDatabases = Database.Static.getAll();
By type
1List<SqlDatabase> sqlDatabases = Database.Static.getByClass(SqlDatabase.class);
Temporary override
Temporarily override the default database for the current thread:
1Database specialDb = Database.Static.getInstance("special");23Database.Static.overrideDefault(specialDb);4try {5// All operations in this block use specialDb6article.save();7Query.from(Article.class).selectAll();8} finally {9Database.Static.restoreDefault();10}
Read methods
For most read operations, use the Query API instead. The Database read methods are useful when you need to work with raw Query objects or implement custom database logic.
readAll
Reads all objects matching a query:
1Query<Article> query = Query.from(Article.class)2.where("publishDate > ?", cutoffDate);34List<Article> articles = db.readAll(query);
readFirst
Reads the first object matching a query:
1Article article = db.readFirst(2Query.from(Article.class)3.where("title = ?", "Getting Started")4);56if (article == null) {7// No matching article found8}
readCount
Returns the count of objects matching a query:
1long totalArticles = db.readCount(2Query.from(Article.class)3);45long publishedCount = db.readCount(6Query.from(Article.class)7.where("publishDate != missing")8);
readPartial
Reads a subset of results with offset and limit (pagination):
1// Page 1: items 0-92PaginatedResult<Article> page1 = db.readPartial(3Query.from(Article.class).sortDescending("publishDate"),40, // offset510 // limit6);78// Page 2: items 10-199PaginatedResult<Article> page2 = db.readPartial(10Query.from(Article.class).sortDescending("publishDate"),1110, // offset1210 // limit13);
readIterable
Returns an iterable for streaming large result sets without loading everything into memory:
1Query<Article> query = Query.from(Article.class);2Iterable<Article> articles = db.readIterable(query, 100); // Fetch size34for (Article article : articles) {5// Process each article6System.out.println(article.getTitle());78// Memory is managed efficiently - old items can be garbage collected9}
This is useful for:
- Processing large datasets
- Export operations
- Batch processing
- Reducing memory footprint
readAllGrouped
Reads grouped results with aggregations:
1// Group articles by author2List<Grouping<Article>> groupings = db.readAllGrouped(3Query.from(Article.class),4"author"5);67for (Grouping<Article> grouping : groupings) {8Author author = (Author) grouping.getKeys().get(0);9long count = grouping.getCount();10System.out.println(author.getName() + ": " + count + " articles");11}
1// Group by multiple fields2List<Grouping<Article>> groupings = db.readAllGrouped(3Query.from(Article.class),4"author",5"publishDate.year" // Group by year6);78for (Grouping<Article> grouping : groupings) {9Author author = (Author) grouping.getKeys().get(0);10Integer year = (Integer) grouping.getKeys().get(1);11long count = grouping.getCount();12System.out.println(author.getName() + " in " + year + ": " + count);13}
readPartialGrouped
Paginated version of readAllGrouped:
1PaginatedResult<Grouping<Article>> page = db.readPartialGrouped(2Query.from(Article.class),30, // offset410, // limit5"author"6);
readLastUpdate
Returns the most recent update time for objects matching a query:
1Date lastUpdate = db.readLastUpdate(2Query.from(Article.class)3);45if (lastUpdate != null) {6System.out.println("Last article update: " + lastUpdate);7}
Useful for:
- Cache invalidation
- Change detection
- Synchronization checks
Index operations
index
Updates indexes for a specific object:
1// Re-index after making changes that affect queries2article.setTitle("Updated Title");3db.index(article.getState());
Typically you don't need to call this directly as save() handles indexing. Use it when:
- Manually updating index data
- Recovering from index corruption
- Implementing custom indexing strategies
indexAll
Re-indexes all objects for a specific index:
1ObjectType articleType = ObjectType.getInstance(Article.class);2ObjectIndex titleIndex = articleType.getIndex("title");34// Re-index all articles for the title index5db.indexAll(titleIndex);
Use cases:
- Adding a new index to existing data
- Recovering from index corruption
- Changing index definitions
recalculate
Recalculates specific indexes for an object:
1ObjectType type = ObjectType.getInstance(Article.class);2ObjectIndex authorIndex = type.getIndex("author");3ObjectIndex dateIndex = type.getIndex("publishDate");45// Recalculate only these indexes6db.recalculate(article.getState(), authorIndex, dateIndex);
More efficient than full re-indexing when you know exactly which indexes need updates.
Batch operations
Batch reads
Read multiple objects by ID efficiently:
1List<UUID> articleIds = Arrays.asList(id1, id2, id3, id4, id5);23List<Article> articles = Query.from(Article.class)4.where("_id = ?", articleIds)5.selectAll();
Batch writes
Write multiple objects in a single transaction:
1db.beginWrites();2try {3for (Article article : articles) {4db.save(article.getState());5}6db.commitWrites(); // All saved atomically7} finally {8db.endWrites();9}
Benefits:
- Atomic (all succeed or all fail)
- Better performance
- Validation happens before any writes
- Single database roundtrip
Database utilities
ping
Check if the database is available:
1// Ping replica/read connection2try {3db.ping(false);45// Ping primary/write connection6db.ping(true);7} catch (Exception e) {8System.out.println("Ping failed: " + e.getMessage());9throw new RuntimeException(e);10}
Use in:
- Health checks
- Monitoring
- Graceful degradation logic
now
Get the database server's current time:
1long dbTime = db.now();23// Example: Set timestamp from database4article.setPublishDate(new Date(db.now()));5article.save();
Why use this instead of new Date()?
- Consistent timestamps across distributed systems
- Avoids clock skew between app servers
- Some databases have special time functions
getName / setName
1String name = db.getName();2db.setName("newName");
Database names are used to:
- Identify databases in multi-database configurations
- Route queries to specific databases
- Configure database-specific settings
Environment (deprecated)
getEnvironment() and setEnvironment() are deprecated. Use DatabaseEnvironment.getCurrent() and DatabaseEnvironment.override() instead.
Notifiers (update listeners)
Register callbacks for object changes:
Update notifiers
1// Listen for article updates2db.addUpdateNotifier(new UpdateNotifier<Article>() {34@Override5public void onUpdate(Article article) {6cacheService.invalidate(article.getId());7}8});
Delete notifiers
1// Listen for article deletions2db.addDeleteNotifier(new DeleteNotifier<Article>() {34@Override5public void onDelete(Article article) {6// Handle deletion7cacheService.remove(article.getId());8searchIndex.remove(article.getId());9}10});
Removing notifiers
1UpdateNotifier<Article> notifier = new UpdateNotifier<Article>() {23@Override4public void onUpdate(Article article) {5// Handle updates6}7};8db.addUpdateNotifier(notifier);910// Later, remove it11db.removeUpdateNotifier(notifier);
Check notifier support
1if (db.canCallNotifiers()) {2// This database supports notifiers3db.addUpdateNotifier(notifier);4} else {5// Use polling or other mechanisms6}
Not all database implementations support notifiers. AbstractSqlDatabase typically does, but some specialized databases may not.
Performance tips
Index your query fields
1// Ensure queried fields are indexed2// @Indexed3// private String title;4//5// @Indexed6// private Date publishDate;7//8// @Indexed9// private Author author;
Use batch operations
1// Good - single transaction2db.beginWrites();3for (Article article : articles) {4db.save(article.getState());5}6db.commitWrites();78// Bad - multiple transactions9for (Article article : articles) {10db.save(article.getState());11}
Use pagination
1// Good - loads 10 items2query.select(0, 10);34// Bad - loads everything5query.selectAll();
Use count() instead of loading all
1// Good2long count = query.count();34// Bad5long badCount = query.selectAll().size();
Use readIterable for large datasets
1// Good - memory efficient2for (Article article : db.readIterable(query, 100)) {3process(article);4}56// Bad - loads everything into memory7List<Article> all = db.readAll(query);8for (Article article : all) {9process(article);10}
Use specific predicates
1// Good - uses index efficiently2Query.from(Article.class)3.where("publishDate > ?", cutoffDate);45// Less efficient - may scan more rows6Query.from(Article.class)7.where("publishDate != missing");
Bulk import
1Database db = Database.Static.getDefault();2db.beginWrites();34try {5for (ArticleData articleData : data) {6Article article = new Article();7article.setTitle(articleData.getTitle());8article.setContent(articleData.getContent());9// ... set other fields1011db.save(article.getState());12}1314db.commitWrites();15System.out.println("Imported " + data.size() + " articles");1617} catch (Exception e) {18// Transaction will be rolled back19System.err.println("Import failed: " + e.getMessage());20throw e;21} finally {22db.endWrites();23}
Streaming export
1Database db = Database.Static.getDefault();2CSVWriter csv = new CSVWriter(writer);34// Write header5csv.writeNext(new String[] { "ID", "Title", "Author", "Date" });67// Stream results8Query<Article> query = Query.from(Article.class)9.sortDescending("publishDate");1011for (Article article : db.readIterable(query, 100)) {12csv.writeNext(new String[] {13article.getId().toString(),14article.getTitle(),15article.getAuthor().getName(),16article.getPublishDate().toString()17});18}1920csv.close();
Common pitfalls
Forgetting to index
1// This will fail if 'title' is not indexed2// Query.from(Article.class)3// .where("title = ?", "Getting Started")4// .selectAll();5// ERROR: Field 'title' is not indexed
Loading too much data
1// Avoid loading thousands of objects at once2// List<Article> all = Query.from(Article.class).selectAll();3// Use pagination or iterable() instead
Inefficient filtering
1// Bad - loads everything then filters in memory2List<Article> all = Query.from(Article.class).selectAll();3List<Article> filtered = all.stream()4.filter(a -> a.getPublishDate().after(cutoffDate))5.collect(Collectors.toList());67// Good - filters at database level8List<Article> goodFiltered = Query.from(Article.class)9.where("publishDate > ?", cutoffDate)10.selectAll();
Not handling null results
1// Bad - can throw NullPointerException2Article article = Query.from(Article.class)3.where("_id = ?", id)4.first();5// String title = article.getTitle(); // NPE if article is null67// Good - check for null8Article safeArticle = Query.from(Article.class)9.where("_id = ?", id)10.first();11if (safeArticle != null) {12String title = safeArticle.getTitle();13}1415// Or use Optional16String optionalTitle = Query.from(Article.class)17.where("_id = ?", id)18.findFirst()19.map(Article::getTitle)20.orElse("Untitled");
Error handling
ValidationException
1try {2article.save();3} catch (ValidationException e) {4for (State state : e.getStates()) {5for (ObjectField field : state.getErrorFields()) {6String fieldName = field.getInternalName();7for (Throwable error : state.getFieldErrors(field)) {8System.err.println("Validation error in " + fieldName + ": " + error.getMessage());9}10}11}12}