Database Operations
The Database interface provides a comprehensive set of methods for interacting with your data store. This guide covers all read and write operations, index management, and database utilities.
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 Operations
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
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);
Warning: 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}
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)
Note: 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}
Note: Not all database implementations support notifiers. AbstractSqlDatabase typically does, but some specialized databases may not.
Practical Examples
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}
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();
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();
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}
Performance Considerations
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 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 readCount Instead of Loading All
1// Good2long count = db.readCount(query);34// Bad5long badCount = db.readAll(query).size();
Use Specific Queries
1// Good - only loads needed objects2PaginatedResult<Article> recent = db.readPartial(3Query.from(Article.class)4.where("publishDate > ?", cutoff)5.sortDescending("publishDate"),60,7108);910// Bad - loads everything then filters11List<Article> all = db.readAll(Query.from(Article.class));12List<Article> recentBad = all.stream()13.filter(a -> a.getPublishDate().after(cutoff))14.sorted((a1, a2) -> a2.getPublishDate().compareTo(a1.getPublishDate()))15.limit(10)16.collect(Collectors.toList());
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}