Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Official Dgraph Java client

License

NotificationsYou must be signed in to change notification settings

dgraph-io/dgraph4j

Repository files navigation

A minimal implementation for a Dgraph client for Java 11 and above, usinggrpc.

Note: v24.0.0 features an upgraded protobuf dependency which requires an upgrade to JDK 11. Onaccount of this breaking change, all legacy applications built upon JDK 8 would be impacted.

This client follows theDgraph Go client closely.

Before using this client, we highly recommend that you go throughdocs.dgraph.io, and understandhow to run and work with Dgraph.

UseDiscuss Issues for reporting issues aboutthis repository.

Table of Contents

Download

grab via Maven:

<dependency>  <groupId>io.dgraph</groupId>  <artifactId>dgraph4j</artifactId>  <version>24.2.0</version></dependency>

or Gradle:

compile'io.dgraph:dgraph4j:24.2.0'

Supported Versions

Depending on the version of Dgraph that you are connecting to, you will have to use a differentversion of this client.

Dgraph versiondgraph4j versionjava version
1.0.X1.X.X1.9.X
1.1.0 - 2.X.X2.X.X1.9.X
20.03.X-20.07.X20.03.X1.9.X
20.11.X20.11.X1.9.X
>= 21.XX.X21.XX.X1.9.X
>= 24.X.X24.X.X11

Note regarding Java 1.8.x support

v24.0.0 features an upgraded protoc-protobuf dependency that requires an upgrade to JDK 11. Thisversion is incompatible with Java 1.8 and and requires an upgrade to Java 11.

The following is only applicable to dgraph4j versions < v24.X.X.

  • If you aren't using gRPC with TLS, then the above version table will work for you with Java 1.8.xtoo.

  • If you're using gRPC with TLS on Java 1.8.x, then you will need to follow gRPC docshere. Basically,it will require you to add the following dependency in your app with correct version for thecorrespondinggrpc-netty version used bydgraph4j. You can find out the correct version of thedependency to use from the version combination table inthis section ingrpc-netty docs.

    For maven:

    <dependency>  <groupId>io.netty</groupId>  <artifactId>netty-tcnative-boringssl-static</artifactId>  <version><!-- See table in gRPC docs for correct version--></version></dependency>

    For Gradle:

    compile'io.netty:netty-tcnative-boringssl-static:<See table in gRPC docs for correct version>'

    The following table lists thegrpc-netty versions used by differentdgraph4j versions overtime, along with the supported versions ofnetty-tcnative-boringssl-static for the correspondinggrpc-netty version:

    dgraph4j versiongrpc-netty versionnetty-tcnative-boringssl-static version
    >= 24.0.01.65.14.1.100.Final
    >= 24.1.11.68.24.1.110.Final
    >= 24.2.01.69.14.1.111.Final

    For example, when usingdgraph4j v24.0.0, the version of thenetty-tcnative-boringssl-staticdependency to be used is4.1.100.Final, as suggested by gRPC docs forgrpc-netty v1.65.1.

Quickstart

Build and run theDgraphJavaSample project in thesamples folder, which contains an end-to-endexample of using the Dgraph Java client. Follow the instructions in the README of that project.

Intro

This library supports two styles of clients, the synchronous clientDgraphClient and the asyncclientDgraphAsyncClient. ADgraphClient orDgraphAsyncClient can be initialised by passing ita list ofDgraphBlockingStub clients. TheanyClient() API can randomly pick a stub, which canthen be used for GRPC operations. In the next section, we will explain how to create a synchronousclient and use it to mutate or query dgraph. For the async client, more details can be found in theUsing the Asynchronous Client section.

Using the Synchronous Client

Creating a Client

Using a Connection String

This library supports connecting to a Dgraph cluster using connection strings. Dgraph connectionsstrings take the formdgraph://{username:password@}host:port?args.

username andpassword are optional. If username is provided, a password must also be present. Ifsupplied, these credentials are used to log into a Dgraph cluster through the ACL mechanism.

Valid connection string args:

ArgValueDescription
apikey<key>a Dgraph Cloud API Key
bearertoken<token>an access token
sslmodedisable | require | verify-caTLS option, the default isdisable. Ifverify-ca is set, the TLS certificate configured in the Dgraph cluster must be from a valid certificate authority.

Note that usingsslmode=require disables certificate validation and significantly reduces thesecurity of TLS. This mode should only be used in non-production (e.g., testing or development)environments.

Some example connection strings:

ValueExplanation
dgraph://localhost:9080Connect to localhost, no ACL, no TLS
dgraph://sally:supersecret@dg.example.com:443?sslmode=verify-caConnect to remote server, use ACL and require TLS and a valid certificate from a CA
dgraph://foo-bar.grpc.us-west-2.aws.cloud.dgraph.io:443?sslmode=verify-ca&apikey=<your-api-connection-key>Connect to a Dgraph Cloud cluster
dgraph://foo-bar.grpc.hypermode.com?sslmode=verify-ca&bearertoken=<some access token>Connect to a Dgraph cluster protected by a secure gateway

Using theDgraphClient.open function with a connection string:

// open a connection to an ACL-enabled, non-TLS cluster and login as grootDgraphClientclient =DgraphClient.open("dgraph://groot:password@localhost:8090");// some time later...client.shutdown();

Using Managed Channels

The following code snippet shows how to create a synchronous client using three connections.

ManagedChannelchannel1 =ManagedChannelBuilder    .forAddress("localhost",9080)    .usePlaintext().build();DgraphStubstub1 =DgraphGrpc.newStub(channel1);ManagedChannelchannel2 =ManagedChannelBuilder    .forAddress("localhost",9082)    .usePlaintext().build();DgraphStubstub2 =DgraphGrpc.newStub(channel2);ManagedChannelchannel3 =ManagedChannelBuilder    .forAddress("localhost",9083)    .usePlaintext().build();DgraphStubstub3 =DgraphGrpc.newStub(channel3);DgraphClientdgraphClient =newDgraphClient(stub1,stub2,stub3);

Creating a Client for Dgraph Cloud

If you want to connect to Dgraph running on aDgraph Cloud instance, thenall you need is the URL of your Dgraph Cloud instance and the API key. You can get a client withthem as follows :

DgraphStubstub =DgraphClient.clientStubFromCloudEndpoint("https://your-instance.cloud.dgraph.io/graphql","your-api-key");DgraphClientdgraphClient =newDgraphClient(stub);

Note theDgraphClient.open method can be used if you have a Dgraph connection string (see above).

Creating a Secure Client using TLS

To setup a client using TLS, you could use the following code snippet. The server needs to be setupusing the instructions providedhere.

If you are doing client verification, you need to convert the client key from PKCS#1 format toPKCS#8 format. By default, grpc doesn't support reading PKCS#1 format keys. To convert the format,you could use theopenssl tool.

First, let's install theopenssl tool:

apt install openssl

Now, use the following command to convert the key:

openssl pkcs8 -in client.name.key -topk8 -nocrypt -out client.name.java.key

Now, you can use the following code snippet to connect to Alpha over TLS:

SslContextBuilderbuilder =GrpcSslContexts.forClient();builder.trustManager(newFile("<path to ca.crt>"));// Skip the next line if you are not performing client verification.builder.keyManager(newFile("<path to client.name.crt>"),newFile("<path to client.name.java.key>"));SslContextsslContext =builder.build();ManagedChannelchannel =NettyChannelBuilder.forAddress("localhost",9080)    .sslContext(sslContext)    .build();DgraphGrpc.DgraphStubstub =DgraphGrpc.newStub(channel);DgraphClientdgraphClient =newDgraphClient(stub);

Check Dgraph version

Checking the version of the Dgraph server this client is interacting with is as easy as:

Versionv =dgraphClient.checkVersion();System.out.println(v.getTag());

Checking the version, before doing anything else can be used as a test to find out if the client isable to communicate with the Dgraph server. This will also help reduce the latency of the firstquery/mutation which results from some dynamic library loading and linking that happens in JVM (seethis issue for more details).

Login Using ACL

If ACL is enabled then you can log-in to the default namespace (0) with following:

dgraphClient.login(USER_ID,USER_PASSWORD);

For logging-in to some other namespace, use theloginIntoNamespace method on the client:

dgraphClient.loginIntoNamespace(USER_ID,USER_PASSWORD,NAMESPACE);

Once logged-in, thedgraphClient object can be used to do any further operations.

Altering the Database

To set the schema, create anOperation object, set the schema and pass it toDgraphClient#altermethod.

Stringschema ="name: string @index(exact) .";Operationoperation =Operation.newBuilder().setSchema(schema).build();dgraphClient.alter(operation);

Starting Dgraph version 20.03.0, indexes can be computed in the background. You can call thefunctionsetRunInBackground(true) as shown below before callingalter. You can find more detailshere.

Stringschema ="name: string @index(exact) .";Operationoperation =Operation.newBuilder()        .setSchema(schema)        .setRunInBackground(true)        .build();dgraphClient.alter(operation);

Operation contains other fields as well, including drop predicate and drop all. Drop all is usefulif you wish to discard all the data, and start from a clean slate, without bringing the instancedown.

// Drop all data including schema from the dgraph instance. This is useful// for small examples such as this, since it puts dgraph into a clean// state.dgraphClient.alter(Operation.newBuilder().setDropAll(true).build());

Creating a Transaction

There are two types of transactions in dgraph, i.e. the read-only transactions that only includequeries and the transactions that change data in dgraph with mutate operations. Both the synchronousclientDgraphClient and the async clientDgraphAsyncClient support the two types of transactionsby providing thenewTransaction and thenewReadOnlyTransaction APIs. Creating a transaction is alocal operation and incurs no network overhead.

In most of the cases, the normal read-write transactions is used, which can have any number of queryor mutate operations. However, if a transaction only has queries, you might benefit from a read-onlytransaction, which can share the same read timestamp across multiple such read-only transactions andcan result in lower latencies.

For normal read-write transactions, it is a good practise to callTransaction#discard() in afinally block after running the transaction. CallingTransaction#discard() afterTransaction#commit() is a no-op and you can calldiscard() multiple times with no additionalside-effects.

Transactiontxn =dgraphClient.newTransaction();try {// Do something here// ...}finally {txn.discard();}

For read-only transactions, there is no need to callTransaction.discard, which is equivalent to ano-op.

TransactionreadOnlyTxn =dgraphClient.newReadOnlyTransaction();

Read-only transactions can be set as best-effort. Best-effort queries relax the requirement oflinearizable reads. This is useful when running queries that do not require a result from the latesttimestamp.

TransactionbestEffortTxn =dgraphClient.newReadOnlyTransaction()    .setBestEffort(true);

Running a Mutation

Transaction#mutate runs a mutation. It takes in aMutation object, which provides two main waysto set data: JSON and RDF N-Quad. You can choose whichever way is convenient.

We're going to use JSON. First we define aPerson class to represent a person. This data will beserialized into JSON.

classPerson {StringnamePerson() {}}

Next, we initialise aPerson object, serialize it and use it inMutation object.

// Create dataPersonperson =newPerson();person.name ="Alice";// Serialize itGsongson =newGson();Stringjson =gson.toJson(person);// Run mutationMutationmu =Mutation.newBuilder()    .setSetJson(ByteString.copyFromUtf8(json.toString()))    .build();// mutationResponse stores a Response protocol buffer objectResponsemutationResponse =txn.mutate(mu);// eg: to get the UIDs created in this mutationSystem.out.println(mutationResponse.getUidsMap())

Sometimes, you only want to commit mutation, without querying anything further. In such cases, youcan use aCommitNow field inMutation object to indicate that the mutation must be immediatelycommitted.

Mutation can be run using thedoRequest function as well.

Requestrequest =Request.newBuilder()    .addMutations(mu)    .build();txn.doRequest(request);

Committing a Transaction

A transaction can be committed using theTransaction#commit() method. If your transactionconsisted solely of calls toTransaction#query(), and no calls toTransaction#mutate(), thencallingTransaction#commit() is not necessary.

An error will be returned if other transactions running concurrently modify the same data that wasmodified in this transaction. It is up to the user to retry transactions when they fail.

Transactiontxn =dgraphClient.newTransaction();try {// …// Perform any number of queries and mutations// …// and finally …txn.commit()}catch (TxnConflictExceptionex) {// Retry or handle exception.}finally {// Clean up. Calling this after txn.commit() is a no-op// and hence safe.txn.discard();}

Running a Query

You can run a query by callingTransaction#query(). You will need to pass in a GraphQL+- querystring, and a map (optional, could be empty) of any variables that you might want to set in thequery.

The response would contain aJSON field, which has the JSON encoded result. You will need todecode it before you can do anything useful with it.

Let’s run the following query:

queryall($a:string) {all(func:eq(name,$a)) {name  }}

First we must create aPeople class that will help us deserialize the JSON result:

classPeople {List<Person>all;People() {}}

Then we run the query, deserialize the result and print it out:

// QueryStringquery ="query all($a: string){\n" +"  all(func: eq(name, $a)) {\n" +"    name\n" +"  }\n" +"}\n";Map<String,String>vars =Collections.singletonMap("$a","Alice");Responseresponse =dgraphClient.newReadOnlyTransaction().queryWithVars(query,vars);// DeserializePeopleppl =gson.fromJson(response.getJson().toStringUtf8(),People.class);// Print resultsSystem.out.printf("people found: %d\n",ppl.all.size());ppl.all.forEach(person ->System.out.println(person.name));

This should print:

people found: 1Alice

You can also usedoRequest function to run the query.

Requestrequest =Request.newBuilder()    .setQuery(query)    .build();txn.doRequest(request);

Running a Query with RDF response

You can get query results as an RDF response by calling eitherqueryRDF() orqueryRDFWithVars().The response contains thegetRdf() method, which will provide the RDF encoded output.

Note: If you are querying foruid values only, use a JSON format response

// QueryStringquery ="query me($a: string) { me(func: eq(name, $a)) { name }}";Map<String,String>vars =Collections.singletonMap("$a","Alice");Responseresponse =dgraphAsyncClient.newReadOnlyTransaction().queryRDFWithVars(query,vars).join();// Print resultsSystem.out.println(response.getRdf().toStringUtf8());

This should print (assuming Alice'suid is0x2):

<0x2><name>"Alice".

Running an Upsert: Query + Mutation

Thetxn.doRequest function allows you to run upserts consisting of one query and one mutation.Variables can be defined in the query and used in the mutation. You could also usetxn.doRequestto perform a query followed by a mutation.

To know more about upsert, we highly recommend going through the docs athttps://docs.dgraph.io/mutations/#upsert-block.

Stringquery ="query {\n" +"user as var(func: eq(email,\"wrong_email@dgraph.io\"))\n" +"}\n";Mutationmu =Mutation.newBuilder()    .setSetNquads(ByteString.copyFromUtf8("uid(user) <email>\"correct_email@dgraph.io\" ."))    .build();Requestrequest =Request.newBuilder()    .setQuery(query)    .addMutations(mu)    .setCommitNow(true)    .build();txn.doRequest(request);

Running a Conditional Upsert

The upsert block also allows specifying a conditional mutation block using an@if directive. Themutation is executed only when the specified condition is true. If the condition is false, themutation is silently ignored.

See more about Conditional UpsertHere.

Stringquery ="query {\n" +"user as var(func: eq(email,\"wrong_email@dgraph.io\"))\n" +"}\n";Mutationmu =Mutation.newBuilder()    .setSetNquads(ByteString.copyFromUtf8("uid(user) <email>\"correct_email@dgraph.io\" ."))    .setCond("@if(eq(len(user), 1))")    .build();Requestrequest =Request.newBuilder()    .setQuery(query)    .addMutations(mu)    .setCommitNow(true)    .build();txn.doRequest(request);

Setting Deadlines

It is recommended that you always set a deadline for each client call, after which the clientterminates. This is in line with the recommendation for any gRPC client. Readthis forumpost for more details.

Setting deadlines for all requests

channel =ManagedChannelBuilder.forAddress("localhost",9080).usePlaintext(true).build();DgraphGrpc.DgraphStubstub =DgraphGrpc.newStub(channel);ClientInterceptortimeoutInterceptor =newClientInterceptor(){@Overridepublic <ReqT,RespT>ClientCall<ReqT,RespT>interceptCall(MethodDescriptor<ReqT,RespT>method,CallOptionscallOptions,Channelnext) {returnnext.newCall(method,callOptions.withDeadlineAfter(500,TimeUnit.MILLISECONDS));    }};stub =stub.withInterceptors(timeoutInterceptor);DgraphClientdgraphClient =newDgraphClient(stub);

Setting deadlines for a single request

dgraphClient.newTransaction().query(query,500,TimeUnit.MILLISECONDS);

Setting Metadata Headers

Certain headers such as authentication tokens need to be set globally for all subsequent calls.Below is an example of setting a header with the name "auth-token":

// create the stub firstManagedChannelchannel =ManagedChannelBuilder.forAddress(TEST_HOSTNAME,TEST_PORT).usePlaintext(true).build();DgraphStubstub =DgraphGrpc.newStub(channel);// use MetadataUtils to augment the stub with headersMetadatametadata =newMetadata();metadata.put(Metadata.Key.of("auth-token",Metadata.ASCII_STRING_MARSHALLER),"the-auth-token-value");stub =MetadataUtils.attachHeaders(stub,metadata);// create the DgraphClient wrapper around the stubDgraphClientdgraphClient =newDgraphClient(stub);// trigger a RPC call using the DgraphClientdgraphClient.alter(Operation.newBuilder().setDropAll(true).build());

Helper Methods

Delete multiple edges

The example below uses the helper methodHelpers#deleteEdges to delete multiple edgescorresponding to predicates on a node with the given uid. The helper method takes an existingmutation, and returns a new mutation with the deletions applied.

Mutationmu =Mutation.newBuilder().build()mu =Helpers.deleteEdges(mu,uid,"friends","loc");dgraphClient.newTransaction().mutate(mu);

Closing the DB Connection

To disconnect from Dgraph, callManagedChannel#shutdown on the gRPC channel object created whencreating a Dgraph client.

channel.shutdown();

You can also close all channels in from the client object:

dgraphClient.shutdown();

Using the Asynchronous Client

Dgraph Client for Java also bundles an asynchronous API, which can be used by instantiating theDgraphAsyncClient class. The usage is almost exactly the same as theDgraphClient (show inprevious section) class. The main differences is that theDgraphAsyncClient#newTransacation()returns anAsyncTransaction class. The API forAsyncTransaction is exactlyTransaction. Theonly difference is that instead of returning the results directly, it returns immediately with acorrespondingCompletableFuture<T> object. This object represents the computation which runsasynchronously to yield the result in the future. Read more aboutCompletableFuture<T> in theJava 8 documentation.

Here is the asynchronous version of the code above, which runs a query.

// QueryStringquery ="query all($a: string){\n" +"  all(func: eq(name, $a)) {\n" +"    name\n" +"}\n" +"}\n";Map<String,String>vars =Collections.singletonMap("$a","Alice");AsyncTransactiontxn =dgraphAsyncClient.newTransaction();txn.query(query).thenAccept(response -> {// DeserializePeopleppl =gson.fromJson(res.getJson().toStringUtf8(),People.class);// Print resultsSystem.out.printf("people found: %d\n",ppl.all.size());ppl.all.forEach(person ->System.out.println(person.name));});

Checking the request latency

If you would like to see the latency for either a mutation or query request, the latency field inthe returned result can be helpful. Here is an example to log the latency of a query request:

Responseresp =txn.query(query);Latencylatency =resp.getLatency();logger.info("parsing latency:" +latency.getParsingNs());logger.info("processing latency:" +latency.getProcessingNs());logger.info("encoding latency:" +latency.getEncodingNs());

Similarly you can get the latency of a mutation request:

AssignedassignedIds =dgraphClient.newTransaction().mutate(mu);Latencylatency =assignedIds.getLatency();

Development

Building the source

Warning: The gradle build runs integration tests on a locally running Dgraph server. The testswill remove all data from your Dgraph instance. So make sure that you don't have any important dataon your Dgraph instance.

./gradlew build

If you have made changes to thetask.proto file, this step will also regenerate the source filesgenerated by Protocol Buffer tools.

Code Style

We usegoogle-java-format to format the source code. If you run./gradlew build, you will bewarned if there is code that is not conformant. You can run./gradlew goJF to format the sourcecode, before committing it.

Running unit tests

Warning: This command will runs integration tests on a locally running Dgraph server. The testswill remove all data from your Dgraph instance. So make sure that you don't have any important dataon your Dgraph instance.

Make sure you have a Dgraph server running on localhost before you run this task.

./gradlewtest

About

Official Dgraph Java client

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Contributors38

Languages


[8]ページ先頭

©2009-2025 Movatter.jp