Files
spring-cloud-static/spring-cloud-gcp/1.1.2.RELEASE/multi/multi__spring_data_cloud_datastore.html
2019-06-21 11:36:23 +00:00

467 lines
82 KiB
HTML

<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>14.&nbsp;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"><link rel="up" href="multi_spring-cloud-gcp.html" title="Spring Cloud GCP"><link rel="prev" href="multi__spring_data_cloud_spanner.html" title="13.&nbsp;Spring Data Cloud Spanner"><link rel="next" href="multi__cloud_memorystore_for_redis.html" title="15.&nbsp;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.&nbsp;Spring Data Cloud Datastore</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__spring_data_cloud_spanner.html">Prev</a>&nbsp;</td><th width="60%" align="center">&nbsp;</th><td width="20%" align="right">&nbsp;<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.&nbsp;Spring Data Cloud Datastore</h1></div></div></div><p><a class="link" href="https://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="https://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">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-gcp-data-datastore<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</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">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-gcp-starter-data-datastore<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</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&nbsp;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&nbsp;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.&nbsp;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">&nbsp;</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.&nbsp;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">&nbsp;</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.&nbsp;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">&nbsp;</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&nbsp;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&nbsp;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&nbsp;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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity(name = "traders")</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Field(name = "trader_id")</xslthl:annotation>
String traderId;
String firstName;
String lastName;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Transient</xslthl:annotation>
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&nbsp;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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity(name = "traders")</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Field(name = "trader_id")</xslthl:annotation>
String traderId;
String firstName;
String lastName;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Transient</xslthl:annotation>
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&nbsp;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&nbsp;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&#8217;s fields as the ID value, because every entity in Cloud Datastore requires a single ID value:</p><pre class="programlisting"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity(name = "trades")</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Field(name = "trade_id")</xslthl:annotation>
String tradeId;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Field(name = "trader_id")</xslthl:annotation>
String traderId;
String action;
Double price;
Double shares;
String symbol;
}</pre><p>Datastore can automatically allocate integer ID values.
If a POJO instance with a <code class="literal">Long</code> ID property 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&nbsp;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&nbsp;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&nbsp;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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
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&lt;Album, String&gt; ALBUM_STRING_CONVERTER =
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Converter&lt;Album, String&gt;() {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Override</xslthl:annotation>
<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&lt;String, Album&gt; STRING_ALBUM_CONVERTER =
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Converter&lt;String, Album&gt;() {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Override</xslthl:annotation>
<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[<xslthl:number xmlns:xslthl="http://xslthl.sourceforge.net/">0</xslthl:number>], LocalDate.parse(parts[parts.length - <xslthl:number xmlns:xslthl="http://xslthl.sourceforge.net/">1</xslthl:number>], DateTimeFormatter.ISO_DATE));
}
};</pre><p>That will be configured in our <code class="literal">@Configuration</code> file:</p><pre class="programlisting"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Configuration</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Bean</xslthl:annotation>
<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&nbsp;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&nbsp;Custom Converter for collections</h3></div></div></div><p>Users can provide converters from <code class="literal">List&lt;?&gt;</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&#8217;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&lt;Album&gt;</code>:</p><pre class="programlisting"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
String singerId;
String name;
ImmutableSet&lt;Album&gt; 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&lt;List&lt;?&gt;, ImmutableSet&lt;?&gt;&gt; LIST_IMMUTABLE_SET_CONVERTER =
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Converter&lt;List&lt;?&gt;, ImmutableSet&lt;?&gt;&gt;() {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Override</xslthl:annotation>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ImmutableSet&lt;?&gt; convert(List&lt;?&gt; 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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Configuration</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Bean</xslthl:annotation>
<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&nbsp;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&nbsp;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> : <xslthl:number xmlns:xslthl="http://xslthl.sourceforge.net/">47</xslthl:number><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;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity("parents")</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
String name;
Child child;
}
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity</xslthl:annotation>
<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&#8217;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;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> value;
EmbeddableTreeNode left;
EmbeddableTreeNode right;
Map&lt;String, Long&gt; longValues;
Map&lt;String, List&lt;Timestamp&gt;&gt; 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><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_maps" href="#_maps"></a>Maps</h4></div></div></div><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&lt;EmbeddableTreeNode&gt;</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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> value;
List&lt;EmbeddableTreeNode&gt; children;
Map&lt;String, EmbeddableTreeNode&gt; siblingNodes;
Map&lt;String, Set&lt;EmbeddableTreeNode&gt;&gt; subNodeGroups;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> EmbeddableTreeNode(List&lt;EmbeddableTreeNode&gt; children) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.children = children;
}
}</pre><p>Because Maps are stored as entities, they can further hold embedded entities:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Singular embedded objects in the value can be stored in the values of embedded Maps.</li><li class="listitem">Collections of embedded objects in the value can also be stored as the values of embedded Maps.</li><li class="listitem">Maps in the value are further stored as embedded entities with the same rules applied recursively for their values.</li></ul></div></div></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&nbsp;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;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity("orders")</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Descendants</xslthl:annotation>
List&lt;Item&gt; items;
}
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity("purchased_item")</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
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&#8217;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&#8217;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&#8230;&#8203;
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&#8217;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&nbsp;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;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Reference</xslthl:annotation>
List&lt;Item&gt; items;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Reference</xslthl:annotation>
Item specialSingleItem;
}
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Entity</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Id</xslthl:annotation>
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&nbsp;Datastore Operations &amp; 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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@SpringBootApplication</xslthl:annotation>
<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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Autowired</xslthl:annotation>
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&lt;Trader&gt; 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&nbsp;GQL Query</h3></div></div></div><p>In addition to retrieving entities by their IDs, you can also submit queries.</p><pre class="programlisting"> &lt;T&gt; Iterable&lt;T&gt; query(Query&lt;? <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> BaseEntity&gt; query, Class&lt;T&gt; entityClass);
&lt;A, T&gt; Iterable&lt;T&gt; query(Query&lt;A&gt; query, Function&lt;A, T&gt; entityFunc);
Iterable&lt;Key&gt; queryKeys(Query&lt;Key&gt; 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&nbsp;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&lt;Trader&gt; 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&lt;Trader&gt; 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;
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Unindexed</xslthl:annotation>
<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&nbsp;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&#8217;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&nbsp;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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Autowired</xslthl:annotation>
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 -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Work with transactionDatastoreOperations here.</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// 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&#8217;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&nbsp;Read-Write Support for Maps</h3></div></div></div><p>You can work with Maps of type <code class="literal">Map&lt;String, ?&gt;</code> instead of with entity objects by directly reading and writing them to and from Cloud Datastore.</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>This is a different situation than using entity objects that contain Map properties.</p></td></tr></table></div><p>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&nbsp;Custom types">Section&nbsp;13.2.10, &#8220;Custom types&#8221;</a> section).</p><p>Example:</p><pre class="programlisting">Map&lt;String, Long&gt; map = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HashMap&lt;&gt;();
map.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"field1"</span>, <xslthl:number xmlns:xslthl="http://xslthl.sourceforge.net/">1L</xslthl:number>);
map.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"field2"</span>, <xslthl:number xmlns:xslthl="http://xslthl.sourceforge.net/">2L</xslthl:number>);
map.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"field3"</span>, <xslthl:number xmlns:xslthl="http://xslthl.sourceforge.net/">3L</xslthl:number>);
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&lt;String, Long&gt; 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&nbsp;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&lt;Trader, String&gt; {
}</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 {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Autowired</xslthl:annotation>
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&lt;Trader&gt; 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&nbsp;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&lt;Trade, String[]&gt; {
List&lt;Trader&gt; 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&lt;Trade&gt; 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&lt;TestEntity&gt; findByAction(String action, Pageable pageable);
Slice&lt;TestEntity&gt; findBySymbol(String symbol, Pageable pageable);
List&lt;TestEntity&gt; 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&#8217;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&#8217;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&#8217;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&nbsp;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&lt;Trader, String&gt; {
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query("SELECT * FROM traders WHERE name = @trader_name")</xslthl:annotation>
List&lt;Trader&gt; tradersByName(<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Param("trader_name")</xslthl:annotation> String traderName);
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query("SELECT * FROM test_entities_ci WHERE id = @id_val")</xslthl:annotation>
TestEntity getOneTestEntity(<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Param("id_val")</xslthl:annotation> <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 &#8230;&#8203;</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"> <xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query(value = "SELECT __key__ from test_entities_ci")</xslthl:annotation>
List&lt;Key&gt; getKeys();
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query(value = "SELECT __key__ from test_entities_ci limit 1")</xslthl:annotation>
Key getKey();
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query("SELECT id FROM test_entities_ci WHERE id &lt;= @id_val")</xslthl:annotation>
List&lt;String&gt; getIds(<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Param("id_val")</xslthl:annotation> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id);
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query("SELECT id FROM test_entities_ci WHERE id &lt;= @id_val limit 1")</xslthl:annotation>
String getOneId(<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Param("id_val")</xslthl:annotation> <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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query("SELECT * FROM |com.example.Trade| WHERE trades.action = @act
AND price &gt; :#{#priceRadius * -1} AND price &lt; :#{#priceRadius * 2}")</xslthl:annotation>
List&lt;Trade&gt; fetchByActionNamedQuery(<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Param("act")</xslthl:annotation> String action, <xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Param("priceRadius")</xslthl:annotation> 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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query("SELECT * FROM |com.example.Trade| WHERE trades.action = @act")</xslthl:annotation>
List&lt;Trade&gt; fetchByActionNamedQuery(<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Param("act")</xslthl:annotation> 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&lt;Trader, String&gt; {
<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&lt;Trader&gt; fetchByName(<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Param("tag0")</xslthl:annotation> 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&nbsp;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&#8217;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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Autowired</xslthl:annotation>
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 -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Work with the single-transaction transactionDatastoreRepo here.</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// 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&nbsp;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();
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Value("#{target.symbol + ' ' + target.action}")</xslthl:annotation>
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&lt;Trade, Key&gt; {
List&lt;Trade&gt; findByTraderId(String traderId);
List&lt;TradeProjection&gt; findByAction(String action);
<xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@Query("SELECT action, symbol FROM trades WHERE action = @action")</xslthl:annotation>
List&lt;TradeProjection&gt; 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.&lt;property-name&gt;</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&nbsp;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">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-data-rest<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</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"><xslthl:annotation xmlns:xslthl="http://xslthl.sourceforge.net/">@RepositoryRestResource(collectionResourceRel = "trades", path = "trades")</xslthl:annotation>
<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&lt;Trade, String[]&gt; {
}</pre><p>For example, you can retrieve all <code class="literal">Trade</code> objects in the repository by using <code class="literal">curl http://&lt;server&gt;:&lt;port&gt;/trades</code>, or any specific trade via <code class="literal">curl http://&lt;server&gt;:&lt;port&gt;/trades/&lt;trader_id&gt;</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://&lt;server&gt;:&lt;port&gt;/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://&lt;server&gt;:&lt;port&gt;/trades/&lt;trader_id&gt;</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&nbsp;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>&nbsp;</td><td width="20%" align="center">&nbsp;</td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__cloud_memorystore_for_redis.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">13.&nbsp;Spring Data Cloud Spanner&nbsp;</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">&nbsp;15.&nbsp;Cloud Memorystore for Redis</td></tr></table></div></body></html>