467 lines
79 KiB
HTML
467 lines
79 KiB
HTML
<html><head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
|
<title>14. Spring Data Cloud Datastore</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud-gcp.html" title="Spring Cloud GCP Reference Documentation"><link rel="up" href="multi_spring-cloud-gcp.html" title="Spring Cloud GCP Reference Documentation"><link rel="prev" href="multi__spring_data_cloud_spanner.html" title="13. Spring Data Cloud Spanner"><link rel="next" href="multi__cloud_memorystore_for_redis.html" title="15. Cloud Memorystore for Redis"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">14. Spring Data Cloud Datastore</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__spring_data_cloud_spanner.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="multi__cloud_memorystore_for_redis.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_data_cloud_datastore" href="#_spring_data_cloud_datastore"></a>14. Spring Data Cloud Datastore</h1></div></div></div><p><a class="link" href="http://projects.spring.io/spring-data/" target="_top">Spring Data</a> is an abstraction for storing and retrieving POJOs in numerous storage technologies.
|
|
Spring Cloud GCP adds Spring Data support for <a class="link" href="http://cloud.google.com/datastore/" target="_top">Google Cloud Datastore</a>.</p><p>Maven coordinates for this module only, using <a class="link" href="https://github.com/spring-cloud/spring-cloud-gcp/blob/master/spring-cloud-gcp-dependencies/pom.xml" target="_top">Spring Cloud GCP BOM</a>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-gcp-data-datastore<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p>Gradle coordinates:</p><pre class="screen">dependencies {
|
|
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-data-datastore'
|
|
}</pre><p>We provide a <a class="link" href="../spring-cloud-gcp-starters/spring-cloud-gcp-starter-data-datastore" target="_top">Spring Boot Starter for Spring Data Datastore</a>, with which you can use our recommended auto-configuration setup.
|
|
To use the starter, see the coordinates below.</p><p>Maven:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-gcp-starter-data-datastore<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p>Gradle:</p><pre class="screen">dependencies {
|
|
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-datastore'
|
|
}</pre><p>This setup takes care of bringing in the latest compatible version of Cloud Java Cloud Datastore libraries as well.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_configuration_5" href="#_configuration_5"></a>14.1 Configuration</h2></div></div></div><p>To setup Spring Data Cloud Datastore, you have to configure the following:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Setup the connection details to Google Cloud Datastore.</li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_cloud_datastore_settings" href="#_cloud_datastore_settings"></a>14.1.1 Cloud Datastore settings</h3></div></div></div><p>You can the use <a class="link" href="../spring-cloud-gcp-starters/spring-cloud-gcp-starter-data-datastore" target="_top">Spring Boot Starter for Spring Data Datastore</a> to autoconfigure Google Cloud Datastore in your Spring application.
|
|
It contains all the necessary setup that makes it easy to authenticate with your Google Cloud project.
|
|
The following configuration options are available:</p><div class="informaltable"><table class="informaltable" style="border-collapse: collapse;border-top: 1px solid ; border-bottom: 1px solid ; border-left: 1px solid ; border-right: 1px solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"><col class="col_4"></colgroup><tbody><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>Name</p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>Description</p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>Required</p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>Default value</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">spring.cloud.gcp.datastore.enabled</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>Enables the Cloud Datastore client</p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>No</p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">true</code></p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">spring.cloud.gcp.datastore.project-id</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>GCP project ID where the Google Cloud Datastore API
|
|
is hosted, if different from the one in the <a class="link" href="multi_spring-cloud-gcp-core.html" title="4. Spring Cloud GCP Core">Spring Cloud GCP Core Module</a></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>No</p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"> </td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">spring.cloud.gcp.datastore.credentials.location</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>OAuth2 credentials for authenticating with the
|
|
Google Cloud Datastore API, if different from the ones in the
|
|
<a class="link" href="multi_spring-cloud-gcp-core.html" title="4. Spring Cloud GCP Core">Spring Cloud GCP Core Module</a></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>No</p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"> </td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">spring.cloud.gcp.datastore.credentials.encoded-key</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>Base64-encoded OAuth2 credentials for authenticating with the
|
|
Google Cloud Datastore API, if different from the ones in the
|
|
<a class="link" href="multi_spring-cloud-gcp-core.html" title="4. Spring Cloud GCP Core">Spring Cloud GCP Core Module</a></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>No</p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"> </td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">spring.cloud.gcp.datastore.credentials.scopes</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><a class="link" href="https://developers.google.com/identity/protocols/googlescopes" target="_top">OAuth2 scope</a> for Spring Cloud GCP
|
|
Cloud Datastore credentials</p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p>No</p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p><a class="link" href="https://www.googleapis.com/auth/datastore" target="_top">https://www.googleapis.com/auth/datastore</a></p></td></tr><tr><td style="border-right: 1px solid ; " align="left" valign="top"><p><code class="literal">spring.cloud.gcp.datastore.namespace</code></p></td><td style="border-right: 1px solid ; " align="left" valign="top"><p>The Cloud Datastore namespace to use</p></td><td style="border-right: 1px solid ; " align="left" valign="top"><p>No</p></td><td style="" align="left" valign="top"><p>the Default namespace of Cloud Datastore in your GCP project</p></td></tr></tbody></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_repository_settings_2" href="#_repository_settings_2"></a>14.1.2 Repository settings</h3></div></div></div><p>Spring Data Repositories can be configured via the <code class="literal">@EnableDatastoreRepositories</code> annotation on your main <code class="literal">@Configuration</code> class.
|
|
With our Spring Boot Starter for Spring Data Cloud Datastore, <code class="literal">@EnableDatastoreRepositories</code> is automatically added.
|
|
It is not required to add it to any other class, unless there is a need to override finer grain configuration parameters provided by <a class="link" href="https://github.com/spring-cloud/spring-cloud-gcp/blob/master/spring-cloud-gcp-data-datastore/src/main/java/org/springframework/cloud/gcp/data/datastore/repository/config/EnableDatastoreRepositories.java" target="_top"><code class="literal">@EnableDatastoreRepositories</code></a>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_autoconfiguration_2" href="#_autoconfiguration_2"></a>14.1.3 Autoconfiguration</h3></div></div></div><p>Our Spring Boot autoconfiguration creates the following beans available in the Spring application context:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">an instance of <code class="literal">DatastoreTemplate</code></li><li class="listitem">an instance of all user defined repositories extending <code class="literal">CrudRepository</code>, <code class="literal">PagingAndSortingRepository</code>, and <code class="literal">DatastoreRepository</code> (an extension of <code class="literal">PagingAndSortingRepository</code> with additional Cloud Datastore features) when repositories are enabled</li><li class="listitem">an instance of <code class="literal">Datastore</code> from the Google Cloud Java Client for Datastore, for convenience and lower level API access</li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_object_mapping_2" href="#_object_mapping_2"></a>14.2 Object Mapping</h2></div></div></div><p>Spring Data Cloud Datastore allows you to map domain POJOs to Cloud Datastore kinds and entities via annotations:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Entity(name = "traders")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Trader {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@Field(name = "trader_id")</span></em>
|
|
String traderId;
|
|
|
|
String firstName;
|
|
|
|
String lastName;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Transient</span></em>
|
|
Double temporaryNumber;
|
|
}</pre><p>Spring Data Cloud Datastore will ignore any property annotated with <code class="literal">@Transient</code>. These properties will not be written to or read from Cloud Datastore.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_constructors_2" href="#_constructors_2"></a>14.2.1 Constructors</h3></div></div></div><p>Simple constructors are supported on POJOs.
|
|
The constructor arguments can be a subset of the persistent properties.
|
|
Every constructor argument needs to have the same name and type as a persistent property on the entity and the constructor should set the property from the given argument.
|
|
Arguments that are not directly set to properties are not supported.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Entity(name = "traders")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Trader {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@Field(name = "trader_id")</span></em>
|
|
String traderId;
|
|
|
|
String firstName;
|
|
|
|
String lastName;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Transient</span></em>
|
|
Double temporaryNumber;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Trader(String traderId, String firstName) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.traderId = traderId;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.firstName = firstName;
|
|
}
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_kind" href="#_kind"></a>14.2.2 Kind</h3></div></div></div><p>The <code class="literal">@Entity</code> annotation can provide the name of the Cloud Datastore kind that stores instances of the annotated class, one per row.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_keys" href="#_keys"></a>14.2.3 Keys</h3></div></div></div><p><code class="literal">@Id</code> identifies the property corresponding to the ID value.</p><p>You must annotate one of your POJO’s fields as the ID value, because every entity in Cloud Datastore requires a single ID value:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Entity(name = "trades")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Trade {
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@Field(name = "trade_id")</span></em>
|
|
String tradeId;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Field(name = "trader_id")</span></em>
|
|
String traderId;
|
|
|
|
String action;
|
|
|
|
Double price;
|
|
|
|
Double shares;
|
|
|
|
String symbol;
|
|
}</pre><p>Datastore has automatic ID allocation.
|
|
If a POJO instance is written to Cloud Datastore with <code class="literal">null</code> as the ID value, then Spring Data Cloud Datastore will obtain a newly allocated ID value from Cloud Datastore and set that in the POJO for saving.
|
|
Because primitive <code class="literal">long</code> ID properties cannot be <code class="literal">null</code> and default to <code class="literal">0</code>, keys will not be allocated.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_fields" href="#_fields"></a>14.2.4 Fields</h3></div></div></div><p>All accessible properties on POJOs are automatically recognized as a Cloud Datastore field.
|
|
Field naming is generated by the <code class="literal">PropertyNameFieldNamingStrategy</code> by default defined on the <code class="literal">DatastoreMappingContext</code> bean.
|
|
The <code class="literal">@Field</code> annotation optionally provides a different field name than that of the property.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_supported_types_2" href="#_supported_types_2"></a>14.2.5 Supported Types</h3></div></div></div><p>Spring Data Cloud Datastore supports the following types for regular fields and elements of collections:</p><div class="informaltable"><table class="informaltable" style="border-collapse: collapse;border-top: 1px solid ; border-bottom: 1px solid ; border-left: 1px solid ; border-right: 1px solid ; "><colgroup><col class="col_1"><col class="col_2"></colgroup><thead><tr><th style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top">Type</th><th style="border-bottom: 1px solid ; " align="left" valign="top">Stored as</th></tr></thead><tbody><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">com.google.cloud.Timestamp</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.TimestampValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">com.google.cloud.datastore.Blob</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.BlobValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">com.google.cloud.datastore.LatLng</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.LatLngValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">java.lang.Boolean</code>, <code class="literal">boolean</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.BooleanValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">java.lang.Double</code>, <code class="literal">double</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.DoubleValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">java.lang.Long</code>, <code class="literal">long</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.LongValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">java.lang.Integer</code>, <code class="literal">int</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.LongValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">java.lang.String</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.StringValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">com.google.cloud.datastore.Entity</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.EntityValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">com.google.cloud.datastore.Key</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.KeyValue</p></td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">byte[]</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top"><p>com.google.cloud.datastore.BlobValue</p></td></tr><tr><td style="border-right: 1px solid ; " align="left" valign="top"><p>Java <code class="literal">enum</code> values</p></td><td style="" align="left" valign="top"><p>com.google.cloud.datastore.StringValue</p></td></tr></tbody></table></div><p>In addition, all types that can be converted to the ones listed in the table by
|
|
<code class="literal">org.springframework.core.convert.support.DefaultConversionService</code> are supported.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_types_2" href="#_custom_types_2"></a>14.2.6 Custom types</h3></div></div></div><p>Custom converters can be used extending the type support for user defined types.</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Converters need to implement the <code class="literal">org.springframework.core.convert.converter.Converter</code> interface in
|
|
both directions.</li><li class="listitem">The user defined type needs to be mapped to one of the basic types supported by Cloud Datastore.</li><li class="listitem">An instance of both Converters (read and write) needs to be passed to the <code class="literal">DatastoreCustomConversions</code> constructor,
|
|
which then has to be made available as a <code class="literal">@Bean</code> for <code class="literal">DatastoreCustomConversions</code>.</li></ol></div><p>For example:</p><p>We would like to have a field of type <code class="literal">Album</code> on our <code class="literal">Singer</code> POJO and want it to be stored as a string property:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Entity</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Singer {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
String singerId;
|
|
|
|
String name;
|
|
|
|
Album album;
|
|
}</pre><p>Where Album is a simple class:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Album {
|
|
String albumName;
|
|
|
|
LocalDate date;
|
|
}</pre><p>We have to define the two converters:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Converter to write custom Album type</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Converter<Album, String> ALBUM_STRING_CONVERTER =
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Converter<Album, String>() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String convert(Album album) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> album.getAlbumName() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" "</span> + album.getDate().format(DateTimeFormatter.ISO_DATE);
|
|
}
|
|
};
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Converters to read custom Album type</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Converter<String, Album> STRING_ALBUM_CONVERTER =
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Converter<String, Album>() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Album convert(String s) {
|
|
String[] parts = s.split(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" "</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Album(parts[<span class="hl-number">0</span>], LocalDate.parse(parts[parts.length - <span class="hl-number">1</span>], DateTimeFormatter.ISO_DATE));
|
|
}
|
|
};</pre><p>That will be configured in our <code class="literal">@Configuration</code> file:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ConverterConfiguration {
|
|
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> DatastoreCustomConversions datastoreCustomConversions() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> DatastoreCustomConversions(
|
|
Arrays.asList(
|
|
ALBUM_STRING_CONVERTER,
|
|
STRING_ALBUM_CONVERTER));
|
|
}
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_collections_and_arrays" href="#_collections_and_arrays"></a>14.2.7 Collections and arrays</h3></div></div></div><p>Arrays and collections (types that implement <code class="literal">java.util.Collection</code>) of supported types are supported.
|
|
They are stored as <code class="literal">com.google.cloud.datastore.ListValue</code>.
|
|
Elements are converted to Cloud Datastore supported types individually. <code class="literal">byte[]</code> is an exception, it is converted to
|
|
<code class="literal">com.google.cloud.datastore.Blob</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_converter_for_collections" href="#_custom_converter_for_collections"></a>14.2.8 Custom Converter for collections</h3></div></div></div><p>Users can provide converters from <code class="literal">List<?></code> to the custom collection type.
|
|
Only read converter is necessary, the Collection API is used on the write side to convert a collection to
|
|
the internal list type.</p><p>Collection converters need to implement the <code class="literal">org.springframework.core.convert.converter.Converter</code> interface.</p><p>Example:</p><p>Let’s improve the Singer class from the previous example.
|
|
Instead of a field of type <code class="literal">Album</code>,
|
|
we would like to have a field of type <code class="literal">ImmutableSet<Album></code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Entity</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Singer {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
String singerId;
|
|
|
|
String name;
|
|
|
|
ImmutableSet<Album> albums;
|
|
}</pre><p>We have to define a read converter only:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Converter<List<?>, ImmutableSet<?>> LIST_IMMUTABLE_SET_CONVERTER =
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Converter<List<?>, ImmutableSet<?>>() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ImmutableSet<?> convert(List<?> source) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ImmutableSet.copyOf(source);
|
|
}
|
|
};</pre><p>And add it to the list of custom converters:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ConverterConfiguration {
|
|
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> DatastoreCustomConversions datastoreCustomConversions() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> DatastoreCustomConversions(
|
|
Arrays.asList(
|
|
LIST_IMMUTABLE_SET_CONVERTER,
|
|
|
|
ALBUM_STRING_CONVERTER,
|
|
STRING_ALBUM_CONVERTER));
|
|
}
|
|
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_relationships_2" href="#_relationships_2"></a>14.3 Relationships</h2></div></div></div><p>There are three ways to represent relationships between entities that are described in this section:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Embedded entities stored directly in the field of the containing entity</li><li class="listitem"><code class="literal">@Descendant</code> annotated properties for one-to-many relationships</li><li class="listitem"><code class="literal">@Reference</code> annotated properties for general relationships without hierarchy</li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_embedded_entities" href="#_embedded_entities"></a>14.3.1 Embedded Entities</h3></div></div></div><p>Fields whose types are also annotated with <code class="literal">@Entity</code> are converted to <code class="literal">EntityValue</code> and stored inside the parent entity.</p><p>Here is an example of Cloud Datastore entity containing an embedded entity in JSON:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Alexander"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"age"</span> : <span class="hl-number">50</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"child"</span> : {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Philip"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>This corresponds to a simple pair of Java entities:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.gcp.data.datastore.core.mapping.Entity;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.data.annotation.Id;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Entity("parents")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Parent {
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
String name;
|
|
|
|
Child child;
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Entity</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Child {
|
|
String name;
|
|
}</pre><p><code class="literal">Child</code> entities are not stored in their own kind.
|
|
They are stored in their entirety in the <code class="literal">child</code> field of the <code class="literal">parents</code> kind.</p><p>Multiple levels of embedded entities are supported.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Embedded entities don’t need to have <code class="literal">@Id</code> field, it is only required for top level entities.</p></td></tr></table></div><p>Example:</p><p>Entities can hold embedded entities that are their own type.
|
|
We can store trees in Cloud Datastore using this feature:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.gcp.data.datastore.core.mapping.Embedded;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.gcp.data.datastore.core.mapping.Entity;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.data.annotation.Id;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Entity</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> EmbeddableTreeNode {
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> value;
|
|
|
|
EmbeddableTreeNode left;
|
|
|
|
EmbeddableTreeNode right;
|
|
|
|
Map<String, Long> longValues;
|
|
|
|
Map<String, List<Timestamp>> listTimestamps;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> EmbeddableTreeNode(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> value, EmbeddableTreeNode left, EmbeddableTreeNode right) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.value = value;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.left = left;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.right = right;
|
|
}
|
|
}</pre><p>Maps will be stored as embedded entities where the key values become the field names in the embedded entity.
|
|
The value types in these maps can be any regularly supported property type, and the key values will be converted to String using the configured converters.</p><p>Also, a collection of entities can be embedded; it will be converted to <code class="literal">ListValue</code> on write.</p><p>Example:</p><p>Instead of a binary tree from the previous example, we would like to store a general tree
|
|
(each node can have an arbitrary number of children) in Cloud Datastore.
|
|
To do that, we need to create a field of type <code class="literal">List<EmbeddableTreeNode></code>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.gcp.data.datastore.core.mapping.Embedded;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.data.annotation.Id;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> EmbeddableTreeNode {
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> value;
|
|
|
|
List<EmbeddableTreeNode> children;
|
|
|
|
Map<String, EmbeddableTreeNode> siblingNodes;
|
|
|
|
Map<String, Set<EmbeddableTreeNode>> subNodeGroups;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> EmbeddableTreeNode(List<EmbeddableTreeNode> children) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.children = children;
|
|
}
|
|
}</pre><p>Embedded objects can be stored in the values of embedded Maps.
|
|
Collections of embedded objects can also be stored as the values of embedded Maps.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_ancestor_descendant_relationships" href="#_ancestor_descendant_relationships"></a>14.3.2 Ancestor-Descendant Relationships</h3></div></div></div><p>Parent-child relationships are supported via the <code class="literal">@Descendants</code> annotation.</p><p>Unlike embedded children, descendants are fully-formed entities residing in their own kinds.
|
|
The parent entity does not have an extra field to hold the descendant entities.
|
|
Instead, the relationship is captured in the descendants' keys, which refer to their parent entities:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.gcp.data.datastore.core.mapping.Descendants;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.gcp.data.datastore.core.mapping.Entity;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.data.annotation.Id;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Entity("orders")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ShoppingOrder {
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Descendants</span></em>
|
|
List<Item> items;
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Entity("purchased_item")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Item {
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
Key purchasedItemKey;
|
|
|
|
String name;
|
|
|
|
Timestamp timeAddedToOrder;
|
|
}</pre><p>For example, an instance of a GQL key-literal representation for <code class="literal">Item</code> would also contain the parent <code class="literal">ShoppingOrder</code> ID value:</p><pre class="screen">Key(orders, '12345', purchased_item, 'eggs')</pre><p>The GQL key-literal representation for the parent <code class="literal">ShoppingOrder</code> would be:</p><pre class="screen">Key(orders, '12345')</pre><p>The Cloud Datastore entities exist separately in their own kinds.</p><p>The <code class="literal">ShoppingOrder</code>:</p><pre class="screen">{
|
|
"id" : 12345
|
|
}</pre><p>The two items inside that order:</p><pre class="screen">{
|
|
"purchasedItemKey" : Key(orders, '12345', purchased_item, 'eggs'),
|
|
"name" : "eggs",
|
|
"timeAddedToOrder" : "2014-09-27 12:30:00.45-8:00"
|
|
}
|
|
|
|
{
|
|
"purchasedItemKey" : Key(orders, '12345', purchased_item, 'sausage'),
|
|
"name" : "sausage",
|
|
"timeAddedToOrder" : "2014-09-28 11:30:00.45-9:00"
|
|
}</pre><p>The parent-child relationship structure of objects is stored in Cloud Datastore using Datastore’s <a class="link" href="https://cloud.google.com/datastore/docs/concepts/entities#ancestor_paths" target="_top">ancestor relationships</a>.
|
|
Because the relationships are defined by the Ancestor mechanism, there is no extra column needed in either the parent or child entity to store this relationship.
|
|
The relationship link is part of the descendant entity’s key value.
|
|
These relationships can be many levels deep.</p><p>Properties holding child entities must be collection-like, but they can be any of the supported inter-convertible collection-like types that are supported for regular properties such as <code class="literal">List</code>, arrays, <code class="literal">Set</code>, etc…​
|
|
Child items must have <code class="literal">Key</code> as their ID type because Cloud Datastore stores the ancestor relationship link inside the keys of the children.</p><p>Reading or saving an entity automatically causes all subsequent levels of children under that entity to be read or saved, respectively.
|
|
If a new child is created and added to a property annotated <code class="literal">@Descendants</code> and the key property is left null, then a new key will be allocated for that child.
|
|
The ordering of the retrieved children may not be the same as the ordering in the original property that was saved.</p><p>Child entities cannot be moved from the property of one parent to that of another unless the child’s key property is set to <code class="literal">null</code> or a value that contains the new parent as an ancestor.
|
|
Since Cloud Datastore entity keys can have multiple parents, it is possible that a child entity appears in the property of multiple parent entities.
|
|
Because entity keys are immutable in Cloud Datastore, to change the key of a child you must delete the existing one and re-save it with the new key.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_key_reference_relationships" href="#_key_reference_relationships"></a>14.3.3 Key Reference Relationships</h3></div></div></div><p>General relationships can be stored using the <code class="literal">@Reference</code> annotation.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.gcp.data.datastore.core.mapping.Reference;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.data.annotation.Id;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Entity</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ShoppingOrder {
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Reference</span></em>
|
|
List<Item> items;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Reference</span></em>
|
|
Item specialSingleItem;
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Entity</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Item {
|
|
<em><span class="hl-annotation" style="color: gray">@Id</span></em>
|
|
Key purchasedItemKey;
|
|
|
|
String name;
|
|
|
|
Timestamp timeAddedToOrder;
|
|
}</pre><p><code class="literal">@Reference</code> relationships are between fully-formed entities residing in their own kinds.
|
|
The relationship between <code class="literal">ShoppingOrder</code> and <code class="literal">Item</code> entities are stored as a Key field inside <code class="literal">ShoppingOrder</code>, which are resolved to the underlying Java entity type by Spring Data Cloud Datastore:</p><pre class="screen">{
|
|
"id" : 12345,
|
|
"specialSingleItem" : Key(item, "milk"),
|
|
"items" : [ Key(item, "eggs"), Key(item, "sausage") ]
|
|
}</pre><p>Reference properties can either be singular or collection-like.
|
|
These properties correspond to actual columns in the entity and Cloud Datastore Kind that hold the key values of the referenced entities.
|
|
The referenced entities are full-fledged entities of other Kinds.</p><p>Similar to the <code class="literal">@Descendants</code> relationships, reading or writing an entity will recursively read or write all of the referenced entities at all levels.
|
|
If referenced entities have <code class="literal">null</code> ID values, then they will be saved as new entities and will have ID values allocated by Cloud Datastore.
|
|
There are no requirements for relationships between the key of an entity and the keys that entity holds as references.
|
|
The order of collection-like reference properties is not preserved when reading back from Cloud Datastore.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_datastore_operations_template" href="#_datastore_operations_template"></a>14.4 Datastore Operations & Template</h2></div></div></div><p><code class="literal">DatastoreOperations</code> and its implementation, <code class="literal">DatastoreTemplate</code>, provides the Template pattern familiar to Spring developers.</p><p>Using the auto-configuration provided by Spring Boot Starter for Datastore, your Spring application context will contain a fully configured <code class="literal">DatastoreTemplate</code> object that you can autowire in your application:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> DatastoreTemplateExample {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
DatastoreTemplate datastoreTemplate;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doSomething() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.datastoreTemplate.deleteAll(Trader.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
|
|
Trader t = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Trader();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.datastoreTemplate.save(t);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
|
|
List<Trader> traders = datastoreTemplate.findAll(Trader.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
|
|
}
|
|
}</pre><p>The Template API provides convenience methods for:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Write operations (saving and deleting)</li><li class="listitem">Read-write transactions</li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_gql_query" href="#_gql_query"></a>14.4.1 GQL Query</h3></div></div></div><p>In addition to retrieving entities by their IDs, you can also submit queries.</p><pre class="programlisting"> <T> Iterable<T> query(Query<? <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> BaseEntity> query, Class<T> entityClass);
|
|
|
|
<A, T> Iterable<T> query(Query<A> query, Function<A, T> entityFunc);
|
|
|
|
Iterable<Key> queryKeys(Query<Key> query);</pre><p>These methods, respectively, allow querying for:
|
|
* entities mapped by a given entity class using all the same mapping and converting features
|
|
* arbitrary types produced by a given mapping function
|
|
* only the Cloud Datastore keys of the entities found by the query</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_find_by_ids" href="#_find_by_ids"></a>14.4.2 Find by ID(s)</h3></div></div></div><p>Datstore reading a single entity or multiple entities in a kind.</p><p>Using <code class="literal">DatastoreTemplate</code> you can execute reads, for example:</p><pre class="programlisting">Trader trader = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.datastoreTemplate.findById(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"trader1"</span>, Trader.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
|
|
|
|
List<Trader> traders = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.datastoreTemplate.findAllById(ImmutableList.of(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"trader1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"trader2"</span>), Trader.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
|
|
|
|
List<Trader> allTraders = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.datastoreTemplate.findAll(Trader.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);</pre><p>Cloud Datastore executes key-based reads with strong consistency, but queries with eventual consistency.
|
|
In the example above the first two reads utilize keys, while the third is executed using a query based on the corresponding Kind of <code class="literal">Trader</code>.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_indexes" href="#_indexes"></a>Indexes</h4></div></div></div><p>By default, all fields are indexed. To disable indexing on a particular field, <code class="literal">@Unindexed</code> annotation can be used.</p><p>Example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.gcp.data.datastore.core.mapping.Unindexed;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ExampleItem {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> indexedField;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Unindexed</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> unindexedField;
|
|
}</pre><p>When using queries directly or via Query Methods, Cloud Datastore requires <a class="link" href="https://cloud.google.com/datastore/docs/concepts/indexes" target="_top">composite custom indexes</a> if the select statement is not <code class="literal">SELECT *</code> or if there is more than one filtering condition in the <code class="literal">WHERE</code> clause.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_read_with_offsets_limits_and_sorting" href="#_read_with_offsets_limits_and_sorting"></a>Read with offsets, limits, and sorting</h4></div></div></div><p><code class="literal">DatastoreRepository</code> and custom-defined entity repositories implement the Spring Data <code class="literal">PagingAndSortingRepository</code>, which supports offsets and limits using page numbers and page sizes.
|
|
Paging and sorting options are also supported in <code class="literal">DatastoreTemplate</code> by supplying a <code class="literal">DatastoreQueryOptions</code> to <code class="literal">findAll</code>.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_partial_read_2" href="#_partial_read_2"></a>Partial read</h4></div></div></div><p>This feature is not supported yet.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_write_update_2" href="#_write_update_2"></a>14.4.3 Write / Update</h3></div></div></div><p>The write methods of <code class="literal">DatastoreOperations</code> accept a POJO and writes all of its properties to Datastore.
|
|
The required Datastore kind and entity metadata is obtained from the given object’s actual type.</p><p>If a POJO was retrieved from Datastore and its ID value was changed and then written or updated, the operation will occur as if against a row with the new ID value.
|
|
The entity with the original ID value will not be affected.</p><pre class="programlisting">Trader t = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Trader();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.datastoreTemplate.save(t);</pre><p>The <code class="literal">save</code> method behaves as update-or-insert.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_partial_update_2" href="#_partial_update_2"></a>Partial Update</h4></div></div></div><p>This feature is not supported yet.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_transactions_2" href="#_transactions_2"></a>14.4.4 Transactions</h3></div></div></div><p>Read and write transactions are provided by <code class="literal">DatastoreOperations</code> via the <code class="literal">performTransaction</code> method:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
DatastoreOperations myDatastoreOperations;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String doWorkInsideTransaction() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> myDatastoreOperations.performTransaction(
|
|
transactionDatastoreOperations -> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// work with transactionDatastoreOperations here. It is also a DatastoreOperations object.</span>
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"transaction completed"</span>;
|
|
}
|
|
);
|
|
}</pre><p>The <code class="literal">performTransaction</code> method accepts a <code class="literal">Function</code> that is provided an instance of a <code class="literal">DatastoreOperations</code> object. The final returned value and type of the function is determined by the user.
|
|
You can use this object just as you would a regular <code class="literal">DatastoreOperations</code> with an exception:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">It cannot perform sub-transactions.</li></ul></div><p>Because of Cloud Datastore’s consistency guarantees, there are <a class="link" href="https://cloud.google.com/datastore/docs/concepts/transactions#what_can_be_done_in_a_transaction" target="_top">limitations</a> to the operations and relationships among entities used inside transactions.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_declarative_transactions_with_transactional_annotation_2" href="#_declarative_transactions_with_transactional_annotation_2"></a>Declarative Transactions with @Transactional Annotation</h4></div></div></div><p>This feature requires a bean of <code class="literal">DatastoreTransactionManager</code>, which is provided when using <code class="literal">spring-cloud-gcp-starter-data-datastore</code>.</p><p><code class="literal">DatastoreTemplate</code> and <code class="literal">DatastoreRepository</code> support running methods with the <code class="literal">@Transactional</code> <a class="link" href="https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative" target="_top">annotation</a> as transactions.
|
|
If a method annotated with <code class="literal">@Transactional</code> calls another method also annotated, then both methods will work within the same transaction.
|
|
<code class="literal">performTransaction</code> cannot be used in <code class="literal">@Transactional</code> annotated methods because Cloud Datastore does not support transactions within transactions.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_read_write_support_for_maps" href="#_read_write_support_for_maps"></a>14.4.5 Read-write support for maps</h3></div></div></div><p>Objects of type <code class="literal">Map<String, ?></code> can be writen to and read from Cloud Datastore.
|
|
The map keys are used as field names for a Datastore entity and map values are converted to Datastore supported types.
|
|
Only simple types are supported (i.e. collections are not supported).
|
|
Converters for custom value types can be added (see <a class="xref" href="multi__spring_data_cloud_spanner.html#_custom_types" title="13.2.10 Custom types">Section 13.2.10, “Custom types”</a> section).</p><p>Example:</p><pre class="programlisting">Map<String, Long> map = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HashMap<>();
|
|
map.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"field1"</span>, <span class="hl-number">1L</span>);
|
|
map.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"field2"</span>, <span class="hl-number">2L</span>);
|
|
map.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"field3"</span>, <span class="hl-number">3L</span>);
|
|
|
|
keyForMap = datastoreTemplate.createKey(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"kindName"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span>);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//write a map</span>
|
|
datastoreTemplate.writeMap(keyForMap, map);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//read a map</span>
|
|
Map<String, Long> loadedMap = datastoreTemplate.findByIdAsMap(keyForMap, Long.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_repositories_2" href="#_repositories_2"></a>14.5 Repositories</h2></div></div></div><p><a class="link" href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories" target="_top">Spring Data Repositories</a> are an abstraction that can reduce boilerplate code.</p><p>For example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TraderRepository <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> DatastoreRepository<Trader, String> {
|
|
}</pre><p>Spring Data generates a working implementation of the specified interface, which can be autowired into an application.</p><p>The <code class="literal">Trader</code> type parameter to <code class="literal">DatastoreRepository</code> refers to the underlying domain type.
|
|
The second type parameter, <code class="literal">String</code> in this case, refers to the type of the key of the domain type.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyApplication {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
TraderRepository traderRepository;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> demo() {
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.traderRepository.deleteAll();
|
|
String traderId = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"demo_trader"</span>;
|
|
Trader t = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Trader();
|
|
t.traderId = traderId;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tradeRepository.save(t);
|
|
|
|
Iterable<Trader> allTraders = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.traderRepository.findAll();
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> count = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.traderRepository.count();
|
|
}
|
|
}</pre><p>Repositories allow you to define custom Query Methods (detailed in the following sections) for retrieving, counting, and deleting based on filtering and paging parameters.
|
|
Filtering parameters can be of types supported by your configured custom converters.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_query_methods_by_convention_2" href="#_query_methods_by_convention_2"></a>14.5.1 Query methods by convention</h3></div></div></div><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TradeRepository <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> DatastoreRepository<Trade, String[]> {
|
|
List<Trader> findByAction(String action);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> countByAction(String action);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> existsByAction(String action);
|
|
|
|
List<Trade> findTop3ByActionAndSymbolAndPriceGreaterThanAndPriceLessThanOrEqualOrderBySymbolDesc(
|
|
String action, String symbol, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">double</span> priceFloor, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">double</span> priceCeiling);
|
|
|
|
Page<TestEntity> findByAction(String action, Pageable pageable);
|
|
|
|
Slice<TestEntity> findBySymbol(String symbol, Pageable pageable);
|
|
|
|
List<TestEntity> findBySymbol(String symbol, Sort sort);
|
|
}</pre><p>In the example above the <a class="link" href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories.query-methods" target="_top">query methods</a> in <code class="literal">TradeRepository</code> are generated based on the name of the methods using thehttps://docs.spring.io/spring-data/data-commons/docs/current/reference/html#repositories.query-methods.query-creation[Spring Data Query creation naming convention].</p><p>Cloud Datastore only supports filter components joined by AND, and the following operations:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">equals</code></li><li class="listitem"><code class="literal">greater than or equals</code></li><li class="listitem"><code class="literal">greater than</code></li><li class="listitem"><code class="literal">less than or equals</code></li><li class="listitem"><code class="literal">less than</code></li><li class="listitem"><code class="literal">is null</code></li></ul></div><p>After writing a custom repository interface specifying just the signatures of these methods, implementations are generated for you and can be used with an auto-wired instance of the repository.
|
|
Because of Cloud Datastore’s requirement that explicitly selected fields must all appear in a composite index together, <code class="literal">find</code> name-based query methods are run as <code class="literal">SELECT *</code>.</p><p>Delete queries are also supported. For example, query methods such as <code class="literal">deleteByAction</code> or <code class="literal">removeByAction</code> delete entities found by <code class="literal">findByAction</code>.
|
|
Delete queries are executed as separate read and delete operations instead of as a single transaction because Cloud Datastore cannot query in transactions unless ancestors for queries are specified.
|
|
As a result, <code class="literal">removeBy</code> and <code class="literal">deleteBy</code> name-convention query methods cannot be used inside transactions via either <code class="literal">performInTransaction</code> or <code class="literal">@Transactional</code> annotation.</p><p>Delete queries can have the following return types:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">An integer type that is the number of entities deleted</li><li class="listitem">A collection of entities that were deleted</li><li class="listitem">'void'</li></ul></div><p>Methods can have <code class="literal">org.springframework.data.domain.Pageable</code> parameter to control pagination and sorting,
|
|
or <code class="literal">org.springframework.data.domain.Sort</code> parameter to control sorting only.
|
|
See <a class="link" href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories.query-methods" target="_top">Spring Data documentation</a> for details.</p><p>For returning multiple items in a repository method, we support Java collections as well as <code class="literal">org.springframework.data.domain.Page</code> and <code class="literal">org.springframework.data.domain.Slice</code>.
|
|
If a method’s return type is <code class="literal">org.springframework.data.domain.Page</code>, the returned object will include current page,
|
|
total number of results and total number of pages.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Methods that return <code class="literal">Page</code> execute an additional query to compute total number of pages.
|
|
Methods that return <code class="literal">Slice</code>, on the other hand, don’t execute any additional queries and therefore are much more efficient.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_gql_query_methods" href="#_custom_gql_query_methods"></a>14.5.2 Custom GQL query methods</h3></div></div></div><p>Custom GQL queries can be mapped to repository methods in one of two ways:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">namedQueries</code> properties file</li><li class="listitem">using the <code class="literal">@Query</code> annotation</li></ul></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_query_methods_with_annotation_2" href="#_query_methods_with_annotation_2"></a>Query methods with annotation</h4></div></div></div><p>Using the <code class="literal">@Query</code> annotation:</p><p>The names of the tags of the GQL correspond to the <code class="literal">@Param</code> annotated names of the method parameters.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TraderRepository <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> DatastoreRepository<Trader, String> {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Query("SELECT * FROM traders WHERE name = @trader_name")</span></em>
|
|
List<Trader> tradersByName(<em><span class="hl-annotation" style="color: gray">@Param("trader_name")</span></em> String traderName);
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Query("SELECT * FROM test_entities_ci WHERE id = @id_val")</span></em>
|
|
TestEntity getOneTestEntity(<em><span class="hl-annotation" style="color: gray">@Param("id_val")</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id);
|
|
}</pre><p>The following parameter types are supported:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">com.google.cloud.Timestamp</code></li><li class="listitem"><code class="literal">com.google.cloud.datastore.Blob</code></li><li class="listitem"><code class="literal">com.google.cloud.datastore.Key</code></li><li class="listitem"><code class="literal">com.google.cloud.datastore.Cursor</code></li><li class="listitem"><code class="literal">java.lang.Boolean</code></li><li class="listitem"><code class="literal">java.lang.Double</code></li><li class="listitem"><code class="literal">java.lang.Long</code></li><li class="listitem"><code class="literal">java.lang.String</code></li><li class="listitem"><code class="literal">enum</code> values. These are queried as <code class="literal">String</code> values.</li></ul></div><p>With the exception of <code class="literal">Cursor</code>, array forms of each of the types are also supported.</p><p>If you would like to obtain the count of items of a query or if there are any items returned by the query, set the <code class="literal">count = true</code> or <code class="literal">exists = true</code> properties of the <code class="literal">@Query</code> annotation, respectively.
|
|
The return type of the query method in these cases should be an integer type or a boolean type.</p><p>Cloud Datastore provides provides the <code class="literal">SELECT <span class="emphasis"><em>key</em></span> FROM …​</code> special column for all kinds that retrieves the <code class="literal">Key`s of each row. Selecting this special `<span class="emphasis"><em>key</em></span></code> column is especially useful and efficient for <code class="literal">count</code> and <code class="literal">exists</code> queries.</p><p>You can also query for non-entity types:</p><pre class="programlisting"> <em><span class="hl-annotation" style="color: gray">@Query(value = "SELECT __key__ from test_entities_ci")</span></em>
|
|
List<Key> getKeys();
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Query(value = "SELECT __key__ from test_entities_ci limit 1")</span></em>
|
|
Key getKey();
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Query("SELECT id FROM test_entities_ci WHERE id <= @id_val")</span></em>
|
|
List<String> getIds(<em><span class="hl-annotation" style="color: gray">@Param("id_val")</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id);
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Query("SELECT id FROM test_entities_ci WHERE id <= @id_val limit 1")</span></em>
|
|
String getOneId(<em><span class="hl-annotation" style="color: gray">@Param("id_val")</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id);</pre><p>SpEL can be used to provide GQL parameters:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Query("SELECT * FROM |com.example.Trade| WHERE trades.action = @act
|
|
AND price > :#{#priceRadius * -1} AND price < :#{#priceRadius * 2}")</span></em>
|
|
List<Trade> fetchByActionNamedQuery(<em><span class="hl-annotation" style="color: gray">@Param("act")</span></em> String action, <em><span class="hl-annotation" style="color: gray">@Param("priceRadius")</span></em> Double r);</pre><p>Kind names can be directly written in the GQL annotations.
|
|
Kind names can also be resolved from the <code class="literal">@Entity</code> annotation on domain classes.</p><p>In this case, the query should refer to table names with fully qualified class names surrounded by <code class="literal">|</code> characters: <code class="literal">|fully.qualified.ClassName|</code>.
|
|
This is useful when SpEL expressions appear in the kind name provided to the <code class="literal">@Entity</code> annotation.
|
|
For example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Query("SELECT * FROM |com.example.Trade| WHERE trades.action = @act")</span></em>
|
|
List<Trade> fetchByActionNamedQuery(<em><span class="hl-annotation" style="color: gray">@Param("act")</span></em> String action);</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_query_methods_with_named_queries_properties_2" href="#_query_methods_with_named_queries_properties_2"></a>Query methods with named queries properties</h4></div></div></div><p>You can also specify queries with Cloud Datastore parameter tags and SpEL expressions in properties files.</p><p>By default, the <code class="literal">namedQueriesLocation</code> attribute on <code class="literal">@EnableDatastoreRepositories</code> points to the <code class="literal">META-INF/datastore-named-queries.properties</code> file.
|
|
You can specify the query for a method in the properties file by providing the GQL as the value for the "interface.method" property:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">Trader.fetchByName</span>=SELECT * FROM traders WHERE name = @tag0</pre><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TraderRepository <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> DatastoreRepository<Trader, String> {
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// This method uses the query from the properties file instead of one generated based on name.</span>
|
|
List<Trader> fetchByName(<em><span class="hl-annotation" style="color: gray">@Param("tag0")</span></em> String traderName);
|
|
|
|
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_transactions_3" href="#_transactions_3"></a>14.5.3 Transactions</h3></div></div></div><p>These transactions work very similarly to those of <code class="literal">DatastoreOperations</code>, but is specific to the repository’s domain type and provides repository functions instead of template functions.</p><p>For example, this is a read-write transaction:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
DatastoreRepository myRepo;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String doWorkInsideTransaction() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> myRepo.performTransaction(
|
|
transactionDatastoreRepo -> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// work with the single-transaction transactionDatastoreRepo here. This is a DatastoreRepository object.</span>
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"transaction completed"</span>;
|
|
}
|
|
);
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_projections_2" href="#_projections_2"></a>14.5.4 Projections</h3></div></div></div><p>Spring Data Cloud Datastore supports <a class="link" href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#projections" target="_top">projections</a>. You can define projection interfaces based on domain types and add query methods that return them in your repository:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TradeProjection {
|
|
|
|
String getAction();
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Value("#{target.symbol + ' ' + target.action}")</span></em>
|
|
String getSymbolAndAction();
|
|
}
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TradeRepository <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> DatastoreRepository<Trade, Key> {
|
|
|
|
List<Trade> findByTraderId(String traderId);
|
|
|
|
List<TradeProjection> findByAction(String action);
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Query("SELECT action, symbol FROM trades WHERE action = @action")</span></em>
|
|
List<TradeProjection> findByQuery(String action);
|
|
}</pre><p>Projections can be provided by name-convention-based query methods as well as by custom GQL queries.
|
|
If using custom GQL queries, you can further restrict the fields retrieved from Cloud Datastore to just those required by the projection.
|
|
However, custom select statements (those not using <code class="literal">SELECT *</code>) require composite indexes containing the selected fields.</p><p>Properties of projection types defined using SpEL use the fixed name <code class="literal">target</code> for the underlying domain object.
|
|
As a result, accessing underlying properties take the form <code class="literal">target.<property-name></code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_rest_repositories_2" href="#_rest_repositories_2"></a>14.5.5 REST Repositories</h3></div></div></div><p>When running with Spring Boot, repositories can be exposed as REST services by simply adding this dependency to your pom file:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-boot-starter-data-rest<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p>If you prefer to configure parameters (such as path), you can use <code class="literal">@RepositoryRestResource</code> annotation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RepositoryRestResource(collectionResourceRel = "trades", path = "trades")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TradeRepository <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> DatastoreRepository<Trade, String[]> {
|
|
}</pre><p>For example, you can retrieve all <code class="literal">Trade</code> objects in the repository by using <code class="literal">curl http://<server>:<port>/trades</code>, or any specific trade via <code class="literal">curl http://<server>:<port>/trades/<trader_id></code>.</p><p>You can also write trades using <code class="literal">curl -XPOST -H"Content-Type: application/json" -<a class="link" href="mailto:d@test.json" target="_top">d@test.json</a> http://<server>:<port>/trades/</code> where the file <code class="literal">test.json</code> holds the JSON representation of a <code class="literal">Trade</code> object.</p><p>To delete trades, you can use <code class="literal">curl -XDELETE http://<server>:<port>/trades/<trader_id></code></p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_sample_10" href="#_sample_10"></a>14.6 Sample</h2></div></div></div><p>A <a class="link" href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-basic-sample" target="_top">Simple Spring Boot Application</a> and more advanced <a class="link" href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-sample" target="_top">Sample Spring Boot Application</a> are provided to show how to use the Spring Data Cloud Datastore starter and template.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__spring_data_cloud_spanner.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="multi__cloud_memorystore_for_redis.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">13. Spring Data Cloud Spanner </td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud-gcp.html">Home</a></td><td width="40%" align="right" valign="top"> 15. Cloud Memorystore for Redis</td></tr></table></div></body></html> |