This guide will teach you the basics of using the SurrealDB SDK for Java to interact with a SurrealDB instance. We will cover topics such as installing the SDK, connecting to an in-memory database, and performing basic queries. For demonstration purposes, the presented queries follow the example of a library database.
ImportantYou can find a working example project implementing the queries described in the guide in our surrealdb-examples repository
This guide assumes the following:
The SurrealDB Java SDK is available on the Maven Central repository. You can install it using Gradle or Maven. Copy the relevant code snippet into your build.gradle or pom.xml file to add the SDK as a dependency.
ext { surrealdbVersion = "0.2.1" } dependencies { implementation "com.surrealdb:surrealdb:${surrealdbVersion}" }
val surrealdbVersion by extra("0.2.1") dependencies { implementation("com.surrealdb:surrealdb:${surrealdbVersion}") }
<dependency> <groupId>com.surrealdb</groupId> <artifactId>surrealdb</artifactId> <version>0.2.1</version> </dependency>
The first step towards sending queries is instantiating the driver. In the following example we use a try-with-resources block to ensure the driver is properly closed after use. After instantiating the driver, we will connect to an in-memory database, and select our namespace and database.
SurrealDB.javapackage me.yourname.example; import com.surrealdb.Surreal; public class SurrealDB { public static void main(String[] args) { // Instantiate the driver try (final Surreal driver = new Surreal()) { // Connect to an in-memory database driver.connect("memory"); // Select a namespace and database driver.useNs("example").useDb("example"); // ... } } }
In order to represent database records within your application, you need to define POJO classes that match the tables in your database. Additionally, model classes require a public no-argument constructor to be used with the SDK.
For this example, we will create a Book class representing book records in our database.
Book.javapackage me.yourname.surrealdb; import com.surrealdb.RecordId; import java.time.ZonedDateTime; public class Book { public RecordId id; public String title; public String author; public ZonedDateTime publishedAt; public boolean available; // A default constructor is required public Book() { } public Book(String title, String author, ZonedDateTime publishedAt, boolean available) { this.title = title; this.author = author; this.publishedAt = publishedAt; this.available = available; } public String toString() { return "Book{" + "id=" + id + ", title='" + title + '\'' + ", author='" + author + '\'' + ", publishedAt=" + publishedAt + ", available=" + available + '}'; } }
Model classes are extremely useful as they can be used to represent more than just tables, such as complex query responses, relations, and more. Additionally, the fields in model classes can contain nested objects, arrays, and other SurrealDB data types.
You can learn more about supported SurrealDB data types on the data types page.
The first step towards querying data is to populate the database with records. This can be achieved using the create method, which allows you to pass model class instances to create new records.
In the following example, we instantiate a new Book object and pass it to the create method. This method will return a list of created Book records, each with a unique record id.
// Create a new book Book book = new Book( "Aeon's Surreal Renaissance", "Dave MacLeod", ZonedDateTime.parse("2024-10-15T00:00:00Z"), true ); // Create a book record Book created = driver.create(Book.class, "book", book).get(0); // Print the record id System.out.println("Created a new book with id " + created.id);
Since create allows us to create multiple records at once, it returns a list of books. In this case, we only created one book, so we use .get(0) to retrieve it.
You might also want to create records with a specific ID. In this case, you can pass the RecordId as the second argument to the create method. This approach will return the Book instance directly instead of a list.
// Create a book record with a custom id Book created = driver.create(Book.class, new RecordId("book", "aeon"), book);
Now we have records in our database, we can query them using the select method. This method allows you to retrieve either all records from a table, or select a single record by its ID.
// Select all books Iterator<Book> books = driver.select(Book.class, "book"); while (books.hasNext()) { System.out.println("Found book: " + books.next().title); } // Select a specific book Optional<Book> aeon = driver.select(Book.class, new RecordId("book", "aeon")); aeon.ifPresent(value -> { System.out.println("Found book: " + value.title); });
While the select method is useful for retrieving all records or a single record by ID, you may want to perform filtering, ordering, and other operations on your select queries. In these situations you can use the query method to execute custom SurrealQL queries. We will cover this in the Complex queries section.
Records can be updated using the update and upsert methods. These methods allow you to update all records of a table, or a single record by its ID.
In the following example we update the availability status of a book record. We pass the id of the record we want to update, the type of update we want to perform, and the updated record. The UpType.MERGE parameter tells the SDK to merge the updated record with the existing record, keeping any fields that are not present in the updated record.
After the update is applied, a new Book instance is returned with the updated fields.
// Modify an existing book record existing.available = false; // Update the book record Book updated = driver.update(Book.class, existing.id, UpType.MERGE, existing); // Print the book status System.out.println("New availability: " + updated.available);
You can pass a table name instead of a record ID to update all records in a table. This will return an iterator of updated records.
// Create an empty book instance Book update = new Book(); update.available = true; // Update all book records Iterator<Book> updated = driver.update(Book.class, "book", UpType.MERGE, update);
As you can see, we can instantiate an empty Book object and set only the fields we want to update. This allows us to conveniently update only specific fields.
Existing records can be deleted using the delete method. You can either pass a table name to delete all records in a table, or a record ID to delete a specific record.
// Delete a specific book driver.delete(new RecordId("book", "aeon")); // Delete all book records driver.delete("book");
The Java SDK makes it easy to define relations between records using the relate method. This method takes two RecordId instances, an edge name, and an optional value model.
This next example demonstrates how to relate a book to a publisher.
RecordId bookId = new RecordId("book", "aeon"); RecordId publisherId = new RecordId("publisher", "surrealdb"); // Relate a book to a publisher driver.relate(bookId, "published_by", publisherId);
Much like other methods, you can also pass a class reference as a first argument to control the return and value type. This class must be a subclass of Relation, or simply Relation itself. This class holds the id, in, and out of a relation.
Relation relation = driver.relate(Relation.class, bookId, "published_by", publisherId); System.out.println("Related " + relation.in + " to " + relation.out + " with relation " + relation.id);
While the previously mentioned methods are useful for basic operations, you may want to perform more complex queries. This can be achieved using the query method, which allows you to execute custom SurrealQL queries.
You can pass a query string to the query method and it will return a Response object containing the query results.
Response response = driver.query("SELECT * FROM book WHERE available = true"); // Take the result of the first statement Value result = response.take(0);
You can optionally pass parameters to the query by using the queryBind method, which takes a map of values to bind to the query.
Response response = driver.queryBind( "SELECT * FROM book WHERE available = $status", Map.of("status", true) ); // Take the result of the first statement Value result = response.take(0);
While we can represent object data using model classes, we can also use the Value class to represent SurrealDB data types directly.
In all previous examples we can omit the model class reference arguments to make methods return a Value instance instead of a model class instance. This class provides useful functions to check and convert data types, as well as access nested objects and arrays.
Value result = driver.query("RETURN 123").take(0); // Check if the result is a long before casting if (result.isLong()) { System.out.println("Long value: " + result.getLong()); }
Now that you have learned the basics of the SurrealDB SDK for Java, you can learn more about the API in the API documentation or explore the data types supported by the SDK.