Skip to main content
Version: 1.1.0

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:

1
import com.psddev.dari.db.Record;
2
3
public class Article extends Record {
4
5
@Indexed
6
private String title;
7
8
@Indexed
9
@Required
10
private Author author;
11
12
@Indexed
13
private Date publishDate;
14
15
private String content;
16
17
private List<String> tags;
18
19
// Getters and setters
20
public String getTitle() {
21
return title;
22
}
23
24
public void setTitle(String title) {
25
this.title = title;
26
}
27
28
public Author getAuthor() {
29
return author;
30
}
31
32
public void setAuthor(Author author) {
33
this.author = author;
34
}
35
36
public Date getPublishDate() {
37
return publishDate;
38
}
39
40
public void setPublishDate(Date publishDate) {
41
this.publishDate = publishDate;
42
}
43
44
public String getContent() {
45
return content;
46
}
47
48
public void setContent(String content) {
49
this.content = content;
50
}
51
52
public List<String> getTags() {
53
if (tags == null) {
54
tags = new ArrayList<>();
55
}
56
return tags;
57
}
58
59
public void setTags(List<String> tags) {
60
this.tags = tags;
61
}
62
63
// Optional: Override getLabel for display purposes
64
@Override
65
public String getLabel() {
66
return title != null ? title : "Untitled Article";
67
}
68
}
1
import com.psddev.dari.db.Record;
2
3
public class Author extends Record {
4
5
@Indexed
6
@Required
7
private String name;
8
9
@Indexed
10
private String email;
11
12
private String bio;
13
14
// Getters and setters
15
public String getName() {
16
return name;
17
}
18
19
public void setName(String name) {
20
this.name = name;
21
}
22
23
public String getEmail() {
24
return email;
25
}
26
27
public void setEmail(String email) {
28
this.email = email;
29
}
30
31
public String getBio() {
32
return bio;
33
}
34
35
public void setBio(String bio) {
36
this.bio = bio;
37
}
38
39
@Override
40
public String getLabel() {
41
return 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 author
2
Author author = new Author();
3
author.setName("John Doe");
4
author.setEmail("john@example.com");
5
author.setBio("Experienced writer and developer");
6
author.save();
7
8
// Create an article
9
Article article = new Article();
10
article.setTitle("Getting Started with DB plugin");
11
article.setAuthor(author); // Reference to author
12
article.setPublishDate(new Date());
13
article.setContent("This is the content of the article...");
14
article.setTags(Arrays.asList("database", "tutorial", "java"));
15
article.save();

Reading Objects by ID

Every Record has a unique ID (UUID):

1
// Get the ID
2
UUID articleId = article.getId();
3
4
// Later, load by ID
5
Article loaded = Query.from(Article.class)
6
.where("_id = ?", articleId)
7
.first();

Querying Objects

1
// Find all articles
2
List<Article> allArticles = Query.from(Article.class)
3
.selectAll();
4
5
// Find articles by author
6
List<Article> johnArticles = Query.from(Article.class)
7
.where("author = ?", author)
8
.selectAll();
9
10
// Find articles by author name (path-based query)
11
List<Article> johnArticles = Query.from(Article.class)
12
.where("author/name = ?", "John Doe")
13
.selectAll();
14
15
// Find recent articles
16
Date cutoffDate = DateUtils.addDays(new Date(), -7);
17
List<Article> recentArticles = Query.from(Article.class)
18
.where("publishDate > ?", cutoffDate)
19
.sortDescending("publishDate")
20
.selectAll();
21
22
// Paginated results
23
List<Article> firstPage = Query.from(Article.class)
24
.sortDescending("publishDate")
25
.select(0, 10); // Skip 0, limit 10
26
27
// Count articles
28
long totalArticles = Query.from(Article.class)
29
.count();

Updating Objects

1
// Load the object
2
Article article = Query.from(Article.class)
3
.where("_id = ?", articleId)
4
.first();
5
6
// Modify fields
7
article.setTitle("Updated Title");
8
article.getTags().add("updated");
9
10
// Save changes
11
article.save();

Deleting Objects

1
// Delete a single object
2
article.delete();
3
4
// Delete by query
5
Query.from(Article.class)
6
.where("publishDate < ?", cutoffDate)
7
.deleteAll();

Working with References

DB plugin automatically handles relationships between objects:

1
// Save author first
2
Author author = new Author();
3
author.setName("Jane Smith");
4
author.save();
5
6
// Create article with reference
7
Article article = new Article();
8
article.setTitle("My Article");
9
article.setAuthor(author); // This stores a reference, not a copy
10
article.save();
11
12
// Later, when you load the article
13
Article loaded = Query.from(Article.class)
14
.where("_id = ?", articleId)
15
.first();
16
17
// The author is lazy-loaded
18
Author loadedAuthor = loaded.getAuthor(); // Database query happens here
19
System.out.println(loadedAuthor.getName()); // "Jane Smith"

Reference Resolution Options

1
// Reference-only query (don't load full objects)
2
List<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:

1
Database db = Database.Static.getDefault();
2
3
try {
4
db.beginWrites();
5
6
// Create multiple objects
7
Author author = new Author();
8
author.setName("John Doe");
9
author.save();
10
11
Article article1 = new Article();
12
article1.setTitle("First Article");
13
article1.setAuthor(author);
14
article1.save();
15
16
Article article2 = new Article();
17
article2.setTitle("Second Article");
18
article2.setAuthor(author);
19
article2.save();
20
21
// Commit all changes
22
db.commitWrites();
23
24
} finally {
25
// Always clean up (rolls back on exception)
26
db.endWrites();
27
}

Validation

DB plugin validates objects before saving:

1
public class Article extends Record {
2
3
@Required
4
@Maximum(200)
5
private String title;
6
7
@Required
8
private Author author;
9
10
// Custom validation
11
@Override
12
protected void onValidate() {
13
if (title != null && title.contains("spam")) {
14
getState().addError(
15
getState().getField("title"),
16
new IllegalArgumentException("Title contains spam")
17
);
18
}
19
}
20
}
21
22
// Attempting to save an invalid object throws ValidationException
23
Article article = new Article();
24
// article.setTitle("Required field missing");
25
// article.setAuthor(null); // Also required
26
27
try {
28
article.save();
29
} catch (ValidationException e) {
30
// Handle validation errors
31
for (ObjectField field : e.getErrors().keySet()) {
32
System.out.println("Error in " + field.getInternalName() + ": "
33
+ e.getErrors().get(field));
34
}
35
}

Lifecycle Hooks

Override lifecycle methods to add custom behavior:

1
public class Article extends Record {
2
3
@Override
4
protected void beforeSave() {
5
// Called before validation and save
6
if (publishDate == null) {
7
publishDate = new Date();
8
}
9
}
10
11
@Override
12
protected void afterSave() {
13
// Called after successful save
14
System.out.println("Article saved: " + getLabel());
15
}
16
17
@Override
18
protected void beforeDelete() {
19
// Called before delete
20
System.out.println("Deleting article: " + getLabel());
21
}
22
23
@Override
24
protected void afterDelete() {
25
// Called after successful delete
26
System.out.println("Article deleted");
27
}
28
}

Common Query Patterns

Search by Multiple Tags

1
// Find articles with any of the specified tags
2
List<Article> articles = Query.from(Article.class)
3
.where("tags = ?", Arrays.asList("java", "database", "tutorial"))
4
.selectAll();
5
6
// Find articles with all specified tags (requires all)
7
// This requires querying differently - use repeated predicates
8
Query<Article> query = Query.from(Article.class);
9
for (String tag : Arrays.asList("java", "database")) {
10
query.and("tags = ?", tag);
11
}
12
List<Article> articlesWithAllTags = query.selectAll();

Counting and Statistics

1
// Total count
2
long totalArticles = Query.from(Article.class).count();
3
4
// Conditional count
5
long publishedCount = Query.from(Article.class)
6
.where("publishDate != missing")
7
.count();
8
9
// Count by author
10
long johnCount = Query.from(Article.class)
11
.where("author/name = ?", "John Doe")
12
.count();

Best Practices

1. Always Index Queryable Fields

1
// Good
2
@Indexed
3
private String title;
4
5
// Bad - can't query this field
6
private String title;

2. Use Transactions for Multiple Writes

1
// Good - atomic
2
db.beginWrites();
3
try {
4
author.save();
5
article.save();
6
db.commitWrites();
7
} finally {
8
db.endWrites();
9
}
10
11
// Bad - non-atomic, could partially succeed
12
author.save();
13
article.save();

3. Use Pagination for Large Result Sets

1
// Good - memory efficient
2
List<Article> page = Query.from(Article.class)
3
.select(offset, limit);
4
5
// Bad - loads everything into memory
6
List<Article> all = Query.from(Article.class)
7
.selectAll();

4. Prefer Path Queries Over Manual Joins

1
// Good - simple and efficient
2
Query.from(Article.class)
3
.where("author/name = ?", "John Doe")
4
.selectAll();
5
6
// Avoid - more complex
7
Author author = Query.from(Author.class)
8
.where("name = ?", "John Doe")
9
.first();
10
Query.from(Article.class)
11
.where("author = ?", author)
12
.selectAll();

5. Initialize Collections in Getters

1
// Good
2
public List<String> getTags() {
3
if (tags == null) {
4
tags = new ArrayList<>();
5
}
6
return tags;
7
}
8
9
// Bad - can return null
10
public List<String> getTags() {
11
return tags;
12
}