Getting Started
This guide will help you get up and running with DB plugin quickly. You'll learn how to create your first domain objects, and perform basic operations.
Creating Your First Record Class
A Record is your domain object that maps to database storage. Here's a simple example:
1import com.psddev.dari.db.Record;23public class Article extends Record {45@Indexed6private String title;78@Indexed9@Required10private Author author;1112@Indexed13private Date publishDate;1415private String content;1617private List<String> tags;1819// Getters and setters20public String getTitle() {21return title;22}2324public void setTitle(String title) {25this.title = title;26}2728public Author getAuthor() {29return author;30}3132public void setAuthor(Author author) {33this.author = author;34}3536public Date getPublishDate() {37return publishDate;38}3940public void setPublishDate(Date publishDate) {41this.publishDate = publishDate;42}4344public String getContent() {45return content;46}4748public void setContent(String content) {49this.content = content;50}5152public List<String> getTags() {53if (tags == null) {54tags = new ArrayList<>();55}56return tags;57}5859public void setTags(List<String> tags) {60this.tags = tags;61}6263// Optional: Override getLabel for display purposes64@Override65public String getLabel() {66return title != null ? title : "Untitled Article";67}68}
1import com.psddev.dari.db.Record;23public class Author extends Record {45@Indexed6@Required7private String name;89@Indexed10private String email;1112private String bio;1314// Getters and setters15public String getName() {16return name;17}1819public void setName(String name) {20this.name = name;21}2223public String getEmail() {24return email;25}2627public void setEmail(String email) {28this.email = email;29}3031public String getBio() {32return bio;33}3435public void setBio(String bio) {36this.bio = bio;37}3839@Override40public String getLabel() {41return name;42}43}
Important Annotations
@Indexed- Makes a field queryable. Required for any field you want to filter/sort by.@Required- Field must have a value before saving.@Embedded- Always stores the object inline (no separate table/document).
Basic CRUD Operations
Creating and Saving Objects
1// Create a new author2Author author = new Author();3author.setName("John Doe");4author.setEmail("john@example.com");5author.setBio("Experienced writer and developer");6author.save();78// Create an article9Article article = new Article();10article.setTitle("Getting Started with DB plugin");11article.setAuthor(author); // Reference to author12article.setPublishDate(new Date());13article.setContent("This is the content of the article...");14article.setTags(Arrays.asList("database", "tutorial", "java"));15article.save();
Reading Objects by ID
Every Record has a unique ID (UUID):
1// Get the ID2UUID articleId = article.getId();34// Later, load by ID5Article loaded = Query.from(Article.class)6.where("_id = ?", articleId)7.first();
Querying Objects
1// Find all articles2List<Article> allArticles = Query.from(Article.class)3.selectAll();45// Find articles by author6List<Article> johnArticles = Query.from(Article.class)7.where("author = ?", author)8.selectAll();910// Find articles by author name (path-based query)11List<Article> johnArticles = Query.from(Article.class)12.where("author/name = ?", "John Doe")13.selectAll();1415// Find recent articles16Date cutoffDate = DateUtils.addDays(new Date(), -7);17List<Article> recentArticles = Query.from(Article.class)18.where("publishDate > ?", cutoffDate)19.sortDescending("publishDate")20.selectAll();2122// Paginated results23List<Article> firstPage = Query.from(Article.class)24.sortDescending("publishDate")25.select(0, 10); // Skip 0, limit 102627// Count articles28long totalArticles = Query.from(Article.class)29.count();
Updating Objects
1// Load the object2Article article = Query.from(Article.class)3.where("_id = ?", articleId)4.first();56// Modify fields7article.setTitle("Updated Title");8article.getTags().add("updated");910// Save changes11article.save();
Deleting Objects
1// Delete a single object2article.delete();34// Delete by query5Query.from(Article.class)6.where("publishDate < ?", cutoffDate)7.deleteAll();
Working with References
DB plugin automatically handles relationships between objects:
1// Save author first2Author author = new Author();3author.setName("Jane Smith");4author.save();56// Create article with reference7Article article = new Article();8article.setTitle("My Article");9article.setAuthor(author); // This stores a reference, not a copy10article.save();1112// Later, when you load the article13Article loaded = Query.from(Article.class)14.where("_id = ?", articleId)15.first();1617// The author is lazy-loaded18Author loadedAuthor = loaded.getAuthor(); // Database query happens here19System.out.println(loadedAuthor.getName()); // "Jane Smith"
Reference Resolution Options
1// Reference-only query (don't load full objects)2List<Article> articleRefs = Query.from(Article.class)3.resolveToReferenceOnly()4.selectAll();5// articleRefs contains lightweight reference objects
Using Transactions
For multiple operations that should succeed or fail together:
1Database db = Database.Static.getDefault();23try {4db.beginWrites();56// Create multiple objects7Author author = new Author();8author.setName("John Doe");9author.save();1011Article article1 = new Article();12article1.setTitle("First Article");13article1.setAuthor(author);14article1.save();1516Article article2 = new Article();17article2.setTitle("Second Article");18article2.setAuthor(author);19article2.save();2021// Commit all changes22db.commitWrites();2324} finally {25// Always clean up (rolls back on exception)26db.endWrites();27}
Validation
DB plugin validates objects before saving:
1public class Article extends Record {23@Required4@Maximum(200)5private String title;67@Required8private Author author;910// Custom validation11@Override12protected void onValidate() {13if (title != null && title.contains("spam")) {14getState().addError(15getState().getField("title"),16new IllegalArgumentException("Title contains spam")17);18}19}20}2122// Attempting to save an invalid object throws ValidationException23Article article = new Article();24// article.setTitle("Required field missing");25// article.setAuthor(null); // Also required2627try {28article.save();29} catch (ValidationException e) {30// Handle validation errors31for (ObjectField field : e.getErrors().keySet()) {32System.out.println("Error in " + field.getInternalName() + ": "33+ e.getErrors().get(field));34}35}
Lifecycle Hooks
Override lifecycle methods to add custom behavior:
1public class Article extends Record {23@Override4protected void beforeSave() {5// Called before validation and save6if (publishDate == null) {7publishDate = new Date();8}9}1011@Override12protected void afterSave() {13// Called after successful save14System.out.println("Article saved: " + getLabel());15}1617@Override18protected void beforeDelete() {19// Called before delete20System.out.println("Deleting article: " + getLabel());21}2223@Override24protected void afterDelete() {25// Called after successful delete26System.out.println("Article deleted");27}28}
Common Query Patterns
Search by Multiple Tags
1// Find articles with any of the specified tags2List<Article> articles = Query.from(Article.class)3.where("tags = ?", Arrays.asList("java", "database", "tutorial"))4.selectAll();56// Find articles with all specified tags (requires all)7// This requires querying differently - use repeated predicates8Query<Article> query = Query.from(Article.class);9for (String tag : Arrays.asList("java", "database")) {10query.and("tags = ?", tag);11}12List<Article> articlesWithAllTags = query.selectAll();
Counting and Statistics
1// Total count2long totalArticles = Query.from(Article.class).count();34// Conditional count5long publishedCount = Query.from(Article.class)6.where("publishDate != missing")7.count();89// Count by author10long johnCount = Query.from(Article.class)11.where("author/name = ?", "John Doe")12.count();
Best Practices
1. Always Index Queryable Fields
1// Good2@Indexed3private String title;45// Bad - can't query this field6private String title;
2. Use Transactions for Multiple Writes
1// Good - atomic2db.beginWrites();3try {4author.save();5article.save();6db.commitWrites();7} finally {8db.endWrites();9}1011// Bad - non-atomic, could partially succeed12author.save();13article.save();
3. Use Pagination for Large Result Sets
1// Good - memory efficient2List<Article> page = Query.from(Article.class)3.select(offset, limit);45// Bad - loads everything into memory6List<Article> all = Query.from(Article.class)7.selectAll();
4. Prefer Path Queries Over Manual Joins
1// Good - simple and efficient2Query.from(Article.class)3.where("author/name = ?", "John Doe")4.selectAll();56// Avoid - more complex7Author author = Query.from(Author.class)8.where("name = ?", "John Doe")9.first();10Query.from(Article.class)11.where("author = ?", author)12.selectAll();
5. Initialize Collections in Getters
1// Good2public List<String> getTags() {3if (tags == null) {4tags = new ArrayList<>();5}6return tags;7}89// Bad - can return null10public List<String> getTags() {11return tags;12}