Java API (Remote)

ArcadeDB comes with a convenient Java library to work with a remote database.

RemoteServer Class

The RemoteServer class is used to manage remote server connections and databases.

RemoteDatabase Class

The RemoteDatabase class is used to work with a remote database instance.

Add the following dependencies in your Maven pom.xml file under the tag <dependencies>:

<dependency>
    <groupId>com.arcadedb</groupId>
    <artifactId>arcadedb-engine</artifactId>
    <version>26.5.1</version>
</dependency>
<dependency>
    <groupId>com.arcadedb</groupId>
    <artifactId>arcadedb-network</artifactId>
    <version>26.5.1</version>
</dependency>

10-Minute Tutorial

Connect to a remote database

There are 2 main classes that handle the connection to the remote database by translating the Java API into http-calls:

We start by creating a RemoteServer:

RemoteServer server = new RemoteServer("localhost", 2480, "root", "playwithdata");

Next, let’s create a RemoteDatabase instance pointing to the database "mydb" on ArcadeDB Server located on "localhost", port 5432. User and passwords are respectively "root" and "playwithdata":

RemoteDatabase database = new RemoteDatabase("localhost", 2480, "mydb", "root", "playwithdata");
The RemoteDatabase is not thread-safe. It holds a backend session. If a thread closes a transaction, it also closes the session, so that any request performed by a concurrent thread using the same RemoteDatabase instance leads to unexpected behavior. To avoid concurrency problems you should not hold RemoteDatabase instances as fields on the heap, but rather create a new instance in method scope as a variable on the stack.

RemoteImmutableVertex and RemoteMutableVertex

Vertices retrieved through RemoteDatabase are instances of RemoteImmutableVertex (or RemoteMutableVertex after calling modify()). These classes expose graph-traversal methods that translate directly into SQL queries over HTTP — there is no per-vertex round trip.

Traversal methods

Method Description

getVertices(direction, edgeTypes…​)

Returns all neighbor vertices, transparently paginating in batches of DEFAULT_PAGE_SIZE (2,000) records per HTTP call.

getVerticesPaged(direction, pageSize, edgeTypes…​)

Same as above but with an explicit page size. Use to tune round-trip vs. payload trade-off for supernodes.

getVertices(direction, limit, skip, edgeTypes…​)

Returns a single page of up to limit neighbor vertices starting at offset skip. Useful for implementing your own pagination.

getEdges(direction, edgeTypes…​)

Returns all connected edges, transparently paginating with DEFAULT_PAGE_SIZE per HTTP call.

getEdgesPaged(direction, pageSize, edgeTypes…​)

Same as above but with an explicit page size.

getEdges(direction, limit, skip, edgeTypes…​)

Returns a single page of up to limit edges starting at offset skip.

countEdges(direction, edgeTypes…​)

Returns the number of connected edges (single HTTP call, no result materialization).

DEFAULT_PAGE_SIZE = 2_000 is the batch size used by getVertices() / getEdges(). Values up to 20,000 are safe against the server’s default result limit.

Example

RemoteDatabase db = new RemoteDatabase("localhost", 2480, "mydb", "root", "playwithdata");

// standard traversal -- paginates automatically, no cast needed
Vertex hub = db.lookupByRID(new RID(db, "#1:0"), true);
for (Vertex neighbor : hub.getVertices(Vertex.DIRECTION.OUT, "Knows")) {
    System.out.println(neighbor.getString("name"));
}

// supernode -- explicit page size requires cast to the concrete remote type
RemoteImmutableVertex hubRemote = (RemoteImmutableVertex) hub;
for (Vertex neighbor : hubRemote.getVerticesPaged(Vertex.DIRECTION.OUT, 5_000, "Knows")) {
    System.out.println(neighbor.getString("name"));
}
Each page uses a SKIP/LIMIT SQL clause, so the edge list is re-scanned from the beginning on every page. For supernodes with millions of edges prefer a SQL query with a server-side WHERE filter to narrow results before fetching them.

The database, which name you have given as the third parameter in the constructor is not automatically created nor is it checked whether it exists. Before you use the RemoteDatabase make sure that the database exists, or, if it doesn’t, create it:

if (!server.exists("mydb"))
    server.create();

For a list of all databases hosted by the server use:

List<String> databases = server.databases();
assert(databases.contains("mydb"));

Now that we have the database created we may create the schema (but we may also work without):

String schema =
"""
    create vertex type Customer if not exists;
    create property Customer.name if not exists string;
    create property Customer.surname if not exists string;
    create index if not exists on Customer (name, surname) unique;
""";
database.command("sqlscript", schema);

As language we have used sqlscript, which has the advantage that all the statements in the script are executed within one single transaction. Of course you can also send these requests line by line with sql as language. If you go line by line it is important to enclose the requests in a common transaction:

database.begin();
database.command("sql", "create vertex type Customer if not exists");
database.command("sql", "create property Customer.name if not exists string");
database.command("sql", "create property Customer.surname if not exists string");
database.command("sql", "create index if not exists on Customer (name, surname) unique");
database.commit();

Or:

database.transaction(() -> {
    ...
});

The transaction method encloses the lambda function with a begin - commit, or a rollback in case of any exception.

Now that the schema is in place, we will insert some data:

database.command("sql", "insert into Customer(name, surname) values(?, ?)", "Jay", "Miner");
database.command("sql",
        "update Customer set lastcall = :lastcall where name = :name and surname = :surname",
        Map.of("lastcall", Date.from(Instant.now()), "name", "Jay", "surname", "Miner"));

The first command creates a new vertex of type 'Customer'. The second command updates this vertex by adding a new value for the property 'lastcall'. The first command uses unnamed parameters where the values are mapped by their order in the parameter list. Whereas the second command uses named parameters, which are mapped by the parameter names.

We can now query some customer data from our database:

ResultSet resultSet = database.query("sql", "select from Customer where name = :name",
        Map.of("name", "Jay"));
Map<String, Object> customer = resultSet.stream().findFirst().map(Result::toMap).orElse(Map.of());

The query returns a ResultSet, which holds an array of objects of type Result, which can be read as Map:

{name=Jay, @cat=d, @rid=#1:0, lastcall=1675961747290, surname=Miner, @type=Customer}