Files
spring-cloud-static/spring-cloud-gcp/1.2.0.M2/reference/html/spring-cloud-gcp.html
2019-08-15 15:10:34 +00:00

8028 lines
380 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.7.1">
<meta name="author" content="João André Martins, Jisha Abubaker, Ray Tsang, Mike Eltsufin, Artem Bilan, Andreas Berger, Balint Pato, Chengyuan Zhao, Dmitry Solomakha, Elena Felder, Daniel Zou">
<title>Spring Cloud GCP</title>
<link rel="stylesheet" href="css/spring.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
.hidden {
display: none;
}
.switch {
border-width: 1px 1px 0 1px;
border-style: solid;
border-color: #7a2518;
display: inline-block;
}
.switch--item {
padding: 10px;
background-color: #ffffff;
color: #7a2518;
display: inline-block;
cursor: pointer;
}
.switch--item:not(:first-child) {
border-width: 0 0 0 1px;
border-style: solid;
border-color: #7a2518;
}
.switch--item.selected {
background-color: #7a2519;
color: #ffffff;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
<script type="text/javascript">
function addBlockSwitches() {
$('.primary').each(function() {
primary = $(this);
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
primary.children('.title').remove();
});
$('.secondary').each(function(idx, node) {
secondary = $(node);
primary = findPrimary(secondary);
switchItem = createSwitchItem(secondary, primary.children('.switch'));
switchItem.content.addClass('hidden');
findPrimary(secondary).append(switchItem.content);
secondary.remove();
});
}
function createBlockSwitch(primary) {
blockSwitch = $('<div class="switch"></div>');
primary.prepend(blockSwitch);
return blockSwitch;
}
function findPrimary(secondary) {
candidate = secondary.prev();
while (!candidate.is('.primary')) {
candidate = candidate.prev();
}
return candidate;
}
function createSwitchItem(block, blockSwitch) {
blockName = block.children('.title').text();
content = block.children('.content').first().append(block.next('.colist'));
item = $('<div class="switch--item">' + blockName + '</div>');
item.on('click', '', content, function(e) {
$(this).addClass('selected');
$(this).siblings().removeClass('selected');
e.data.siblings('.content').addClass('hidden');
e.data.removeClass('hidden');
});
blockSwitch.append(item);
return {'item': item, 'content': content};
}
$(addBlockSwitches);
</script>
</head>
<body id="spring-cloud-gcp-reference" class="book toc2 toc-left">
<div id="header">
<h1>Spring Cloud GCP</h1>
<div class="details">
<span id="author" class="author">João André Martins</span><br>
<span id="author2" class="author">Jisha Abubaker</span><br>
<span id="author3" class="author">Ray Tsang</span><br>
<span id="author4" class="author">Mike Eltsufin</span><br>
<span id="author5" class="author">Artem Bilan</span><br>
<span id="author6" class="author">Andreas Berger</span><br>
<span id="author7" class="author">Balint Pato</span><br>
<span id="author8" class="author">Chengyuan Zhao</span><br>
<span id="author9" class="author">Dmitry Solomakha</span><br>
<span id="author10" class="author">Elena Felder</span><br>
<span id="author11" class="author">Daniel Zou</span><br>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_introduction">1. Introduction</a></li>
<li><a href="#_getting_started">2. Getting Started</a>
<ul class="sectlevel2">
<li><a href="#_setting_up_dependencies">2.1. Setting up Dependencies</a></li>
<li><a href="#_learning_spring_cloud_gcp">2.2. Learning Spring Cloud GCP</a></li>
</ul>
</li>
<li><a href="#spring-cloud-gcp-core">3. Spring Cloud GCP Core</a>
<ul class="sectlevel2">
<li><a href="#_project_id">3.1. Project ID</a></li>
<li><a href="#_credentials">3.2. Credentials</a></li>
<li><a href="#_environment">3.3. Environment</a></li>
<li><a href="#_spring_initializr_2">3.4. Spring Initializr</a></li>
</ul>
</li>
<li><a href="#_google_cloud_pubsub">4. Google Cloud Pub/Sub</a>
<ul class="sectlevel2">
<li><a href="#_sample">4.1. Sample</a></li>
<li><a href="#_pubsub_operations_template">4.2. Pub/Sub Operations &amp; Template</a></li>
<li><a href="#_reactive_stream_subscription">4.3. Reactive Stream Subscription</a></li>
<li><a href="#_pubsub_management">4.4. Pub/Sub management</a></li>
<li><a href="#pubsub-configuration">4.5. Configuration</a></li>
</ul>
</li>
<li><a href="#_spring_resources">5. Spring Resources</a>
<ul class="sectlevel2">
<li><a href="#_google_cloud_storage">5.1. Google Cloud Storage</a></li>
<li><a href="#_configuration">5.2. Configuration</a></li>
<li><a href="#_sample_2">5.3. Sample</a></li>
</ul>
</li>
<li><a href="#_spring_jdbc">6. Spring JDBC</a>
<ul class="sectlevel2">
<li><a href="#_prerequisites">6.1. Prerequisites</a></li>
<li><a href="#_spring_boot_starter_for_google_cloud_sql">6.2. Spring Boot Starter for Google Cloud SQL</a></li>
<li><a href="#_samples">6.3. Samples</a></li>
</ul>
</li>
<li><a href="#_spring_integration">7. Spring Integration</a>
<ul class="sectlevel2">
<li><a href="#_channel_adapters_for_cloud_pubsub">7.1. Channel Adapters for Cloud Pub/Sub</a></li>
<li><a href="#_sample_3">7.2. Sample</a></li>
<li><a href="#_channel_adapters_for_google_cloud_storage">7.3. Channel Adapters for Google Cloud Storage</a></li>
<li><a href="#_sample_4">7.4. Sample</a></li>
</ul>
</li>
<li><a href="#_spring_cloud_stream">8. Spring Cloud Stream</a>
<ul class="sectlevel2">
<li><a href="#_overview">8.1. Overview</a></li>
<li><a href="#_configuration_2">8.2. Configuration</a></li>
<li><a href="#_streaming_vs_polled_input">8.3. Streaming vs. Polled Input</a></li>
<li><a href="#_sample_5">8.4. Sample</a></li>
</ul>
</li>
<li><a href="#_spring_cloud_bus">9. Spring Cloud Bus</a>
<ul class="sectlevel2">
<li><a href="#_configuration_management_with_spring_cloud_config_and_spring_cloud_bus">9.1. Configuration Management with Spring Cloud Config and Spring Cloud Bus</a></li>
</ul>
</li>
<li><a href="#_spring_cloud_sleuth">10. Spring Cloud Sleuth</a>
<ul class="sectlevel2">
<li><a href="#_tracing">10.1. Tracing</a></li>
<li><a href="#_spring_boot_starter_for_stackdriver_trace">10.2. Spring Boot Starter for Stackdriver Trace</a></li>
<li><a href="#_overriding_the_auto_configuration">10.3. Overriding the auto-configuration</a></li>
<li><a href="#_customizing_spans">10.4. Customizing spans</a></li>
<li><a href="#_integration_with_logging">10.5. Integration with Logging</a></li>
<li><a href="#_sample_6">10.6. Sample</a></li>
</ul>
</li>
<li><a href="#_stackdriver_logging">11. Stackdriver Logging</a>
<ul class="sectlevel2">
<li><a href="#_web_mvc_interceptor">11.1. Web MVC Interceptor</a></li>
<li><a href="#_logback_support">11.2. Logback Support</a></li>
<li><a href="#_sample_7">11.3. Sample</a></li>
</ul>
</li>
<li><a href="#_spring_cloud_config">12. Spring Cloud Config</a>
<ul class="sectlevel2">
<li><a href="#_configuration_3">12.1. Configuration</a></li>
<li><a href="#_quick_start">12.2. Quick start</a></li>
<li><a href="#_refreshing_the_configuration_at_runtime">12.3. Refreshing the configuration at runtime</a></li>
<li><a href="#_sample_8">12.4. Sample</a></li>
</ul>
</li>
<li><a href="#_spring_data_cloud_spanner">13. Spring Data Cloud Spanner</a>
<ul class="sectlevel2">
<li><a href="#_configuration_4">13.1. Configuration</a></li>
<li><a href="#_object_mapping">13.2. Object Mapping</a></li>
<li><a href="#_spanner_operations_template">13.3. Spanner Operations &amp; Template</a></li>
<li><a href="#_repositories">13.4. Repositories</a></li>
<li><a href="#_query_methods">13.5. Query Methods</a></li>
<li><a href="#_database_and_schema_admin">13.6. Database and Schema Admin</a></li>
<li><a href="#_events">13.7. Events</a></li>
<li><a href="#_auditing">13.8. Auditing</a></li>
<li><a href="#_multi_instance_usage">13.9. Multi-Instance Usage</a></li>
<li><a href="#_sample_9">13.10. Sample</a></li>
</ul>
</li>
<li><a href="#_spring_data_cloud_datastore">14. Spring Data Cloud Datastore</a>
<ul class="sectlevel2">
<li><a href="#_configuration_5">14.1. Configuration</a></li>
<li><a href="#_object_mapping_2">14.2. Object Mapping</a></li>
<li><a href="#_relationships_2">14.3. Relationships</a></li>
<li><a href="#_datastore_operations_template">14.4. Datastore Operations &amp; Template</a></li>
<li><a href="#_repositories_2">14.5. Repositories</a></li>
<li><a href="#_events_2">14.6. Events</a></li>
<li><a href="#_auditing_2">14.7. Auditing</a></li>
<li><a href="#_partitioning_data_by_namespace">14.8. Partitioning Data by Namespace</a></li>
<li><a href="#_spring_boot_actuator_support">14.9. Spring Boot Actuator Support</a></li>
<li><a href="#_sample_10">14.10. Sample</a></li>
</ul>
</li>
<li><a href="#_cloud_firestore">15. Cloud Firestore</a>
<ul class="sectlevel2">
<li><a href="#_using_cloud_firestore">15.1. Using Cloud Firestore</a></li>
<li><a href="#_configuration_6">15.2. Configuration</a></li>
<li><a href="#_sample_11">15.3. Sample</a></li>
</ul>
</li>
<li><a href="#_cloud_memorystore_for_redis">16. Cloud Memorystore for Redis</a>
<ul class="sectlevel2">
<li><a href="#_spring_caching">16.1. Spring Caching</a></li>
</ul>
</li>
<li><a href="#_cloud_identity_aware_proxy_iap_authentication">17. Cloud Identity-Aware Proxy (IAP) Authentication</a>
<ul class="sectlevel2">
<li><a href="#_configuration_7">17.1. Configuration</a></li>
<li><a href="#_sample_12">17.2. Sample</a></li>
</ul>
</li>
<li><a href="#_google_cloud_vision">18. Google Cloud Vision</a>
<ul class="sectlevel2">
<li><a href="#_dependency_setup">18.1. Dependency Setup</a></li>
<li><a href="#_image_analysis">18.2. Image Analysis</a></li>
<li><a href="#_document_ocr_template">18.3. Document OCR Template</a></li>
<li><a href="#_configuration_8">18.4. Configuration</a></li>
<li><a href="#_sample_13">18.5. Sample</a></li>
</ul>
</li>
<li><a href="#_cloud_foundry">19. Cloud Foundry</a></li>
<li><a href="#_kotlin_support">20. Kotlin Support</a>
<ul class="sectlevel2">
<li><a href="#_prerequisites_2">20.1. Prerequisites</a></li>
<li><a href="#_sample_14">20.2. Sample</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div class="sect1">
<h2 id="_introduction"><a class="link" href="#_introduction">1. Introduction</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The Spring Cloud GCP project makes the Spring Framework a first-class citizen of Google Cloud Platform (GCP).</p>
</div>
<div class="paragraph">
<p>Spring Cloud GCP lets you leverage the power and simplicity of the Spring Framework to:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Publish and subscribe to Google Cloud Pub/Sub topics</p>
</li>
<li>
<p>Configure Spring JDBC with a few properties to use Google Cloud SQL</p>
</li>
<li>
<p>Map objects, relationships, and collections with Spring Data Cloud Spanner and Spring Data Cloud Datastore</p>
</li>
<li>
<p>Write and read from Spring Resources backed up by Google Cloud Storage</p>
</li>
<li>
<p>Exchange messages with Spring Integration using Google Cloud Pub/Sub on the background</p>
</li>
<li>
<p>Trace the execution of your app with Spring Cloud Sleuth and Google Stackdriver Trace</p>
</li>
<li>
<p>Configure your app with Spring Cloud Config, backed up by the Google Runtime Configuration API</p>
</li>
<li>
<p>Consume and produce Google Cloud Storage data via Spring Integration GCS Channel Adapters</p>
</li>
<li>
<p>Use Spring Security via Google Cloud IAP</p>
</li>
<li>
<p>Analyze your images for text, objects, and other content with Google Cloud Vision</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_getting_started"><a class="link" href="#_getting_started">2. Getting Started</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section describes how to get up to speed with Spring Cloud GCP libraries.</p>
</div>
<div class="sect2">
<h3 id="_setting_up_dependencies"><a class="link" href="#_setting_up_dependencies">2.1. Setting up Dependencies</a></h3>
<div class="paragraph">
<p>All Spring Cloud GCP artifacts are made available through Maven Central.
The following resources are provided to help you setup the libraries for your project:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Maven Bill of Materials for dependency management</p>
</li>
<li>
<p>Starter Dependencies for depending on Spring Cloud GCP modules</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You may also consult our <a href="https://github.com/spring-cloud/spring-cloud-gcp">Github project</a> to examine the code or build directly from source.</p>
</div>
<div class="sect3">
<h4 id="_bill_of_materials"><a class="link" href="#_bill_of_materials">2.1.1. Bill of Materials</a></h4>
<div class="paragraph">
<p>The Spring Cloud GCP Bill of Materials (BOM) contains the versions of all the dependencies it uses.</p>
</div>
<div class="paragraph">
<p>If youre a Maven user, adding the following to your pom.xml file will allow you omit any Spring Cloud GCP dependency version numbers from your configuration.
Instead, the version of the BOM youre using determines the versions of the used dependencies.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-dependencies&lt;/artifactId&gt;
&lt;version&gt;1.2.0.M2&lt;/version&gt;
&lt;type&gt;pom&lt;/type&gt;
&lt;scope&gt;import&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/dependencyManagement&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>See the <a href="#README.adoc">sections</a> in the README for selecting an available version and Maven repository.</p>
</div>
<div class="paragraph">
<p>In the following sections, it will be assumed you are using the Spring Cloud GCP BOM and the dependency snippets will not contain versions.</p>
</div>
<div class="paragraph">
<p>Gradle users can achieve the same kind of BOM experience using Spring&#8217;s <a href="https://github.com/spring-gradle-plugins/dependency-management-plugin">dependency-management-plugin</a> Gradle plugin.
For simplicity, the Gradle dependency snippets in the remainder of this document will also omit their versions.</p>
</div>
</div>
<div class="sect3">
<h4 id="_starter_dependencies"><a class="link" href="#_starter_dependencies">2.1.2. Starter Dependencies</a></h4>
<div class="paragraph">
<p>Spring Cloud GCP offers <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-starters">starter dependencies</a> through Maven to easily depend on different modules of the library.
Each starter contains all the dependencies and transitive dependencies needed to begin using their corresponding Spring Cloud GCP module.</p>
</div>
<div class="paragraph">
<p>For example, if you wish to write a Spring application with Cloud Pub/Sub, you would include the <code>spring-cloud-gcp-starter-pubsub</code> dependency in your project.
You do <strong>not</strong> need to include the underlying <code>spring-cloud-gcp-pubsub</code> dependency, because the <code>starter</code> dependency includes it.</p>
</div>
<div class="paragraph">
<p>A summary of these artifacts are provided below.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Spring Cloud GCP Starter</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Maven Artifact Name</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Core</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Automatically configure authentication and Google project settings</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#spring-cloud-gcp-core">org.springframework.cloud:spring-cloud-gcp-starter</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Spanner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provides integrations with Google Cloud Spanner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_spring_data_cloud_spanner">org.springframework.cloud:spring-cloud-gcp-starter-data-spanner</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Datastore</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provides integrations with Google Cloud Datastore</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_spring_data_cloud_datastore">org.springframework.cloud:spring-cloud-gcp-starter-data-datastore</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Pub/Sub</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provides integrations with Google Cloud Pub/Sub</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_google_cloud_pubsub">org.springframework.cloud:spring-cloud-gcp-starter-pubsub</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Logging</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables Stackdriver Logging</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_stackdriver_logging">org.springframework.cloud:spring-cloud-gcp-starter-logging</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">SQL - MySQL</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud SQL integrations with MySQL</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_spring_jdbc">org.springframework.cloud:spring-cloud-gcp-starter-sql-mysql</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">SQL - PostgreSQL</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud SQL integrations with PostgreSQL</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_spring_jdbc">org.springframework.cloud:spring-cloud-gcp-starter-sql-postgresql</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Storage</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provides integrations with Google Cloud Storage and Spring Resource</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_spring_resources">org.springframework.cloud:spring-cloud-gcp-starter-storage</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Config</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables usage of Google Runtime Configuration API as a Spring Cloud Config server</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_spring_cloud_config">org.springframework.cloud:spring-cloud-gcp-starter-config</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Trace</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables instrumentation with Google Stackdriver Tracing</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_spring_cloud_sleuth">org.springframework.cloud:spring-cloud-gcp-starter-trace</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Vision</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provides integrations with Google Cloud Vision</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_google_cloud_vision">org.springframework.cloud:spring-cloud-gcp-starter-vision</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Security - IAP</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provides a security layer over applications deployed to Google Cloud</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_cloud_identity_aware_proxy_iap_authentication">org.springframework.cloud:spring-cloud-gcp-starter-security-iap</a></p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect3">
<h4 id="_spring_initializr"><a class="link" href="#_spring_initializr">2.1.3. Spring Initializr</a></h4>
<div class="paragraph">
<p><a href="https://start.spring.io/">Spring Initializr</a> is a tool which generates the scaffolding code for a new Spring Boot project.
It handles the work of generating the Maven or Gradle build file so you do not have to manually add the dependencies yourself.</p>
</div>
<div class="paragraph">
<p>Spring Initializr offers three modules from Spring Cloud GCP that you can use to generate your project.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>GCP Support</strong>: The GCP Support module contains auto-configuration support for every Spring Cloud GCP integration.
Most of the autoconfiguration code is only enabled if the required dependency is added to your project.</p>
</li>
<li>
<p><strong>GCP Messaging</strong>: Google Cloud Pub/Sub integrations work out of the box.</p>
</li>
<li>
<p><strong>GCP Storage</strong>: Google Cloud Storage integrations work out of the box.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_learning_spring_cloud_gcp"><a class="link" href="#_learning_spring_cloud_gcp">2.2. Learning Spring Cloud GCP</a></h3>
<div class="paragraph">
<p>There are a variety of resources to help you learn how to use Spring Cloud GCP libraries.</p>
</div>
<div class="sect3">
<h4 id="_sample_applications"><a class="link" href="#_sample_applications">2.2.1. Sample Applications</a></h4>
<div class="paragraph">
<p>The easiest way to learn how to use Spring Cloud GCP is to consult the <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples">sample applications on Github</a>.
Spring Cloud GCP provides sample applications which demonstrate how to use every integration in the library.
The table below highlights several samples of the most commonly used integrations in Spring Cloud GCP.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">GCP Integration</th>
<th class="tableblock halign-left valign-top">Sample Application</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Pub/Sub</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-sample">spring-cloud-gcp-pubsub-sample</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Spanner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-spanner-sample">spring-cloud-gcp-data-spanner-sample</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Datastore</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-sample">spring-cloud-gcp-data-datastore-sample</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud SQL (w/ MySQL)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-sql-mysql-sample">spring-cloud-gcp-sql-mysql-sample</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Storage</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-storage-resource-sample">spring-cloud-gcp-storage-resource-sample</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Stackdriver Logging</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample">spring-cloud-gcp-logging-sample</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Trace</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-trace-sample">spring-cloud-gcp-trace-sample</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Vision</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-vision-api-sample">spring-cloud-gcp-vision-api-sample</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Security - IAP</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-security-iap-sample">spring-cloud-gcp-security-iap-sample</a></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Each sample application demonstrates how to use Spring Cloud GCP libraries in context and how to setup the dependencies for the project.
The applications are fully functional and can be deployed to Google Cloud Platform as well.
If you are interested, you may consult guides for <a href="https://codelabs.developers.google.com/codelabs/cloud-app-engine-springboot/index.html">deploying an application to AppEngine</a> and <a href="https://codelabs.developers.google.com/codelabs/cloud-springboot-kubernetes/index.html">to Google Kubernetes Engine</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_codelabs"><a class="link" href="#_codelabs">2.2.2. Codelabs</a></h4>
<div class="paragraph">
<p>For a more hands-on approach, there are several guides and codelabs to help you get up to speed.
These guides provide step-by-step instructions for building an application using Spring Cloud GCP.</p>
</div>
<div class="paragraph">
<p>Some examples include:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://codelabs.developers.google.com/codelabs/cloud-app-engine-springboot/index.html">Deploy a Spring Boot app to App Engine</a></p>
</li>
<li>
<p><a href="https://codelabs.developers.google.com/codelabs/cloud-spring-cloud-gcp-kotlin/index.html">Build a Kotlin Spring Boot app with Cloud SQL and Cloud Pub/Sub</a></p>
</li>
<li>
<p><a href="https://codelabs.developers.google.com/codelabs/cloud-spring-datastore/index.html">Build a Spring Boot application with Datastore</a></p>
</li>
<li>
<p><a href="https://codelabs.developers.google.com/codelabs/cloud-spring-cloud-gcp-pubsub-integration/index.html">Messaging with Spring Integration and Cloud Pub/Sub</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The full collection of Spring codelabs can be found on the <a href="https://codelabs.developers.google.com/spring">Google Developer Codelabs page</a>.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="spring-cloud-gcp-core"><a class="link" href="#spring-cloud-gcp-core">3. Spring Cloud GCP Core</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Each Spring Cloud GCP module uses <code>GcpProjectIdProvider</code> and <code>CredentialsProvider</code> to get the GCP project ID and access credentials.</p>
</div>
<div class="paragraph">
<p>Spring Cloud GCP provides a Spring Boot starter to auto-configure the core components.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter'
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_project_id"><a class="link" href="#_project_id">3.1. Project ID</a></h3>
<div class="paragraph">
<p><code>GcpProjectIdProvider</code> is a functional interface that returns a GCP project ID string.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface GcpProjectIdProvider {
String getProjectId();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Spring Cloud GCP starter auto-configures a <code>GcpProjectIdProvider</code>.
If a <code>spring.cloud.gcp.project-id</code> property is specified, the provided <code>GcpProjectIdProvider</code> returns that property value.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">spring.cloud.gcp.project-id=my-gcp-project-id</code></pre>
</div>
</div>
<div class="paragraph">
<p>Otherwise, the project ID is discovered based on an
<a href="https://googlecloudplatform.github.io/google-cloud-java/google-cloud-clients/apidocs/com/google/cloud/ServiceOptions.html#getDefaultProjectId--">ordered list of rules</a>:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>The project ID specified by the <code>GOOGLE_CLOUD_PROJECT</code> environment variable</p>
</li>
<li>
<p>The Google App Engine project ID</p>
</li>
<li>
<p>The project ID specified in the JSON credentials file pointed by the <code>GOOGLE_APPLICATION_CREDENTIALS</code> environment variable</p>
</li>
<li>
<p>The Google Cloud SDK project ID</p>
</li>
<li>
<p>The Google Compute Engine project ID, from the Google Compute Engine Metadata Server</p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_credentials"><a class="link" href="#_credentials">3.2. Credentials</a></h3>
<div class="paragraph">
<p><code>CredentialsProvider</code> is a functional interface that returns the credentials to authenticate and authorize calls to Google Cloud Client Libraries.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface CredentialsProvider {
Credentials getCredentials() throws IOException;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Spring Cloud GCP starter auto-configures a <code>CredentialsProvider</code>.
It uses the <code>spring.cloud.gcp.credentials.location</code> property to locate the OAuth2 private key of a Google service account.
Keep in mind this property is a Spring Resource, so the credentials file can be obtained from a number of <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html#resources-implementations">different locations</a> such as the file system, classpath, URL, etc.
The next example specifies the credentials location property in the file system.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>spring.cloud.gcp.credentials.location=file:/usr/local/key.json</code></pre>
</div>
</div>
<div class="paragraph">
<p>Alternatively, you can set the credentials by directly specifying the <code>spring.cloud.gcp.credentials.encoded-key</code> property.
The value should be the base64-encoded account private key in JSON format.</p>
</div>
<div class="paragraph">
<p>If that credentials aren&#8217;t specified through properties, the starter tries to discover credentials from a <a href="https://github.com/GoogleCloudPlatform/google-cloud-java#authentication">number of places</a>:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Credentials file pointed to by the <code>GOOGLE_APPLICATION_CREDENTIALS</code> environment variable</p>
</li>
<li>
<p>Credentials provided by the Google Cloud SDK <code>gcloud auth application-default login</code> command</p>
</li>
<li>
<p>Google App Engine built-in credentials</p>
</li>
<li>
<p>Google Cloud Shell built-in credentials</p>
</li>
<li>
<p>Google Compute Engine built-in credentials</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>If your app is running on Google App Engine or Google Compute Engine, in most cases, you should omit the <code>spring.cloud.gcp.credentials.location</code> property and, instead, let the Spring Cloud GCP Starter get the correct credentials for those environments.
On App Engine Standard, the <a href="https://cloud.google.com/appengine/docs/standard/java/appidentity/">App Identity service account credentials</a> are used, on App Engine Flexible, the <a href="https://cloud.google.com/appengine/docs/flexible/java/service-account">Flexible service account credential</a> are used and on Google Compute Engine, the <a href="https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#using_the_compute_engine_default_service_account">Compute Engine Default Service Account</a> is used.</p>
</div>
<div class="sect3">
<h4 id="_scopes"><a class="link" href="#_scopes">3.2.1. Scopes</a></h4>
<div class="paragraph">
<p>By default, the credentials provided by the Spring Cloud GCP Starter contain scopes for every service supported by Spring Cloud GCP.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Service</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Scope</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Spanner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/spanner.admin" class="bare">https://www.googleapis.com/auth/spanner.admin</a>, <a href="https://www.googleapis.com/auth/spanner.data" class="bare">https://www.googleapis.com/auth/spanner.data</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Datastore</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/datastore" class="bare">https://www.googleapis.com/auth/datastore</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Pub/Sub</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/pubsub" class="bare">https://www.googleapis.com/auth/pubsub</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Storage (Read Only)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/devstorage.read_only" class="bare">https://www.googleapis.com/auth/devstorage.read_only</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Storage (Write/Write)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/devstorage.read_write" class="bare">https://www.googleapis.com/auth/devstorage.read_write</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Runtime Config</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/cloudruntimeconfig" class="bare">https://www.googleapis.com/auth/cloudruntimeconfig</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Trace (Append)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/trace.append" class="bare">https://www.googleapis.com/auth/trace.append</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Platform</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/cloud-platform" class="bare">https://www.googleapis.com/auth/cloud-platform</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Vision</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/cloud-vision" class="bare">https://www.googleapis.com/auth/cloud-vision</a></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>The Spring Cloud GCP starter allows you to configure a custom scope list for the provided credentials.
To do that, specify a comma-delimited list of <a href="https://developers.google.com/identity/protocols/googlescopes">Google OAuth2 scopes</a> in the <code>spring.cloud.gcp.credentials.scopes</code> property.</p>
</div>
<div class="paragraph">
<p><code>spring.cloud.gcp.credentials.scopes</code> is a comma-delimited list of <a href="https://developers.google.com/identity/protocols/googlescopes">Google OAuth2 scopes</a> for Google Cloud Platform services that the credentials returned by the provided <code>CredentialsProvider</code> support.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>spring.cloud.gcp.credentials.scopes=https://www.googleapis.com/auth/pubsub,https://www.googleapis.com/auth/sqlservice.admin</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also use <code>DEFAULT_SCOPES</code> placeholder as a scope to represent the starters default scopes, and append the additional scopes you need to add.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>spring.cloud.gcp.credentials.scopes=DEFAULT_SCOPES,https://www.googleapis.com/auth/cloud-vision</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_environment"><a class="link" href="#_environment">3.3. Environment</a></h3>
<div class="paragraph">
<p><code>GcpEnvironmentProvider</code> is a functional interface, auto-configured by the Spring Cloud GCP starter, that returns a <code>GcpEnvironment</code> enum.
The provider can help determine programmatically in which GCP environment (App Engine Flexible, App Engine Standard, Kubernetes Engine or Compute Engine) the application is deployed.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface GcpEnvironmentProvider {
GcpEnvironment getCurrentEnvironment();
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_spring_initializr_2"><a class="link" href="#_spring_initializr_2">3.4. Spring Initializr</a></h3>
<div class="paragraph">
<p>This starter is available from <a href="https://start.spring.io/">Spring Initializr</a> through the <code>GCP Support</code> entry.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_google_cloud_pubsub"><a class="link" href="#_google_cloud_pubsub">4. Google Cloud Pub/Sub</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring Cloud GCP provides an abstraction layer to publish to and subscribe from Google Cloud Pub/Sub topics and to create, list or delete Google Cloud Pub/Sub topics and subscriptions.</p>
</div>
<div class="paragraph">
<p>A Spring Boot starter is provided to auto-configure the various required Pub/Sub components.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-pubsub&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-pubsub'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This starter is also available from <a href="https://start.spring.io">Spring Initializr</a> through the <code>GCP Messaging</code> entry.</p>
</div>
<div class="sect2">
<h3 id="_sample"><a class="link" href="#_sample">4.1. Sample</a></h3>
<div class="paragraph">
<p>Sample applications for <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-sample">using the template</a> and <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-reactive-sample">using a subscription-backed reactive stream</a> are available.</p>
</div>
</div>
<div class="sect2">
<h3 id="_pubsub_operations_template"><a class="link" href="#_pubsub_operations_template">4.2. Pub/Sub Operations &amp; Template</a></h3>
<div class="paragraph">
<p><code>PubSubOperations</code> is an abstraction that allows Spring users to use Google Cloud Pub/Sub without depending on any Google Cloud Pub/Sub API semantics.
It provides the common set of operations needed to interact with Google Cloud Pub/Sub.
<code>PubSubTemplate</code> is the default implementation of <code>PubSubOperations</code> and it uses the <a href="https://github.com/GoogleCloudPlatform/google-cloud-java/tree/master/google-cloud-pubsub">Google Cloud Java Client for Pub/Sub</a> to interact with Google Cloud Pub/Sub.</p>
</div>
<div class="sect3">
<h4 id="_publishing_to_a_topic"><a class="link" href="#_publishing_to_a_topic">4.2.1. Publishing to a topic</a></h4>
<div class="paragraph">
<p><code>PubSubTemplate</code> provides asynchronous methods to publish messages to a Google Cloud Pub/Sub topic.
The <code>publish()</code> method takes in a topic name to post the message to, a payload of a generic type and, optionally, a map with the message headers.
The topic name could either be a canonical topic name within the current project, or the fully-qualified name referring to a topic in a different project using the <code>projects/&lt;project_name&gt;/topics/&lt;topic_name&gt;</code> format.</p>
</div>
<div class="paragraph">
<p>Here is an example of how to publish a message to a Google Cloud Pub/Sub topic:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Map&lt;String, String&gt; headers = Collections.singletonMap("key1", "val1");
pubSubTemplate.publish(topicName, "message", headers).get();</code></pre>
</div>
</div>
<div class="paragraph">
<p>By default, the <code>SimplePubSubMessageConverter</code> is used to convert payloads of type <code>byte[]</code>, <code>ByteString</code>, <code>ByteBuffer</code>, and <code>String</code> to Pub/Sub messages.</p>
</div>
</div>
<div class="sect3">
<h4 id="_subscribing_to_a_subscription"><a class="link" href="#_subscribing_to_a_subscription">4.2.2. Subscribing to a subscription</a></h4>
<div class="paragraph">
<p>Google Cloud Pub/Sub allows many subscriptions to be associated to the same topic.
<code>PubSubTemplate</code> allows you to listen to subscriptions via the <code>subscribe()</code> method.
When listening to a subscription, messages will be pulled from Google Cloud Pub/Sub
asynchronously and passed to a user provided message handler.</p>
</div>
<div class="sect4">
<h5 id="_example"><a class="link" href="#_example">Example</a></h5>
<div class="paragraph">
<p>Subscribe to a subscription with a message handler:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Subscriber subscriber = pubSubTemplate.subscribe(subscriptionName, (message) -&gt; {
logger.info("Message received from " + subscriptionName + " subscription: "
+ message.getPubsubMessage().getData().toStringUtf8());
message.ack();
});</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_subscribe_methods"><a class="link" href="#_subscribe_methods">Subscribe methods</a></h5>
<div class="paragraph">
<p><code>PubSubTemplate</code> provides the following subscribe methods:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 80%;">
</colgroup>
<tbody>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">subscribe(String subscription, Consumer&lt;BasicAcknowledgeablePubsubMessage&gt; messageConsumer)</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">asynchronously pulls messages and passes them to <code>messageConsumer</code></p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">subscribeAndConvert(String subscription,
Consumer&lt;ConvertedBasicAcknowledgeablePubsubMessage&lt;T&gt;&gt; messageConsumer,
Class&lt;T&gt; payloadType)</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">same as <code>pull</code>, but converts message payload to <code>payloadType</code> using the converter configured in the template</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_pulling_messages_from_a_subscription"><a class="link" href="#_pulling_messages_from_a_subscription">4.2.3. Pulling messages from a subscription</a></h4>
<div class="paragraph">
<p>Google Cloud Pub/Sub supports synchronous pulling of messages from a subscription.
This is different from subscribing to a subscription, in the sense that subscribing is an asynchronous task.</p>
</div>
<div class="sect4">
<h5 id="_example_2"><a class="link" href="#_example_2">Example</a></h5>
<div class="paragraph">
<p>Pull up to 10 messages:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">int maxMessages = 10;
boolean returnImmediately = false;
List&lt;AcknowledgeablePubsubMessage&gt; messages = pubSubTemplate.pull(subscriptionName, maxMessages,
returnImmediately);
//acknowledge the messages
pubSubTemplate.ack(messages);
messages.forEach(message -&gt; logger.info(message.getPubsubMessage().getData().toStringUtf8()));</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_pull_methods"><a class="link" href="#_pull_methods">Pull methods</a></h5>
<div class="paragraph">
<p><code>PubsubTemplate</code> provides the following pull methods:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 80%;">
</colgroup>
<tbody>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">pull(String subscription, Integer maxMessages,
Boolean returnImmediately)</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Pulls a number of messages from a subscription, allowing for the retry settings to be configured.
Any messages received by <code>pull()</code> are not automatically acknowledged. See <a href="#_acknowledging_messages">Acknowledging messages</a>.</p>
<p class="tableblock"> If <code>returnImmediately</code> is <code>true</code>, the system will respond immediately even if it there are no messages available to return in the <code>Pull</code> response. Otherwise, the system may wait (for a bounded amount of time) until at least one message is available, rather than returning no messages.</p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">pullAndAck</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Works the same as the <code>pull</code> method and, additionally, acknowledges all received messages.</p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">pullNext</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Allows for a single message to be pulled and automatically acknowledged from a subscription.</p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">pullAndConvert</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Works the same as the <code>pull</code> method and, additionally, converts the Pub/Sub binary payload to an object of the desired type, using the converter configured in the template.</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect4">
<h5 id="_acknowledging_messages"><a class="link" href="#_acknowledging_messages">Acknowledging messages</a></h5>
<div class="paragraph">
<p>There are two ways to acknowledge messages.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>To acknowledge multiple messages at once, you can use the <code>PubSubTemplate.ack()</code> method.
You can also use the <code>PubSubTemplate.nack()</code> for negatively acknowledging messages.
Using these methods for acknowledging messages in batches is more efficient than acknowledging messages individually, but they <strong>require</strong> the collection of messages to be from the same project.</p>
</li>
<li>
<p>To acknowledge messages individually you can use the <code>ack()</code> or <code>nack()</code> method on each of them (to acknowledge or negatively acknowledge, correspondingly).</p>
</li>
</ol>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
All <code>ack()</code>, <code>nack()</code>, and <code>modifyAckDeadline()</code> methods on messages as well as <code>PubSubSubscriberTemplate</code> are implemented asynchronously, returning a <code>ListenableFuture&lt;Void&gt;</code> to be able to process the asynchronous execution.
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_json_support"><a class="link" href="#_json_support">4.2.4. JSON support</a></h4>
<div class="paragraph">
<p>For serialization and deserialization of POJOs using Jackson JSON, configure a <code>PubSubMessageConverter</code> bean, and the Spring Boot starter for GCP Pub/Sub will automatically wire it into the <code>PubSubTemplate</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// Note: The ObjectMapper is used to convert Java POJOs to and from JSON.
// You will have to configure your own instance if you are unable to depend
// on the ObjectMapper provided by Spring Boot starters.
@Bean
public PubSubMessageConverter pubSubMessageConverter() {
return new JacksonPubSubMessageConverter(new ObjectMapper());
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Alternatively, you can set it directly by calling the <code>setMessageConverter()</code> method on the <code>PubSubTemplate</code>.
Other implementations of the <code>PubSubMessageConverter</code> can also be configured in the same manner.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Assuming you have the following class defined:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">static class TestUser {
String username;
String password;
public String getUsername() {
return this.username;
}
void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
void setPassword(String password) {
this.password = password;
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can serialize objects to JSON on publish automatically:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">TestUser user = new TestUser();
user.setUsername("John");
user.setPassword("password");
pubSubTemplate.publish(topicName, user);</code></pre>
</div>
</div>
<div class="paragraph">
<p>And thatt&#8217;s how you convert messages to objects on pull:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">int maxMessages = 1;
boolean returnImmediately = false;
List&lt;ConvertedAcknowledgeablePubsubMessage&lt;TestUser&gt;&gt; messages = pubSubTemplate.pullAndConvert(
subscriptionName, maxMessages, returnImmediately, TestUser.class);
ConvertedAcknowledgeablePubsubMessage&lt;TestUser&gt; message = messages.get(0);
//acknowledge the message
message.ack();
TestUser receivedTestUser = message.getPayload();</code></pre>
</div>
</div>
<div class="paragraph">
<p>Please refer to our <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-integration-pubsub-json-sample">Pub/Sub JSON Payload Sample App</a> as a reference for using this functionality.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_reactive_stream_subscription"><a class="link" href="#_reactive_stream_subscription">4.3. Reactive Stream Subscription</a></h3>
<div class="paragraph">
<p>It is also possible to acquire a reactive stream backed by a subscription.
To do so, a Project Reactor dependency (<code>io.projectreactor:reactor-core</code>) must be added to the project.
The combination of the Pub/Sub starter and the Project Reactor dependencies will then make a <code>PubSubReactiveFactory</code> bean available, which can then be used to get a <code>Publisher</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
PubSubReactiveFactory reactiveFactory;
// ...
Flux&lt;AcknowledgeablePubsubMessage&gt; flux
= reactiveFactory.createPolledFlux("exampleSubscription", 1000);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>Flux</code> then represents an infinite stream of GCP Pub/Sub messages coming in through the specified subscription.
For unlimited demand, the Pub/Sub subscription will be polled regularly, at intervals determined by <code>pollingPeriodMs</code> parameter passed in when creating the <code>Flux</code>.
For bounded demand, the <code>pollingPeriodMs</code> parameter is unused.
Instead, as many messages as possible (up to the requested number) are delivered immediately, with the remaining messages delivered as they become available.</p>
</div>
<div class="paragraph">
<p>The full range of Project Reactor operations can be applied to the stream.
For example, if you only want to fetch 5 messages, you can use <code>limitRequest</code> operation to turn the infinite stream into a finite one:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Flux&lt;AcknowledgeablePubsubMessage&gt; fiveMessageFlux = flux.limitRequest(5);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Acknowledging messages flowing through the <code>Flux</code> should be manually acknowledged.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">flux.doOnNext(AcknowledgeablePubsubMessage::ack);</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_pubsub_management"><a class="link" href="#_pubsub_management">4.4. Pub/Sub management</a></h3>
<div class="paragraph">
<p><code>PubSubAdmin</code> is the abstraction provided by Spring Cloud GCP to manage Google Cloud Pub/Sub resources.
It allows for the creation, deletion and listing of topics and subscriptions.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Generally when referring to topics, you can either use the short canonical topic name within the current project, or the fully-qualified name referring to a topic in a different project using the <code>projects/&lt;project_name&gt;/topics/&lt;topic_name&gt;</code> format.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p><code>PubSubAdmin</code> depends on <code>GcpProjectIdProvider</code> and either a <code>CredentialsProvider</code> or a <code>TopicAdminClient</code> and a <code>SubscriptionAdminClient</code>.
If given a <code>CredentialsProvider</code>, it creates a <code>TopicAdminClient</code> and a <code>SubscriptionAdminClient</code> with the Google Cloud Java Library for Pub/Sub default settings.
The Spring Boot starter for GCP Pub/Sub auto-configures a <code>PubSubAdmin</code> object using the <code>GcpProjectIdProvider</code> and the <code>CredentialsProvider</code> auto-configured by the Spring Boot GCP Core starter.</p>
</div>
<div class="sect3">
<h4 id="_creating_a_topic"><a class="link" href="#_creating_a_topic">4.4.1. Creating a topic</a></h4>
<div class="paragraph">
<p><code>PubSubAdmin</code> implements a method to create topics:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public Topic createTopic(String topicName)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here is an example of how to create a Google Cloud Pub/Sub topic:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public void newTopic() {
pubSubAdmin.createTopic("topicName");
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_deleting_a_topic"><a class="link" href="#_deleting_a_topic">4.4.2. Deleting a topic</a></h4>
<div class="paragraph">
<p><code>PubSubAdmin</code> implements a method to delete topics:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public void deleteTopic(String topicName)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here is an example of how to delete a Google Cloud Pub/Sub topic:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public void deleteTopic() {
pubSubAdmin.deleteTopic("topicName");
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_listing_topics"><a class="link" href="#_listing_topics">4.4.3. Listing topics</a></h4>
<div class="paragraph">
<p><code>PubSubAdmin</code> implements a method to list topics:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public List&lt;Topic&gt; listTopics</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here is an example of how to list every Google Cloud Pub/Sub topic name in a project:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;String&gt; topics = pubSubAdmin
.listTopics()
.stream()
.map(Topic::getName)
.collect(Collectors.toList());</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_creating_a_subscription"><a class="link" href="#_creating_a_subscription">4.4.4. Creating a subscription</a></h4>
<div class="paragraph">
<p><code>PubSubAdmin</code> implements a method to create subscriptions to existing topics:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public Subscription createSubscription(String subscriptionName, String topicName, Integer ackDeadline, String pushEndpoint)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here is an example of how to create a Google Cloud Pub/Sub subscription:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public void newSubscription() {
pubSubAdmin.createSubscription("subscriptionName", "topicName", 10, “https://my.endpoint/push”);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Alternative methods with default settings are provided for ease of use.
The default value for <code>ackDeadline</code> is 10 seconds.
If <code>pushEndpoint</code> isnt specified, the subscription uses message pulling, instead.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public Subscription createSubscription(String subscriptionName, String topicName)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public Subscription createSubscription(String subscriptionName, String topicName, Integer ackDeadline)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public Subscription createSubscription(String subscriptionName, String topicName, String pushEndpoint)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_deleting_a_subscription"><a class="link" href="#_deleting_a_subscription">4.4.5. Deleting a subscription</a></h4>
<div class="paragraph">
<p><code>PubSubAdmin</code> implements a method to delete subscriptions:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public void deleteSubscription(String subscriptionName)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here is an example of how to delete a Google Cloud Pub/Sub subscription:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public void deleteSubscription() {
pubSubAdmin.deleteSubscription("subscriptionName");
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_listing_subscriptions"><a class="link" href="#_listing_subscriptions">4.4.6. Listing subscriptions</a></h4>
<div class="paragraph">
<p><code>PubSubAdmin</code> implements a method to list subscriptions:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public List&lt;Subscription&gt; listSubscriptions()</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here is an example of how to list every subscription name in a project:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;String&gt; subscriptions = pubSubAdmin
.listSubscriptions()
.stream()
.map(Subscription::getName)
.collect(Collectors.toList());</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="pubsub-configuration"><a class="link" href="#pubsub-configuration">4.5. Configuration</a></h3>
<div class="paragraph">
<p>The Spring Boot starter for Google Cloud Pub/Sub provides the following configuration options:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables or disables Pub/Sub auto-configuration</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.project-id</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GCP project ID where the Google Cloud Pub/Sub API is hosted, if different from the one in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.credentials.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">OAuth2 credentials for authenticating with the
Google Cloud Pub/Sub API, if different from the ones in the
<a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.credentials.encoded-key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Base64-encoded contents of OAuth2 account private key for authenticating with the
Google Cloud Pub/Sub API, if different from the ones in the
<a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.credentials.scopes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://developers.google.com/identity/protocols/googlescopes">OAuth2 scope</a> for Spring Cloud GCP
Pub/Sub credentials</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/pubsub" class="bare">https://www.googleapis.com/auth/pubsub</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.keepAliveIntervalMinutes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Determines frequency of keepalive gRPC ping</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>5 minutes</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.subscriber.parallel-pull-count</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The number of pull workers</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The available number of processors</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.subscriber.max-ack-extension-period</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The maximum period a message ack deadline will be extended, in seconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.subscriber.pull-endpoint</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The endpoint for synchronous pulling messages</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">pubsub.googleapis.com:443</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].executor-threads</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of threads used by <code>Subscriber</code> instances created by <code>SubscriberFactory</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">4</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.total-timeout-seconds</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">TotalTimeout has ultimate control over how long the logic should keep trying the remote call until it gives up completely.
The higher the total timeout, the more retries can be attempted.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.initial-retry-delay-second</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">InitialRetryDelay controls the delay before the first retry.
Subsequent retries will use this value adjusted according to the RetryDelayMultiplier.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.retry-delay-multiplier</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">RetryDelayMultiplier controls the change in retry delay.
The retry delay of the previous call is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.max-retry-delay-seconds</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
can&#8217;t increase the retry delay higher than this amount.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.max-attempts</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">MaxAttempts defines the maximum number of attempts to perform.
If this value is greater than 0, and the number of attempts reaches this limit, the logic will give up retrying even if the total retry time is still lower than TotalTimeout.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.jittered</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Jitter determines if the delay time should be randomized.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.initial-rpc-timeout-seconds</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">InitialRpcTimeout controls the timeout for the initial RPC.
Subsequent calls will use this value adjusted according to the RpcTimeoutMultiplier.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.rpc-timeout-multiplier</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">RpcTimeoutMultiplier controls the change in RPC timeout.
The timeout of the previous call is multiplied by the RpcTimeoutMultiplier to calculate the timeout for the next call.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher].retry.max-rpc-timeout-seconds</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
can&#8217;t increase the RPC timeout higher than this amount.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher.batching].flow-control.max-outstanding-element-count</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of outstanding elements to keep in memory before enforcing flow control.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">unlimited</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher.batching].flow-control.max-outstanding-request-bytes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of outstanding bytes to keep in memory before enforcing flow control.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">unlimited</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.[subscriber,publisher.batching].flow-control.limit-exceeded-behavior</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The behavior when the specified limits are exceeded.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Block</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.publisher.batching.element-count-threshold</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The element count threshold to use for batching.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">unset (threshold does not apply)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.publisher.batching.request-byte-threshold</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The request byte threshold to use for batching.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">unset (threshold does not apply)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.publisher.batching.delay-threshold-seconds</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The delay threshold to use for batching.
After this amount of time has elapsed (counting from the first element added), the elements will be wrapped up in a batch and sent.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">unset (threshold does not apply)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.pubsub.publisher.batching.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables batching.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_resources"><a class="link" href="#_spring_resources">5. Spring Resources</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html">Spring Resources</a> are an abstraction for a number of low-level resources, such as file system files, classpath files, servlet context-relative files, etc.
Spring Cloud GCP adds a new resource type: a Google Cloud Storage (GCS) object.</p>
</div>
<div class="paragraph">
<p>A Spring Boot starter is provided to auto-configure the various Storage components.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-storage&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-storage'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This starter is also available from <a href="https://start.spring.io/">Spring Initializr</a> through the <code>GCP Storage</code> entry.</p>
</div>
<div class="sect2">
<h3 id="_google_cloud_storage"><a class="link" href="#_google_cloud_storage">5.1. Google Cloud Storage</a></h3>
<div class="paragraph">
<p>The Spring Resource Abstraction for Google Cloud Storage allows GCS objects to be accessed by their GCS URL using the <code>@Value</code> annotation:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Value("gs://[YOUR_GCS_BUCKET]/[GCS_FILE_NAME]")
private Resource gcsResource;</code></pre>
</div>
</div>
<div class="paragraph">
<p>&#8230;&#8203;or the Spring application context</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">SpringApplication.run(...).getResource("gs://[YOUR_GCS_BUCKET]/[GCS_FILE_NAME]");</code></pre>
</div>
</div>
<div class="paragraph">
<p>This creates a <code>Resource</code> object that can be used to read the object, among <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html#resources-resource">other possible operations</a>.</p>
</div>
<div class="paragraph">
<p>It is also possible to write to a <code>Resource</code>, although a <code>WriteableResource</code> is required.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Value("gs://[YOUR_GCS_BUCKET]/[GCS_FILE_NAME]")
private Resource gcsResource;
...
try (OutputStream os = ((WritableResource) gcsResource).getOutputStream()) {
os.write("foo".getBytes());
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>To work with the <code>Resource</code> as a Google Cloud Storage resource, cast it to <code>GoogleStorageResource</code>.</p>
</div>
<div class="paragraph">
<p>If the resource path refers to an object on Google Cloud Storage (as opposed to a bucket), then the <code>getBlob</code> method can be called to obtain a <a href="https://github.com/GoogleCloudPlatform/google-cloud-java/blob/master/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java"><code>Blob</code></a>.
This type represents a GCS file, which has associated <a href="https://cloud.google.com/storage/docs/gsutil/addlhelp/WorkingWithObjectMetadata">metadata</a>, such as content-type, that can be set.
The <code>createSignedUrl</code> method can also be used to obtain <a href="https://cloud.google.com/storage/docs/access-control/signed-urls">signed URLs</a> for GCS objects.
However, creating signed URLs requires that the resource was created using service account credentials.</p>
</div>
<div class="paragraph">
<p>The Spring Boot Starter for Google Cloud Storage auto-configures the <code>Storage</code> bean required by the <code>spring-cloud-gcp-storage</code> module, based on the <code>CredentialsProvider</code> provided by the Spring Boot GCP starter.</p>
</div>
<div class="sect3">
<h4 id="_setting_the_content_type"><a class="link" href="#_setting_the_content_type">5.1.1. Setting the Content Type</a></h4>
<div class="paragraph">
<p>You can set the content-type of Google Cloud Storage files from their corresponding <code>Resource</code> objects:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">((GoogleStorageResource)gcsResource).getBlob().toBuilder().setContentType("text/html").build().update();</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_configuration"><a class="link" href="#_configuration">5.2. Configuration</a></h3>
<div class="paragraph">
<p>The Spring Boot Starter for Google Cloud Storage provides the following configuration options:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.storage.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables the GCP storage APIs.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.storage.auto-create-files</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Creates files and buckets on Google Cloud Storage when writes are made to non-existent files</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.storage.credentials.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">OAuth2 credentials for authenticating with the Google Cloud Storage API, if different from the ones in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.storage.credentials.encoded-key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Base64-encoded contents of OAuth2 account private key for authenticating with the Google Cloud Storage API, if different from the ones in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.storage.credentials.scopes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://developers.google.com/identity/protocols/googlescopes">OAuth2 scope</a> for Spring Cloud GCP Storage credentials</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/devstorage.read_write" class="bare">https://www.googleapis.com/auth/devstorage.read_write</a></p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_sample_2"><a class="link" href="#_sample_2">5.3. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-storage-resource-sample">sample application</a> and a <a href="https://codelabs.developers.google.com/codelabs/spring-cloud-gcp-gcs/index.html">codelab</a> are available.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_jdbc"><a class="link" href="#_spring_jdbc">6. Spring JDBC</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring Cloud GCP adds integrations with
<a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/html/jdbc.html">Spring JDBC</a> so you can run your MySQL or PostgreSQL databases in Google Cloud SQL using Spring JDBC, or other libraries that depend on it like Spring Data JPA.</p>
</div>
<div class="paragraph">
<p>The Cloud SQL support is provided by Spring Cloud GCP in the form of two Spring Boot starters, one for MySQL and another one for PostgreSQL.
The role of the starters is to read configuration from properties and assume default settings so that user experience connecting to MySQL and PostgreSQL is as simple as possible.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-sql-mysql&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-sql-postgresql&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-sql-mysql'
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-sql-postgresql'
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_prerequisites"><a class="link" href="#_prerequisites">6.1. Prerequisites</a></h3>
<div class="paragraph">
<p>In order to use the Spring Boot Starters for Google Cloud SQL, the Google Cloud SQL API must be enabled in your GCP project.</p>
</div>
<div class="paragraph">
<p>To do that, go to the <a href="https://console.cloud.google.com/apis/library">API library page</a> of the Google Cloud Console, search for "Cloud SQL API", click the first result and enable the API.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
There are several similar "Cloud SQL" results.
You must access the "Google Cloud SQL API" one and enable the API from there.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_spring_boot_starter_for_google_cloud_sql"><a class="link" href="#_spring_boot_starter_for_google_cloud_sql">6.2. Spring Boot Starter for Google Cloud SQL</a></h3>
<div class="paragraph">
<p>The Spring Boot Starters for Google Cloud SQL provide an auto-configured <a href="https://docs.oracle.com/javase/7/docs/api/javax/sql/DataSource.html"><code>DataSource</code></a> object.
Coupled with Spring JDBC, it provides a
<a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/html/jdbc.html#jdbc-JdbcTemplate"><code>JdbcTemplate</code></a> object bean that allows for operations such as querying and modifying a database.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public List&lt;Map&lt;String, Object&gt;&gt; listUsers() {
return jdbcTemplate.queryForList("SELECT * FROM user;");
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can rely on
<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-connect-to-production-database">Spring Boot data source auto-configuration</a> to configure a <code>DataSource</code> bean.
In other words, properties like the SQL username, <code>spring.datasource.username</code>, and password, <code>spring.datasource.password</code> can be used.
There is also some configuration specific to Google Cloud SQL:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Property name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.sql.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables or disables Cloud SQL auto configuration</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.sql.database-name</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the database to connect to.</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.sql.instance-connection-name</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">A string containing a Google Cloud SQL instance&#8217;s project ID, region and name, each separated by a colon.
For example, <code>my-project-id:my-region:my-instance-name</code>.</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.sql.credentials.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">File system path to the Google OAuth2 credentials private key file.
Used to authenticate and authorize new connections to a Google Cloud SQL instance.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default credentials provided by the Spring GCP Boot starter</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.sql.credentials.encoded-key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Base64-encoded contents of OAuth2 account private key in JSON format.
Used to authenticate and authorize new connections to a Google Cloud SQL instance.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default credentials provided by the Spring GCP Boot starter</p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If you provide your own <code>spring.datasource.url</code>, it will be ignored, unless you disable Cloud SQL auto configuration with <code>spring.cloud.gcp.sql.enabled=false</code>.
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_datasource_creation_flow"><a class="link" href="#_datasource_creation_flow">6.2.1. <code>DataSource</code> creation flow</a></h4>
<div class="paragraph">
<p>Based on the previous properties, the Spring Boot starter for Google Cloud SQL creates a <code>CloudSqlJdbcInfoProvider</code> object which is used to obtain an instance&#8217;s JDBC URL and driver class name.
If you provide your own <code>CloudSqlJdbcInfoProvider</code> bean, it is used instead and the properties related to building the JDBC URL or driver class are ignored.</p>
</div>
<div class="paragraph">
<p>The <code>DataSourceProperties</code> object provided by Spring Boot Autoconfigure is mutated in order to use the JDBC URL and driver class names provided by <code>CloudSqlJdbcInfoProvider</code>, unless those values were provided in the properties.
It is in the <code>DataSourceProperties</code> mutation step that the credentials factory is registered in a system property to be <code>SqlCredentialFactory</code>.</p>
</div>
<div class="paragraph">
<p><code>DataSource</code> creation is delegated to
<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html">Spring Boot</a>.
You can select the type of connection pool (e.g., Tomcat, HikariCP, etc.) by <a href="https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-connect-to-production-database">adding their dependency to the classpath</a>.</p>
</div>
<div class="paragraph">
<p>Using the created <code>DataSource</code> in conjunction with Spring JDBC provides you with a fully configured and operational <code>JdbcTemplate</code> object that you can use to interact with your SQL database.
You can connect to your database with as little as a database and instance names.</p>
</div>
</div>
<div class="sect3">
<h4 id="_troubleshooting_tips"><a class="link" href="#_troubleshooting_tips">6.2.2. Troubleshooting tips</a></h4>
<div class="sect4">
<h5 id="connection-issues"><a class="link" href="#connection-issues">Connection issues</a></h5>
<div class="paragraph">
<p>If you&#8217;re not able to connect to a database and see an endless loop of <code>Connecting to Cloud SQL instance [&#8230;&#8203;] on IP [&#8230;&#8203;]</code>, it&#8217;s likely that exceptions are being thrown and logged at a level lower than your logger&#8217;s level.
This may be the case with HikariCP, if your logger is set to INFO or higher level.</p>
</div>
<div class="paragraph">
<p>To see what&#8217;s going on in the background, you should add a <code>logback.xml</code> file to your application resources folder, that looks like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;configuration&gt;
&lt;include resource="org/springframework/boot/logging/logback/base.xml"/&gt;
&lt;logger name="com.zaxxer.hikari.pool" level="DEBUG"/&gt;
&lt;/configuration&gt;</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_errors_like_c_g_cloud_sql_core_sslsocketfactory_re_throwing_cached_exception_due_to_attempt_to_refresh_instance_information_too_soon_after_error"><a class="link" href="#_errors_like_c_g_cloud_sql_core_sslsocketfactory_re_throwing_cached_exception_due_to_attempt_to_refresh_instance_information_too_soon_after_error">Errors like <code>c.g.cloud.sql.core.SslSocketFactory : Re-throwing cached exception due to attempt to refresh instance information too soon after error</code></a></h5>
<div class="paragraph">
<p>If you see a lot of errors like this in a loop and can&#8217;t connect to your database, this is usually a symptom that something isn&#8217;t right with the permissions of your credentials or the Google Cloud SQL API is not enabled.
Verify that the Google Cloud SQL API is enabled in the Cloud Console and that your service account has the <a href="https://cloud.google.com/sql/docs/mysql/project-access-control#roles">necessary IAM roles</a>.</p>
</div>
<div class="paragraph">
<p>To find out what&#8217;s causing the issue, you can enable DEBUG logging level as mentioned <a href="#connection-issues">above</a>.</p>
</div>
</div>
<div class="sect4">
<h5 id="_postgresql_java_net_socketexception_already_connected_issue"><a class="link" href="#_postgresql_java_net_socketexception_already_connected_issue">PostgreSQL: <code>java.net.SocketException: already connected</code> issue</a></h5>
<div class="paragraph">
<p>We found this exception to be common if your Maven project&#8217;s parent is <code>spring-boot</code> version <code>1.5.x</code>, or in any other circumstance that would cause the version of the <code>org.postgresql:postgresql</code> dependency to be an older one (e.g., <code>9.4.1212.jre7</code>).</p>
</div>
<div class="paragraph">
<p>To fix this, re-declare the dependency in its correct version.
For example, in Maven:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.postgresql&lt;/groupId&gt;
&lt;artifactId&gt;postgresql&lt;/artifactId&gt;
&lt;version&gt;42.1.1&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_samples"><a class="link" href="#_samples">6.3. Samples</a></h3>
<div class="paragraph">
<p>Available sample applications and codelabs:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-sql-mysql-sample">Spring Cloud GCP MySQL</a></p>
</li>
<li>
<p><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-sql-postgres-sample">Spring Cloud GCP PostgreSQL</a></p>
</li>
<li>
<p><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-jpa-sample">Spring Data JPA with Spring Cloud GCP SQL</a></p>
</li>
<li>
<p>Codelab: <a href="https://codelabs.developers.google.com/codelabs/cloud-spring-petclinic-cloudsql/index.html">Spring Pet Clinic using Cloud SQL</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_integration"><a class="link" href="#_spring_integration">7. Spring Integration</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring Cloud GCP provides Spring Integration adapters that allow your applications to use Enterprise Integration Patterns backed up by Google Cloud Platform services.</p>
</div>
<div class="sect2">
<h3 id="_channel_adapters_for_cloud_pubsub"><a class="link" href="#_channel_adapters_for_cloud_pubsub">7.1. Channel Adapters for Cloud Pub/Sub</a></h3>
<div class="paragraph">
<p>The channel adapters for Google Cloud Pub/Sub connect your Spring <a href="https://docs.spring.io/spring-integration/reference/html/messaging-channels-section.html#channel"><code>MessageChannels</code></a> to Google Cloud Pub/Sub topics and subscriptions.
This enables messaging between different processes, applications or micro-services backed up by Google Cloud Pub/Sub.</p>
</div>
<div class="paragraph">
<p>The Spring Integration Channel Adapters for Google Cloud Pub/Sub are included in the <code>spring-cloud-gcp-pubsub</code> module.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-pubsub&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.integration&lt;/groupId&gt;
&lt;artifactId&gt;spring-integration-core&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-pubsub'
compile group: 'org.springframework.integration', name: 'spring-integration-core'
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="_inbound_channel_adapter_using_pubsub_streaming_pull"><a class="link" href="#_inbound_channel_adapter_using_pubsub_streaming_pull">7.1.1. Inbound channel adapter (using Pub/Sub Streaming Pull)</a></h4>
<div class="paragraph">
<p><code>PubSubInboundChannelAdapter</code> is the inbound channel adapter for GCP Pub/Sub that listens to a GCP Pub/Sub subscription for new messages.
It converts new messages to an internal Spring <a href="https://docs.spring.io/spring-integration/reference/html/messaging-construction-chapter.html#message"><code>Message</code></a> and then sends it to the bound output channel.</p>
</div>
<div class="paragraph">
<p>Google Pub/Sub treats message payloads as byte arrays.
So, by default, the inbound channel adapter will construct the Spring <code>Message</code> with <code>byte[]</code> as the payload.
However, you can change the desired payload type by setting the <code>payloadType</code> property of the <code>PubSubInboundChannelAdapter</code>.
The <code>PubSubInboundChannelAdapter</code> delegates the conversion to the desired payload type to the <code>PubSubMessageConverter</code> configured in the <code>PubSubTemplate</code>.</p>
</div>
<div class="paragraph">
<p>To use the inbound channel adapter, a <code>PubSubInboundChannelAdapter</code> must be provided and configured on the user application side.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
public MessageChannel pubsubInputChannel() {
return new PublishSubscribeChannel();
}
@Bean
public PubSubInboundChannelAdapter messageChannelAdapter(
@Qualifier("pubsubInputChannel") MessageChannel inputChannel,
SubscriberFactory subscriberFactory) {
PubSubInboundChannelAdapter adapter =
new PubSubInboundChannelAdapter(subscriberFactory, "subscriptionName");
adapter.setOutputChannel(inputChannel);
adapter.setAckMode(AckMode.MANUAL);
return adapter;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the example, we first specify the <code>MessageChannel</code> where the adapter is going to write incoming messages to.
The <code>MessageChannel</code> implementation isn&#8217;t important here.
Depending on your use case, you might want to use a <code>MessageChannel</code> other than <code>PublishSubscribeChannel</code>.</p>
</div>
<div class="paragraph">
<p>Then, we declare a <code>PubSubInboundChannelAdapter</code> bean.
It requires the channel we just created and a <code>SubscriberFactory</code>, which creates <code>Subscriber</code> objects from the Google Cloud Java Client for Pub/Sub.
The Spring Boot starter for GCP Pub/Sub provides a configured <code>SubscriberFactory</code>.</p>
</div>
<div class="paragraph">
<p>The <code>PubSubInboundChannelAdapter</code> supports three acknowledgement modes, with <code>AckMode.AUTO</code> being the default value;</p>
</div>
<div class="paragraph">
<p>Automatic acking (<code>AckMode.AUTO</code>)</p>
</div>
<div class="paragraph">
<p>A message is acked with GCP Pub/Sub if the adapter sent it to the channel and no exceptions were thrown.
If a <code>RuntimeException</code> is thrown while the message is processed, then the message is nacked.</p>
</div>
<div class="paragraph">
<p>Automatic acking OK (<code>AckMode.AUTO_ACK</code>)</p>
</div>
<div class="paragraph">
<p>A message is acked with GCP Pub/Sub if the adapter sent it to the channel and no exceptions were thrown.
If a <code>RuntimeException</code> is thrown while the message is processed, then the message is neither acked / nor nacked.</p>
</div>
<div class="paragraph">
<p>This is useful when using the subscription&#8217;s ack deadline timeout as a retry delivery backoff mechanism.</p>
</div>
<div class="paragraph">
<p>Manually acking (<code>AckMode.MANUAL</code>)</p>
</div>
<div class="paragraph">
<p>The adapter attaches a <code>BasicAcknowledgeablePubsubMessage</code> object to the <code>Message</code> headers.
Users can extract the <code>BasicAcknowledgeablePubsubMessage</code> using the <code>GcpPubSubHeaders.ORIGINAL_MESSAGE</code> key and use it to (n)ack a message.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
@ServiceActivator(inputChannel = "pubsubInputChannel")
public MessageHandler messageReceiver() {
return message -&gt; {
LOGGER.info("Message arrived! Payload: " + new String((byte[]) message.getPayload()));
BasicAcknowledgeablePubsubMessage originalMessage =
message.getHeaders().get(GcpPubSubHeaders.ORIGINAL_MESSAGE, BasicAcknowledgeablePubsubMessage.class);
originalMessage.ack();
};
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_pollable_message_source_using_pubsub_synchronous_pull"><a class="link" href="#_pollable_message_source_using_pubsub_synchronous_pull">7.1.2. Pollable Message Source (using Pub/Sub Synchronous Pull)</a></h4>
<div class="paragraph">
<p>While <code>PubSubInboundChannelAdapter</code>, through the underlying Asynchronous Pull Pub/Sub mechanism, provides the best performance for high-volume applications that receive a steady flow of messages, it can create load balancing anomalies due to message caching.
This behavior is most obvious when publishing a large batch of small messages that take a long time to process individually.
It manifests as one subscriber taking up most messages, even if multiple subscribers are available to take on the work.
For a more detailed explanation of this scenario, see <a href="https://cloud.google.com/pubsub/docs/pull#dealing-with-large-backlogs-of-small-messages">GCP Pub/Sub documentation</a>.</p>
</div>
<div class="paragraph">
<p>In such a scenario, a <code>PubSubMessageSource</code> can help spread the load between different subscribers more evenly.</p>
</div>
<div class="paragraph">
<p>As with the Inbound Channel Adapter, the message source has a configurable acknowledgement mode, payload type, and header mapping.</p>
</div>
<div class="paragraph">
<p>The default behavior is to return from the synchronous pull operation immediately if no messages are present.
This can be overridden by using <code>setBlockOnPull()</code> method to wait for at least one message to arrive.</p>
</div>
<div class="paragraph">
<p>By default, <code>PubSubMessageSource</code> pulls from the subscription one message at a time.
To pull a batch of messages on each request, use the <code>setMaxFetchSize()</code> method to set the batch size.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
@InboundChannelAdapter(channel = "pubsubInputChannel", poller = @Poller(fixedDelay = "100"))
public MessageSource&lt;Object&gt; pubsubAdapter(PubSubTemplate pubSubTemplate) {
PubSubMessageSource messageSource = new PubSubMessageSource(pubSubTemplate, "exampleSubscription");
messageSource.setAckMode(AckMode.MANUAL);
messageSource.setPayloadType(String.class);
messageSource.setBlockOnPull(true);
messageSource.setMaxFetchSize(100);
return messageSource;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>@InboundChannelAdapter</code> annotation above ensures that the configured <code>MessageSource</code> is polled for messages, which are then available for manipulation with any Spring Integration mechanism on the <code>pubsubInputChannel</code> message channel.
For example, messages can be retrieved in a method annotated with <code>@ServiceActivator</code>, as seen below.</p>
</div>
<div class="paragraph">
<p>For additional flexibility, <code>PubSubMessageSource</code> attaches an <code>AcknowledgeablePubSubMessage</code> object to the <code>GcpPubSubHeaders.ORIGINAL_MESSAGE</code> message header.
The object can be used for manually (n)acking the message.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@ServiceActivator(inputChannel = "pubsubInputChannel")
public void messageReceiver(String payload,
@Header(GcpPubSubHeaders.ORIGINAL_MESSAGE) AcknowledgeablePubsubMessage message)
throws InterruptedException {
LOGGER.info("Message arrived by Synchronous Pull! Payload: " + payload);
message.ack();
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<code>AcknowledgeablePubSubMessage</code> objects acquired by synchronous pull are aware of their own acknowledgement IDs.
Streaming pull does not expose this information due to limitations of the underlying API, and returns <code>BasicAcknowledgeablePubsubMessage</code> objects that allow acking/nacking individual messages, but not extracting acknowledgement IDs for future processing.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_outbound_channel_adapter"><a class="link" href="#_outbound_channel_adapter">7.1.3. Outbound channel adapter</a></h4>
<div class="paragraph">
<p><code>PubSubMessageHandler</code> is the outbound channel adapter for GCP Pub/Sub that listens for new messages on a Spring <code>MessageChannel</code>.
It uses <code>PubSubTemplate</code> to post them to a GCP Pub/Sub topic.</p>
</div>
<div class="paragraph">
<p>To construct a Pub/Sub representation of the message, the outbound channel adapter needs to convert the Spring <code>Message</code> payload to a byte array representation expected by Pub/Sub.
It delegates this conversion to the <code>PubSubTemplate</code>.
To customize the conversion, you can specify a <code>PubSubMessageConverter</code> in the <code>PubSubTemplate</code> that should convert the <code>Object</code> payload and headers of the Spring <code>Message</code> to a <code>PubsubMessage</code>.</p>
</div>
<div class="paragraph">
<p>To use the outbound channel adapter, a <code>PubSubMessageHandler</code> bean must be provided and configured on the user application side.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
@ServiceActivator(inputChannel = "pubsubOutputChannel")
public MessageHandler messageSender(PubSubTemplate pubsubTemplate) {
return new PubSubMessageHandler(pubsubTemplate, "topicName");
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The provided <code>PubSubTemplate</code> contains all the necessary configuration to publish messages to a GCP Pub/Sub topic.</p>
</div>
<div class="paragraph">
<p><code>PubSubMessageHandler</code> publishes messages asynchronously by default.
A publish timeout can be configured for synchronous publishing.
If none is provided, the adapter waits indefinitely for a response.</p>
</div>
<div class="paragraph">
<p>It is possible to set user-defined callbacks for the <code>publish()</code> call in <code>PubSubMessageHandler</code> through the <code>setPublishFutureCallback()</code> method.
These are useful to process the message ID, in case of success, or the error if any was thrown.</p>
</div>
<div class="paragraph">
<p>To override the default destination you can use the <code>GcpPubSubHeaders.DESTINATION</code> header.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
private MessageChannel pubsubOutputChannel;
public void handleMessage(Message&lt;?&gt; msg) throws MessagingException {
final Message&lt;?&gt; message = MessageBuilder
.withPayload(msg.getPayload())
.setHeader(GcpPubSubHeaders.TOPIC, "customTopic").build();
pubsubOutputChannel.send(message);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>It is also possible to set an SpEL expression for the topic with the <code>setTopicExpression()</code> or <code>setTopicExpressionString()</code> methods.</p>
</div>
</div>
<div class="sect3">
<h4 id="_header_mapping"><a class="link" href="#_header_mapping">7.1.4. Header mapping</a></h4>
<div class="paragraph">
<p>These channel adapters contain header mappers that allow you to map, or filter out, headers from Spring to Google Cloud Pub/Sub messages, and vice-versa.
By default, the inbound channel adapter maps every header on the Google Cloud Pub/Sub messages to the Spring messages produced by the adapter.
The outbound channel adapter maps every header from Spring messages into Google Cloud Pub/Sub ones, except the ones added by Spring, like headers with key <code>"id"</code>, <code>"timestamp"</code> and <code>"gcp_pubsub_acknowledgement"</code>.
In the process, the outbound mapper also converts the value of the headers into string.</p>
</div>
<div class="paragraph">
<p>Each adapter declares a <code>setHeaderMapper()</code> method to let you further customize which headers you want to map from Spring to Google Cloud Pub/Sub, and vice-versa.</p>
</div>
<div class="paragraph">
<p>For example, to filter out headers <code>"foo"</code>, <code>"bar"</code> and all headers starting with the prefix "prefix_", you can use <code>setHeaderMapper()</code> along with the <code>PubSubHeaderMapper</code> implementation provided by this module.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">PubSubMessageHandler adapter = ...
...
PubSubHeaderMapper headerMapper = new PubSubHeaderMapper();
headerMapper.setOutboundHeaderPatterns("!foo", "!bar", "!prefix_*", "*");
adapter.setHeaderMapper(headerMapper);</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The order in which the patterns are declared in <code>PubSubHeaderMapper.setOutboundHeaderPatterns()</code> and <code>PubSubHeaderMapper.setInboundHeaderPatterns()</code> matters.
The first patterns have precedence over the following ones.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In the previous example, the <code>"*"</code> pattern means every header is mapped.
However, because it comes last in the list, <a href="https://docs.spring.io/spring-integration/api/org/springframework/integration/util/PatternMatchUtils.html#smartMatch-java.lang.String-java.lang.String&#8230;&#8203;-">the previous patterns take precedence</a>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_sample_3"><a class="link" href="#_sample_3">7.2. Sample</a></h3>
<div class="paragraph">
<p>Available examples:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-integration-pubsub-sample">sender and receiver sample application</a></p>
</li>
<li>
<p><a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-integration-pubsub-json-sample">JSON payloads sample application</a></p>
</li>
<li>
<p><a href="https://codelabs.developers.google.com/codelabs/cloud-spring-cloud-gcp-pubsub-integration/index.html">codelab</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_channel_adapters_for_google_cloud_storage"><a class="link" href="#_channel_adapters_for_google_cloud_storage">7.3. Channel Adapters for Google Cloud Storage</a></h3>
<div class="paragraph">
<p>The channel adapters for Google Cloud Storage allow you to read and write files to Google Cloud Storage through <code>MessageChannels</code>.</p>
</div>
<div class="paragraph">
<p>Spring Cloud GCP provides two inbound adapters, <code>GcsInboundFileSynchronizingMessageSource</code> and <code>GcsStreamingMessageSource</code>, and one outbound adapter, <code>GcsMessageHandler</code>.</p>
</div>
<div class="paragraph">
<p>The Spring Integration Channel Adapters for Google Cloud Storage are included in the <code>spring-cloud-gcp-storage</code> module.</p>
</div>
<div class="paragraph">
<p>To use the Storage portion of Spring Integration for Spring Cloud GCP, you must also provide the <code>spring-integration-file</code> dependency, since it isn&#8217;t pulled transitively.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-storage&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.integration&lt;/groupId&gt;
&lt;artifactId&gt;spring-integration-file&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-storage'
compile group: 'org.springframework.integration', name: 'spring-integration-file'
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="_inbound_channel_adapter"><a class="link" href="#_inbound_channel_adapter">7.3.1. Inbound channel adapter</a></h4>
<div class="paragraph">
<p>The Google Cloud Storage inbound channel adapter polls a Google Cloud Storage bucket for new files and sends each of them in a <code>Message</code> payload to the <code>MessageChannel</code> specified in the <code>@InboundChannelAdapter</code> annotation.
The files are temporarily stored in a folder in the local file system.</p>
</div>
<div class="paragraph">
<p>Here is an example of how to configure a Google Cloud Storage inbound channel adapter.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
@InboundChannelAdapter(channel = "new-file-channel", poller = @Poller(fixedDelay = "5000"))
public MessageSource&lt;File&gt; synchronizerAdapter(Storage gcs) {
GcsInboundFileSynchronizer synchronizer = new GcsInboundFileSynchronizer(gcs);
synchronizer.setRemoteDirectory("your-gcs-bucket");
GcsInboundFileSynchronizingMessageSource synchAdapter =
new GcsInboundFileSynchronizingMessageSource(synchronizer);
synchAdapter.setLocalDirectory(new File("local-directory"));
return synchAdapter;
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_inbound_streaming_channel_adapter"><a class="link" href="#_inbound_streaming_channel_adapter">7.3.2. Inbound streaming channel adapter</a></h4>
<div class="paragraph">
<p>The inbound streaming channel adapter is similar to the normal inbound channel adapter, except it does not require files to be stored in the file system.</p>
</div>
<div class="paragraph">
<p>Here is an example of how to configure a Google Cloud Storage inbound streaming channel adapter.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
@InboundChannelAdapter(channel = "streaming-channel", poller = @Poller(fixedDelay = "5000"))
public MessageSource&lt;InputStream&gt; streamingAdapter(Storage gcs) {
GcsStreamingMessageSource adapter =
new GcsStreamingMessageSource(new GcsRemoteFileTemplate(new GcsSessionFactory(gcs)));
adapter.setRemoteDirectory("your-gcs-bucket");
return adapter;
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_outbound_channel_adapter_2"><a class="link" href="#_outbound_channel_adapter_2">7.3.3. Outbound channel adapter</a></h4>
<div class="paragraph">
<p>The outbound channel adapter allows files to be written to Google Cloud Storage.
When it receives a <code>Message</code> containing a payload of type <code>File</code>, it writes that file to the Google Cloud Storage bucket specified in the adapter.</p>
</div>
<div class="paragraph">
<p>Here is an example of how to configure a Google Cloud Storage outbound channel adapter.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
@ServiceActivator(inputChannel = "writeFiles")
public MessageHandler outboundChannelAdapter(Storage gcs) {
GcsMessageHandler outboundChannelAdapter = new GcsMessageHandler(new GcsSessionFactory(gcs));
outboundChannelAdapter.setRemoteDirectoryExpression(new ValueExpression&lt;&gt;("your-gcs-bucket"));
return outboundChannelAdapter;
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_sample_4"><a class="link" href="#_sample_4">7.4. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-integration-storage-sample">sample application</a> is available.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_cloud_stream"><a class="link" href="#_spring_cloud_stream">8. Spring Cloud Stream</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring Cloud GCP provides a <a href="https://cloud.spring.io/spring-cloud-stream/">Spring Cloud Stream</a> binder to Google Cloud Pub/Sub.</p>
</div>
<div class="paragraph">
<p>The provided binder relies on the <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-pubsub/src/main/java/org/springframework/cloud/gcp/pubsub/integration">Spring Integration Channel Adapters for Google Cloud Pub/Sub</a>.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-pubsub-stream-binder&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-pubsub-stream-binder'
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_overview"><a class="link" href="#_overview">8.1. Overview</a></h3>
<div class="paragraph">
<p>This binder binds producers to Google Cloud Pub/Sub topics and consumers to subscriptions.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Partitioning is currently not supported by this binder.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_configuration_2"><a class="link" href="#_configuration_2">8.2. Configuration</a></h3>
<div class="paragraph">
<p>You can configure the Spring Cloud Stream Binder for Google Cloud Pub/Sub to automatically generate the underlying resources, like the Google Cloud Pub/Sub topics and subscriptions for producers and consumers.
For that, you can use the <code>spring.cloud.stream.gcp.pubsub.bindings.&lt;channelName&gt;.&lt;consumer|producer&gt;.auto-create-resources</code> property, which is turned ON by default.</p>
</div>
<div class="paragraph">
<p>Starting with version 1.1, these and other binder properties can be configured globally for all the bindings, e.g. <code>spring.cloud.stream.gcp.pubsub.default.consumer.auto-create-resources</code>.</p>
</div>
<div class="paragraph">
<p>If you are using Pub/Sub auto-configuration from the Spring Cloud GCP Pub/Sub Starter, you should refer to the <a href="#pubsub-configuration">configuration</a> section for other Pub/Sub parameters.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
To use this binder with a <a href="https://cloud.google.com/pubsub/docs/emulator">running emulator</a>, configure its host and port via <code>spring.cloud.gcp.pubsub.emulator-host</code>.
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_producer_destination_configuration"><a class="link" href="#_producer_destination_configuration">8.2.1. Producer Destination Configuration</a></h4>
<div class="paragraph">
<p>If automatic resource creation is turned ON and the topic corresponding to the destination name does not exist, it will be created.</p>
</div>
<div class="paragraph">
<p>For example, for the following configuration, a topic called <code>myEvents</code> would be created.</p>
</div>
<div class="listingblock">
<div class="title">application.properties</div>
<div class="content">
<pre class="highlightjs highlight"><code>spring.cloud.stream.bindings.events.destination=myEvents
spring.cloud.stream.gcp.pubsub.bindings.events.producer.auto-create-resources=true</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_consumer_destination_configuration"><a class="link" href="#_consumer_destination_configuration">8.2.2. Consumer Destination Configuration</a></h4>
<div class="paragraph">
<p>If automatic resource creation is turned ON and the subscription and/or the topic do not exist for a consumer, a subscription and potentially a topic will be created.
The topic name will be the same as the destination name, and the subscription name will be the destination name followed by the consumer group name.</p>
</div>
<div class="paragraph">
<p>Regardless of the <code>auto-create-resources</code> setting, if the consumer group is not specified, an anonymous one will be created with the name <code>anonymous.&lt;destinationName&gt;.&lt;randomUUID&gt;</code>.
Then when the binder shuts down, all Pub/Sub subscriptions created for anonymous consumer groups will be automatically cleaned up.</p>
</div>
<div class="paragraph">
<p>For example, for the following configuration, a topic named <code>myEvents</code> and a subscription called <code>myEvents.consumerGroup1</code> would be created.
If the consumer group is not specified, a subscription called <code>anonymous.myEvents.a6d83782-c5a3-4861-ac38-e6e2af15a7be</code> would be created and later cleaned up.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
If you are manually creating Pub/Sub subscriptions for consumers, make sure that they follow the naming convention of <code>&lt;destinationName&gt;.&lt;consumerGroup&gt;</code>.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">application.properties</div>
<div class="content">
<pre class="highlightjs highlight"><code>spring.cloud.stream.bindings.events.destination=myEvents
spring.cloud.stream.gcp.pubsub.bindings.events.consumer.auto-create-resources=true
# specify consumer group, and avoid anonymous consumer group generation
spring.cloud.stream.bindings.events.group=consumerGroup1</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_streaming_vs_polled_input"><a class="link" href="#_streaming_vs_polled_input">8.3. Streaming vs. Polled Input</a></h3>
<div class="paragraph">
<p>Many Spring Cloud Stream applications will use the built-in <code>Sink</code> binding, which triggers the <em>streaming</em> input binder creation.
Messages can then be consumed with an input handler marked by <code>@StreamListener(Sink.INPUT)</code> annotation, at whatever rate Pub/Sub sends them.</p>
</div>
<div class="paragraph">
<p>For more control over the rate of message arrival, a polled input binder can be set up by defining a custom binding interface with an <code>@Input</code>-annotated method returning <code>PollableMessageSource</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface PollableSink {
@Input("input")
PollableMessageSource input();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>PollableMessageSource</code> can then be injected and queried, as needed.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@EnableBinding(PollableSink.class)
public class SinkExample {
@Autowired
PollableMessageSource destIn;
@Bean
public ApplicationRunner singlePollRunner() {
return args -&gt; {
// This will poll only once.
// Add a loop or a scheduler to get more messages.
destIn.poll((message) -&gt; System.out.println("Message retrieved: " + message));
};
}
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_sample_5"><a class="link" href="#_sample_5">8.4. Sample</a></h3>
<div class="paragraph">
<p>Sample applications are available:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>For <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-binder-sample">streaming input</a>.</p>
</li>
<li>
<p>For <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-polling-binder-sample">polled input</a>.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_cloud_bus"><a class="link" href="#_spring_cloud_bus">9. Spring Cloud Bus</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Using <a href="https://cloud.google.com/pubsub/">Cloud Pub/Sub</a> as the <a href="https://spring.io/projects/spring-cloud-bus">Spring Cloud Bus</a> implementation is as simple as importing the <code>spring-cloud-gcp-starter-bus-pubsub</code> starter.</p>
</div>
<div class="paragraph">
<p>This starter brings in the <a href="#_spring_cloud_stream">Spring Cloud Stream binder for Cloud Pub/Sub</a>, which is used to both publish and subscribe to the bus.
If the bus topic (named <code>springCloudBus</code> by default) does not exist, the binder automatically creates it.
The binder also creates anonymous subscriptions for each project using the <code>spring-cloud-gcp-starter-bus-pubsub</code> starter.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-bus-pubsub&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-bus-pubsub'
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_configuration_management_with_spring_cloud_config_and_spring_cloud_bus"><a class="link" href="#_configuration_management_with_spring_cloud_config_and_spring_cloud_bus">9.1. Configuration Management with Spring Cloud Config and Spring Cloud Bus</a></h3>
<div class="paragraph">
<p>Spring Cloud Bus can be used to push configuration changes from a Spring Cloud Config server to the clients listening on the same bus.</p>
</div>
<div class="paragraph">
<p>To use GCP Pub/Sub as the bus implementation, both the configuration server and the configuration client need the <code>spring-cloud-gcp-starter-bus-pubsub</code> dependency.</p>
</div>
<div class="paragraph">
<p>All other configuration is standard to <a href="https://spring.io/projects/spring-cloud-config">Spring Cloud Config</a>.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="./images/spring_cloud_bus_over_pubsub.png" alt="spring cloud bus over pubsub">
</div>
</div>
<div class="paragraph">
<p>Spring Cloud Config Server typically runs on port <code>8888</code>, and can read configuration from a <a href="https://cloud.spring.io/spring-cloud-config/spring-cloud-config.html#_environment_repository">variety of source control systems</a> such as GitHub, and even from the local filesystem.
When the server is notified that new configuration is available, it fetches the updated configuration and sends a notification (<code>RefreshRemoteApplicationEvent</code>) out via Spring Cloud Bus.</p>
</div>
<div class="paragraph">
<p>When configuration is stored locally, config server polls the parent directory for changes.
With configuration stored in source control repository, such as GitHub, the config server needs to be notified that a new version of configuration is available.
In a deployed server, this would be done automatically through a GitHub webhook, but in a local testing scenario, the <code>/monitor</code> HTTP endpoint needs to be invoked manually.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>curl -X POST http://localhost:8888/monitor -H "X-Github-Event: push" -H "Content-Type: application/json" -d '{"commits": [{"modified": ["application.properties"]}]}'</pre>
</div>
</div>
<div class="paragraph">
<p>By adding the <code>spring-cloud-gcp-starter-bus-pubsub</code> dependency, you instruct Spring Cloud Bus to use Cloud Pub/Sub to broadcast configuration changes.
Spring Cloud Bus will then create a topic named <code>springCloudBus</code>, as well as a subscription for each configuration client.</p>
</div>
<div class="paragraph">
<p>The configuration server happens to also be a configuration client, subscribing to the configuration changes that it sends out.
Thus, in a scenario with one configuration server and one configuration client, two anonymous subscriptions to the <code>springCloudBus</code> topic are created.
However, a config server disables configuration refresh by default (see <a href="https://static.javadoc.io/org.springframework.cloud/spring-cloud-config-server/2.1.0.RELEASE/index.html">ConfigServerBootstrapApplicationListener</a> for more details).</p>
</div>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-bus-config-sample">demo application</a> showing configuration management and distribution over a Cloud Pub/Sub-powered bus is available.
The sample contains two examples of configuration management with Spring Cloud Bus: one monitoring a local file system, and the other retrieving configuration from a GitHub repository.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_cloud_sleuth"><a class="link" href="#_spring_cloud_sleuth">10. Spring Cloud Sleuth</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://cloud.spring.io/spring-cloud-sleuth/">Spring Cloud Sleuth</a> is an instrumentation framework for Spring Boot applications.
It captures trace information and can forward traces to services like Zipkin for storage and analysis.</p>
</div>
<div class="paragraph">
<p>Google Cloud Platform provides its own managed distributed tracing service called <a href="https://cloud.google.com/trace/">Stackdriver Trace</a>.
Instead of running and maintaining your own Zipkin instance and storage, you can use Stackdriver Trace to store traces, view trace details, generate latency distributions graphs, and generate performance regression reports.</p>
</div>
<div class="paragraph">
<p>This Spring Cloud GCP starter can forward Spring Cloud Sleuth traces to Stackdriver Trace without an intermediary Zipkin server.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-trace&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-trace'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You must enable Stackdriver Trace API from the Google Cloud Console in order to capture traces.
Navigate to the <a href="https://console.cloud.google.com/apis/api/cloudtrace.googleapis.com/overview">Stackdriver Trace API</a> for your project and make sure its enabled.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you are already using a Zipkin server capturing trace information from multiple platform/frameworks, you can also use a <a href="https://cloud.google.com/trace/docs/zipkin">Stackdriver Zipkin proxy</a> to forward those traces to Stackdriver Trace without modifying existing applications.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_tracing"><a class="link" href="#_tracing">10.1. Tracing</a></h3>
<div class="paragraph">
<p>Spring Cloud Sleuth uses the <a href="https://github.com/openzipkin/brave">Brave tracer</a> to generate traces.
This integration enables Brave to use the <a href="https://github.com/openzipkin/zipkin-gcp/tree/master/propagation-stackdriver"><code>StackdriverTracePropagation</code></a> propagation.</p>
</div>
<div class="paragraph">
<p>A propagation is responsible for extracting trace context from an entity (e.g., an HTTP servlet request) and injecting trace context into an entity.
A canonical example of the propagation usage is a web server that receives an HTTP request, which triggers other HTTP requests from the server before returning an HTTP response to the original caller.
In the case of <code>StackdriverTracePropagation</code>, first it looks for trace context in the <code>x-cloud-trace-context</code> key (e.g., an HTTP request header).
The value of the <code>x-cloud-trace-context</code> key can be formatted in three different ways:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>x-cloud-trace-context: TRACE_ID</code></p>
</li>
<li>
<p><code>x-cloud-trace-context: TRACE_ID/SPAN_ID</code></p>
</li>
<li>
<p><code>x-cloud-trace-context: TRACE_ID/SPAN_ID;o=TRACE_TRUE</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p><code>TRACE_ID</code> is a 32-character hexadecimal value that encodes a 128-bit number.</p>
</div>
<div class="paragraph">
<p><code>SPAN_ID</code> is an unsigned long.
Since Stackdriver Trace doesn&#8217;t support span joins, a new span ID is always generated, regardless of the one specified in <code>x-cloud-trace-context</code>.</p>
</div>
<div class="paragraph">
<p><code>TRACE_TRUE</code> can either be <code>0</code> if the entity should be untraced, or <code>1</code> if it should be traced.
This field forces the decision of whether or not to trace the request; if omitted then the decision is deferred to the sampler.</p>
</div>
<div class="paragraph">
<p>If a <code>x-cloud-trace-context</code> key isn&#8217;t found, <code>StackdriverTracePropagation</code> falls back to tracing with the <a href="https://github.com/openzipkin/b3-propagation">X-B3 headers</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_spring_boot_starter_for_stackdriver_trace"><a class="link" href="#_spring_boot_starter_for_stackdriver_trace">10.2. Spring Boot Starter for Stackdriver Trace</a></h3>
<div class="paragraph">
<p>Spring Boot Starter for Stackdriver Trace uses Spring Cloud Sleuth and auto-configures a <a href="https://github.com/openzipkin/zipkin-gcp/blob/master/sender-stackdriver/src/main/java/zipkin2/reporter/stackdriver/StackdriverSender.java">StackdriverSender</a> that sends the Sleuths trace information to Stackdriver Trace.</p>
</div>
<div class="paragraph">
<p>All configurations are optional:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Auto-configure Spring Cloud Sleuth to send traces to Stackdriver Trace.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.project-id</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Overrides the project ID from the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.credentials.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Overrides the credentials location from the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.credentials.encoded-key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Overrides the credentials encoded key from the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.credentials.scopes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Overrides the credentials scopes from the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.num-executor-threads</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of threads used by the Trace executor</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">4</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.authority</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">HTTP/2 authority the channel claims to be connecting to.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.compression</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the compression to use in Trace calls</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.deadline-ms</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Call deadline in milliseconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.max-inbound-size</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Maximum size for inbound messages</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.max-outbound-size</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Maximum size for outbound messages</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.wait-for-ready</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">Waits for the channel to be ready</a> in case of a transient failure</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.trace.messageTimeout</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Timeout in seconds before pending spans will be sent in batches to GCP Stackdriver Trace. (previously <code>spring.zipkin.messageTimeout</code>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>You can use core Spring Cloud Sleuth properties to control Sleuths sampling rate, etc.
Read <a href="https://cloud.spring.io/spring-cloud-sleuth/">Sleuth documentation</a> for more information on Sleuth configurations.</p>
</div>
<div class="paragraph">
<p>For example, when you are testing to see the traces are going through, you can set the sampling rate to 100%.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>spring.sleuth.sampler.probability=1 # Send 100% of the request traces to Stackdriver.
spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*) # Ignore some URL paths.</code></pre>
</div>
</div>
<div class="paragraph">
<p>Spring Cloud GCP Trace does override some Sleuth configurations:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Always uses 128-bit Trace IDs.
This is required by Stackdriver Trace.</p>
</li>
<li>
<p>Does not use Span joins.
Span joins will share the span ID between the client and server Spans.
Stackdriver requires that every Span ID within a Trace to be unique, so Span joins are not supported.</p>
</li>
<li>
<p>Uses <code>StackdriverHttpClientParser</code> and <code>StackdriverHttpServerParser</code> by default to populate Stackdriver related fields.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_overriding_the_auto_configuration"><a class="link" href="#_overriding_the_auto_configuration">10.3. Overriding the auto-configuration</a></h3>
<div class="paragraph">
<p>Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0.
In order to get this to work, every tracing system needs to have a <code>Reporter&lt;Span&gt;</code> and <code>Sender</code>.
If you want to override the provided beans you need to give them a specific name.
To do this you can use respectively <code>StackdriverTraceAutoConfiguration.REPORTER_BEAN_NAME</code> and <code>StackdriverTraceAutoConfiguration.SENDER_BEAN_NAME</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_customizing_spans"><a class="link" href="#_customizing_spans">10.4. Customizing spans</a></h3>
<div class="paragraph">
<p>You can add additional tags and annotations to spans by using the <code>brave.SpanCustomizer</code>, which is available in the application context.</p>
</div>
<div class="paragraph">
<p>Here&#8217;s an example that uses <code>WebMvcConfigurer</code> to configure an MVC interceptor that adds two extra tags to all web controller spans.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@SpringBootApplication
public class Application implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Autowired
private SpanCustomizer spanCustomizer;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
spanCustomizer.tag("session-id", request.getSession().getId());
spanCustomizer.tag("environment", "QA");
return true;
}
});
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can then search and filter traces based on these additional tags in the Stackdriver Trace service.</p>
</div>
</div>
<div class="sect2">
<h3 id="_integration_with_logging"><a class="link" href="#_integration_with_logging">10.5. Integration with Logging</a></h3>
<div class="paragraph">
<p>Integration with Stackdriver Logging is available through the <a href="logging.adoc">Stackdriver Logging Support</a>.
If the Trace integration is used together with the Logging one, the request logs will be associated to the corresponding traces.
The trace logs can be viewed by going to the <a href="https://console.cloud.google.com/traces/traces">Google Cloud Console Trace List</a>, selecting a trace and pressing the <code>Logs &#8594; View</code> link in the <code>Details</code> section.</p>
</div>
</div>
<div class="sect2">
<h3 id="_sample_6"><a class="link" href="#_sample_6">10.6. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-trace-sample">sample application</a> and a <a href="https://codelabs.developers.google.com/codelabs/cloud-spring-cloud-gcp-trace/index.html">codelab</a> are available.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_stackdriver_logging"><a class="link" href="#_stackdriver_logging">11. Stackdriver Logging</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-logging&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-logging'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="https://cloud.google.com/logging/">Stackdriver Logging</a> is the managed logging service provided by Google Cloud Platform.</p>
</div>
<div class="paragraph">
<p>This module provides support for associating a web request trace ID with the corresponding log entries.
It does so by retrieving the <code>X-B3-TraceId</code> value from the <a href="https://logback.qos.ch/manual/mdc.html">Mapped Diagnostic Context (MDC)</a>, which is set by Spring Cloud Sleuth.
If Spring Cloud Sleuth isn&#8217;t used, the configured <code>TraceIdExtractor</code> extracts the desired header value and sets it as the log entry&#8217;s trace ID.
This allows grouping of log messages by request, for example, in the <a href="https://console.cloud.google.com/logs/viewer">Google Cloud Console Logs viewer</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Due to the way logging is set up, the GCP project ID and credentials defined in <code>application.properties</code> are ignored.
Instead, you should set the <code>GOOGLE_CLOUD_PROJECT</code> and <code>GOOGLE_APPLICATION_CREDENTIALS</code> environment variables to the project ID and credentials private key location, respectively.
You can do this easily if you&#8217;re using the <a href="https://cloud.google.com/sdk">Google Cloud SDK</a>, using the <code>gcloud config set project [YOUR_PROJECT_ID]</code> and <code>gcloud auth application-default login</code> commands, respectively.
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_web_mvc_interceptor"><a class="link" href="#_web_mvc_interceptor">11.1. Web MVC Interceptor</a></h3>
<div class="paragraph">
<p>For use in Web MVC-based applications, <code>TraceIdLoggingWebMvcInterceptor</code> is provided that extracts the request trace ID from an HTTP request using a <code>TraceIdExtractor</code> and stores it in a thread-local, which can then be used in a logging appender to add the trace ID metadata to log messages.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
If Spring Cloud GCP Trace is enabled, the logging module disables itself and delegates log correlation to Spring Cloud Sleuth.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p><code>LoggingWebMvcConfigurer</code> configuration class is also provided to help register the <code>TraceIdLoggingWebMvcInterceptor</code> in Spring MVC applications.</p>
</div>
<div class="paragraph">
<p>Applications hosted on the Google Cloud Platform include trace IDs under the <code>x-cloud-trace-context</code> header, which will be included in log entries.
However, if Sleuth is used the trace ID will be picked up from the MDC.</p>
</div>
</div>
<div class="sect2">
<h3 id="_logback_support"><a class="link" href="#_logback_support">11.2. Logback Support</a></h3>
<div class="paragraph">
<p>Currently, only Logback is supported and there are 2 possibilities to log to Stackdriver via this library with Logback: via direct API calls and through JSON-formatted console logs.</p>
</div>
<div class="sect3">
<h4 id="_log_via_api"><a class="link" href="#_log_via_api">11.2.1. Log via API</a></h4>
<div class="paragraph">
<p>A Stackdriver appender is available using <code>org/springframework/cloud/gcp/logging/logback-appender.xml</code>.
This appender builds a Stackdriver Logging log entry from a JUL or Logback log entry, adds a trace ID to it and sends it to Stackdriver Logging.</p>
</div>
<div class="paragraph">
<p><code>STACKDRIVER_LOG_NAME</code> and <code>STACKDRIVER_LOG_FLUSH_LEVEL</code> environment variables can be used to customize the <code>STACKDRIVER</code> appender.</p>
</div>
<div class="paragraph">
<p>Your configuration may then look like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;configuration&gt;
&lt;include resource="org/springframework/cloud/gcp/logging/logback-appender.xml" /&gt;
&lt;root level="INFO"&gt;
&lt;appender-ref ref="STACKDRIVER" /&gt;
&lt;/root&gt;
&lt;/configuration&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you want to have more control over the log output, you can further configure the appender.
The following properties are available:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Property</th>
<th class="tableblock halign-left valign-top">Default Value</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>log</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.log</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Stackdriver Log name.
This can also be set via the <code>STACKDRIVER_LOG_NAME</code> environmental variable.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>flushLevel</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>WARN</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If a log entry with this level is encountered, trigger a flush of locally buffered log to Stackdriver Logging.
This can also be set via the <code>STACKDRIVER_LOG_FLUSH_LEVEL</code> environmental variable.</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect3">
<h4 id="_log_via_console"><a class="link" href="#_log_via_console">11.2.2. Log via Console</a></h4>
<div class="paragraph">
<p>For Logback, a <code>org/springframework/cloud/gcp/logging/logback-json-appender.xml</code> file is made available for import to make it easier to configure the JSON Logback appender.</p>
</div>
<div class="paragraph">
<p>Your configuration may then look something like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;configuration&gt;
&lt;include resource="org/springframework/cloud/gcp/logging/logback-json-appender.xml" /&gt;
&lt;root level="INFO"&gt;
&lt;appender-ref ref="CONSOLE_JSON" /&gt;
&lt;/root&gt;
&lt;/configuration&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>If your application is running on Google Kubernetes Engine, Google Compute Engine or Google App Engine Flexible, your console logging is automatically saved to Google Stackdriver Logging.
Therefore, you can just include <code>org/springframework/cloud/gcp/logging/logback-json-appender.xml</code> in your logging configuration, which logs JSON entries to the console.
The trace id will be set correctly.</p>
</div>
<div class="paragraph">
<p>If you want to have more control over the log output, you can further configure the appender.
The following properties are available:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Property</th>
<th class="tableblock halign-left valign-top">Default Value</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>projectId</code></p></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p>If not set, default value is determined in the following order:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>SPRING_CLOUD_GCP_LOGGING_PROJECT_ID</code> Environmental Variable.</p>
</li>
<li>
<p>Value of <code>DefaultGcpProjectIdProvider.getProjectId()</code></p>
</li>
</ol>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p>This is used to generate fully qualified Stackdriver Trace ID format: <code>projects/[PROJECT-ID]/traces/[TRACE-ID]</code>.</p>
</div>
<div class="paragraph">
<p>This format is required to correlate trace between Stackdriver Trace and Stackdriver Logging.</p>
</div>
<div class="paragraph">
<p>If <code>projectId</code> is not set and cannot be determined, then it&#8217;ll log <code>traceId</code> without the fully qualified format.</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeTraceId</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the <code>traceId</code> be included</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeSpanId</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the <code>spanId</code> be included</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeLevel</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the severity be included</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeThreadName</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the thread name be included</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeMDC</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should all MDC properties be included.
The MDC properties <code>X-B3-TraceId</code>, <code>X-B3-SpanId</code> and <code>X-Span-Export</code> provided by Spring Sleuth will get excluded as they get handled separately</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeLoggerName</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the name of the logger be included</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeFormattedMessage</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the formatted log message be included.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeExceptionInMessage</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the stacktrace be appended to the formatted log message.
This setting is only evaluated if <code>includeFormattedMessage</code> is <code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeContextName</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the logging context be included</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeMessage</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the log message with blank placeholders be included</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeException</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Should the stacktrace be included as a own field</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>serviceContext</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">none</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Define the Stackdriver service context data (service and version). This allows filtering of error reports for service and version in the <a href="https://console.cloud.google.com/errors">Google Cloud Error Reporting View</a>.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>customJson</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">none</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Defines custom json data. Data will be added to the json output.</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>This is an example of such an Logback configuration:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;configuration &gt;
&lt;property name="projectId" value="${projectId:-${GOOGLE_CLOUD_PROJECT}}"/&gt;
&lt;appender name="CONSOLE_JSON" class="ch.qos.logback.core.ConsoleAppender"&gt;
&lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"&gt;
&lt;layout class="org.springframework.cloud.gcp.logging.StackdriverJsonLayout"&gt;
&lt;projectId&gt;${projectId}&lt;/projectId&gt;
&lt;!--&lt;includeTraceId&gt;true&lt;/includeTraceId&gt;--&gt;
&lt;!--&lt;includeSpanId&gt;true&lt;/includeSpanId&gt;--&gt;
&lt;!--&lt;includeLevel&gt;true&lt;/includeLevel&gt;--&gt;
&lt;!--&lt;includeThreadName&gt;true&lt;/includeThreadName&gt;--&gt;
&lt;!--&lt;includeMDC&gt;true&lt;/includeMDC&gt;--&gt;
&lt;!--&lt;includeLoggerName&gt;true&lt;/includeLoggerName&gt;--&gt;
&lt;!--&lt;includeFormattedMessage&gt;true&lt;/includeFormattedMessage&gt;--&gt;
&lt;!--&lt;includeExceptionInMessage&gt;true&lt;/includeExceptionInMessage&gt;--&gt;
&lt;!--&lt;includeContextName&gt;true&lt;/includeContextName&gt;--&gt;
&lt;!--&lt;includeMessage&gt;false&lt;/includeMessage&gt;--&gt;
&lt;!--&lt;includeException&gt;false&lt;/includeException&gt;--&gt;
&lt;!--&lt;serviceContext&gt;
&lt;service&gt;service-name&lt;/service&gt;
&lt;version&gt;service-version&lt;/version&gt;
&lt;/serviceContext&gt;--&gt;
&lt;!--&lt;customJson&gt;{"custom-key": "custom-value"}&lt;/customJson&gt;--&gt;
&lt;/layout&gt;
&lt;/encoder&gt;
&lt;/appender&gt;
&lt;/configuration&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_sample_7"><a class="link" href="#_sample_7">11.3. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample">Sample Spring Boot Application</a> is provided to show how to use the Cloud logging starter.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_cloud_config"><a class="link" href="#_spring_cloud_config">12. Spring Cloud Config</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring Cloud GCP makes it possible to use the <a href="https://cloud.google.com/deployment-manager/runtime-configurator/reference/rest/">Google Runtime Configuration API</a> as a <a href="https://cloud.spring.io/spring-cloud-config/">Spring Cloud Config</a> server to remotely store your application configuration data.</p>
</div>
<div class="paragraph">
<p>The Spring Cloud GCP Config support is provided via its own Spring Boot starter.
It enables the use of the Google Runtime Configuration API as a source for Spring Boot configuration properties.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The Google Cloud Runtime Configuration service is in beta status.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-config&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-config'
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_configuration_3"><a class="link" href="#_configuration_3">12.1. Configuration</a></h3>
<div class="paragraph">
<p>The following parameters are configurable in Spring Cloud GCP Config:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.config.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables the Config client</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.config.name</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of your application</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Value of the <code>spring.application.name</code> property.
If none, <code>application</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.config.profile</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Active profile</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Value of the <code>spring.profiles.active</code> property.
If more than a single profile, last one is chosen</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.config.timeout-millis</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Timeout in milliseconds for connecting to the Google Runtime Configuration API</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>60000</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.config.project-id</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GCP project ID where the Google Runtime Configuration API is hosted</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.config.credentials.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">OAuth2 credentials for authenticating with the Google Runtime Configuration API</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.config.credentials.encoded-key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Base64-encoded OAuth2 credentials for authenticating with the Google Runtime Configuration API</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.config.credentials.scopes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://developers.google.com/identity/protocols/googlescopes">OAuth2 scope</a> for Spring Cloud GCP Config credentials</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/cloudruntimeconfig" class="bare">https://www.googleapis.com/auth/cloudruntimeconfig</a></p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
These properties should be specified in a <a href="https://cloud.spring.io/spring-cloud-static/spring-cloud.html#_the_bootstrap_application_context"><code>bootstrap.yml</code>/<code>bootstrap.properties</code></a> file, rather than the usual <code>applications.yml</code>/<code>application.properties</code>.
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Core properties, as described in <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a>, do not apply to Spring Cloud GCP Config.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_quick_start"><a class="link" href="#_quick_start">12.2. Quick start</a></h3>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Create a configuration in the Google Runtime Configuration API that is called <code>${spring.application.name}_${spring.profiles.active}</code>.
In other words, if <code>spring.application.name</code> is <code>myapp</code> and <code>spring.profiles.active</code> is <code>prod</code>, the configuration should be called <code>myapp_prod</code>.</p>
<div class="paragraph">
<p>In order to do that, you should have the <a href="https://cloud.google.com/sdk/">Google Cloud SDK</a> installed, own a Google Cloud Project and run the following command:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>gcloud init # if this is your first Google Cloud SDK run.
gcloud beta runtime-config configs create myapp_prod
gcloud beta runtime-config configs variables set myapp.queue-size 25 --config-name myapp_prod</code></pre>
</div>
</div>
</li>
<li>
<p>Configure your <code>bootstrap.properties</code> file with your application&#8217;s configuration data:</p>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>spring.application.name=myapp
spring.profiles.active=prod</code></pre>
</div>
</div>
</li>
<li>
<p>Add the <code>@ConfigurationProperties</code> annotation to a Spring-managed bean:</p>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>@Component
@ConfigurationProperties("myapp")
public class SampleConfig {
private int queueSize;
public int getQueueSize() {
return this.queueSize;
}
public void setQueueSize(int queueSize) {
this.queueSize = queueSize;
}
}</code></pre>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>When your Spring application starts, the <code>queueSize</code> field value will be set to 25 for the above <code>SampleConfig</code> bean.</p>
</div>
</div>
<div class="sect2">
<h3 id="_refreshing_the_configuration_at_runtime"><a class="link" href="#_refreshing_the_configuration_at_runtime">12.3. Refreshing the configuration at runtime</a></h3>
<div class="paragraph">
<p><a href="https://cloud.spring.io/spring-cloud-static/docs/1.0.x/spring-cloud.html#_endpoints">Spring Cloud</a> provides support to have configuration parameters be reloadable with the POST request to <code>/actuator/refresh</code> endpoint.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Add the Spring Boot Actuator dependency:</p>
<div class="paragraph">
<p>Maven coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;</pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'
}</code></pre>
</div>
</div>
</li>
<li>
<p>Add <code>@RefreshScope</code> to your Spring configuration class to have parameters be reloadable at runtime.</p>
</li>
<li>
<p>Add <code>management.endpoints.web.exposure.include=refresh</code> to your <code>application.properties</code> to allow unrestricted access to <code>/actuator/refresh</code>.</p>
</li>
<li>
<p>Update a property with <code>gcloud</code>:</p>
<div class="literalblock">
<div class="content">
<pre>$ gcloud beta runtime-config configs variables set \
myapp.queue_size 200 \
--config-name myapp_prod</pre>
</div>
</div>
</li>
<li>
<p>Send a POST request to the refresh endpoint:</p>
<div class="literalblock">
<div class="content">
<pre>$ curl -XPOST https://myapp.host.com/actuator/refresh</pre>
</div>
</div>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_sample_8"><a class="link" href="#_sample_8">12.4. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-config-sample">sample application</a> and a <a href="https://codelabs.developers.google.com/codelabs/cloud-spring-runtime-config/index.html">codelab</a> are available.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_data_cloud_spanner"><a class="link" href="#_spring_data_cloud_spanner">13. Spring Data Cloud Spanner</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://projects.spring.io/spring-data/">Spring Data</a> is an abstraction for storing and retrieving POJOs in numerous storage technologies.
Spring Cloud GCP adds Spring Data support for <a href="https://cloud.google.com/spanner/">Google Cloud Spanner</a>.</p>
</div>
<div class="paragraph">
<p>Maven coordinates for this module only, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-data-spanner&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-data-spanner'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We provide a <a href="../spring-cloud-gcp-starters/spring-cloud-gcp-starter-data-spanner">Spring Boot Starter for Spring Data Spanner</a>, with which you can leverage our recommended auto-configuration setup.
To use the starter, see the coordinates see below.</p>
</div>
<div class="paragraph">
<p>Maven:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-data-spanner&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-spanner'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This setup takes care of bringing in the latest compatible version of Cloud Java Cloud Spanner libraries as well.</p>
</div>
<div class="sect2">
<h3 id="_configuration_4"><a class="link" href="#_configuration_4">13.1. Configuration</a></h3>
<div class="paragraph">
<p>To setup Spring Data Cloud Spanner, you have to configure the following:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Setup the connection details to Google Cloud Spanner.</p>
</li>
<li>
<p>Enable Spring Data Repositories (optional).</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_cloud_spanner_settings"><a class="link" href="#_cloud_spanner_settings">13.1.1. Cloud Spanner settings</a></h4>
<div class="paragraph">
<p>You can the use <a href="../spring-cloud-gcp-starters/spring-cloud-gcp-starter-data-spanner">Spring Boot Starter for Spring Data Spanner</a> to autoconfigure Google Cloud Spanner 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>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.instance-id</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Spanner instance to use</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.database</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cloud Spanner database to use</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.project-id</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GCP project ID where the Google Cloud Spanner API is hosted, if different from the one in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.credentials.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">OAuth2 credentials for authenticating with the
Google Cloud Spanner API, if different from the ones in the
<a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.credentials.encoded-key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Base64-encoded OAuth2 credentials for authenticating with the
Google Cloud Spanner API, if different from the ones in the
<a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.credentials.scopes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://developers.google.com/identity/protocols/googlescopes">OAuth2 scope</a> for Spring Cloud GCP
Cloud Spanner credentials</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/spanner.data" class="bare">https://www.googleapis.com/auth/spanner.data</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.createInterleavedTableDdlOnDeleteCascade</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If <code>true</code>, then schema statements generated by <code>SpannerSchemaUtils</code> for tables with interleaved parent-child relationships will be "ON DELETE CASCADE".
The schema for the tables will be "ON DELETE NO ACTION" if <code>false</code>.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.numRpcChannels</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of gRPC channels used to connect to Cloud Spanner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">4 - Determined by Cloud Spanner client library</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.prefetchChunks</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of chunks prefetched by Cloud Spanner for read and query</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">4 - Determined by Cloud Spanner client library</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.minSessions</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minimum number of sessions maintained in the session pool</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0 - Determined by Cloud Spanner client library</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.maxSessions</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of sessions session pool can have</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">400 - Determined by Cloud Spanner client library</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.maxIdleSessions</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of idle sessions session pool will maintain</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0 - Determined by Cloud Spanner client library</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.writeSessionsFraction</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Fraction of sessions to be kept prepared for write transactions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0.2 - Determined by Cloud Spanner client library</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.spanner.keepAliveIntervalMinutes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">How long to keep idle sessions alive</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">30 - Determined by Cloud Spanner client library</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect3">
<h4 id="_repository_settings"><a class="link" href="#_repository_settings">13.1.2. Repository settings</a></h4>
<div class="paragraph">
<p>Spring Data Repositories can be configured via the <code>@EnableSpannerRepositories</code> annotation on your main <code>@Configuration</code> class.
With our Spring Boot Starter for Spring Data Cloud Spanner, <code>@EnableSpannerRepositories</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 href="https://github.com/spring-cloud/spring-cloud-gcp/blob/master/spring-cloud-gcp-data-spanner/src/main/java/org/springframework/cloud/gcp/data/spanner/repository/config/EnableSpannerRepositories.java"><code>@EnableSpannerRepositories</code></a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_autoconfiguration"><a class="link" href="#_autoconfiguration">13.1.3. Autoconfiguration</a></h4>
<div class="paragraph">
<p>Our Spring Boot autoconfiguration creates the following beans available in the Spring application context:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>an instance of <code>SpannerTemplate</code></p>
</li>
<li>
<p>an instance of <code>SpannerDatabaseAdminTemplate</code> for generating table schemas from object hierarchies and creating and deleting tables and databases</p>
</li>
<li>
<p>an instance of all user-defined repositories extending <code>SpannerRepository</code>, <code>CrudRepository</code>, <code>PagingAndSortingRepository</code>, when repositories are enabled</p>
</li>
<li>
<p>an instance of <code>DatabaseClient</code> from the Google Cloud Java Client for Spanner, for convenience and lower level API access</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_object_mapping"><a class="link" href="#_object_mapping">13.2. Object Mapping</a></h3>
<div class="paragraph">
<p>Spring Data Cloud Spanner allows you to map domain POJOs to Cloud Spanner tables via annotations:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Table(name = "traders")
public class Trader {
@PrimaryKey
@Column(name = "trader_id")
String traderId;
String firstName;
String lastName;
@NotMapped
Double temporaryNumber;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Spring Data Cloud Spanner will ignore any property annotated with <code>@NotMapped</code>.
These properties will not be written to or read from Spanner.</p>
</div>
<div class="sect3">
<h4 id="_constructors"><a class="link" href="#_constructors">13.2.1. Constructors</a></h4>
<div class="paragraph">
<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>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Table(name = "traders")
public class Trader {
@PrimaryKey
@Column(name = "trader_id")
String traderId;
String firstName;
String lastName;
@NotMapped
Double temporaryNumber;
public Trader(String traderId, String firstName) {
this.traderId = traderId;
this.firstName = firstName;
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_table"><a class="link" href="#_table">13.2.2. Table</a></h4>
<div class="paragraph">
<p>The <code>@Table</code> annotation can provide the name of the Cloud Spanner table that stores instances of the annotated class, one per row.
This annotation is optional, and if not given, the name of the table is inferred from the class name with the first character uncapitalized.</p>
</div>
<div class="sect4">
<h5 id="_spel_expressions_for_table_names"><a class="link" href="#_spel_expressions_for_table_names">SpEL expressions for table names</a></h5>
<div class="paragraph">
<p>In some cases, you might want the <code>@Table</code> table name to be determined dynamically.
To do that, you can use <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions">Spring Expression Language</a>.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Table(name = "trades_#{tableNameSuffix}")
public class Trade {
// ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The table name will be resolved only if the <code>tableNameSuffix</code> value/bean in the Spring application context is defined.
For example, if <code>tableNameSuffix</code> has the value "123", the table name will resolve to <code>trades_123</code>.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_primary_keys"><a class="link" href="#_primary_keys">13.2.3. Primary Keys</a></h4>
<div class="paragraph">
<p>For a simple table, you may only have a primary key consisting of a single column.
Even in that case, the <code>@PrimaryKey</code> annotation is required.
<code>@PrimaryKey</code> identifies the one or more ID properties corresponding to the primary key.</p>
</div>
<div class="paragraph">
<p>Spanner has first class support for composite primary keys of multiple columns.
You have to annotate all of your POJO&#8217;s fields that the primary key consists of with <code>@PrimaryKey</code> as below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Table(name = "trades")
public class Trade {
@PrimaryKey(keyOrder = 2)
@Column(name = "trade_id")
private String tradeId;
@PrimaryKey(keyOrder = 1)
@Column(name = "trader_id")
private String traderId;
private String action;
private Double price;
private Double shares;
private String symbol;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>keyOrder</code> parameter of <code>@PrimaryKey</code> identifies the properties corresponding to the primary key columns in order, starting with 1 and increasing consecutively.
Order is important and must reflect the order defined in the Cloud Spanner schema.
In our example the DDL to create the table and its primary key is as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-sql hljs" data-lang="sql">CREATE TABLE trades (
trader_id STRING(MAX),
trade_id STRING(MAX),
action STRING(15),
symbol STRING(10),
price FLOAT64,
shares FLOAT64
) PRIMARY KEY (trader_id, trade_id)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Spanner does not have automatic ID generation.
For most use-cases, sequential IDs should be used with caution to avoid creating data hotspots in the system.
Read <a href="https://cloud.google.com/spanner/docs/schema-and-data-model#primary_keys">Spanner Primary Keys documentation</a> for a better understanding of primary keys and recommended practices.</p>
</div>
</div>
<div class="sect3">
<h4 id="_columns"><a class="link" href="#_columns">13.2.4. Columns</a></h4>
<div class="paragraph">
<p>All accessible properties on POJOs are automatically recognized as a Cloud Spanner column.
Column naming is generated by the <code>PropertyNameFieldNamingStrategy</code> by default defined on the <code>SpannerMappingContext</code> bean.
The <code>@Column</code> annotation optionally provides a different column name than that of the property and some other settings:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>name</code> is the optional name of the column</p>
</li>
<li>
<p><code>spannerTypeMaxLength</code> specifies for <code>STRING</code> and <code>BYTES</code> columns the maximum length.
This setting is only used when generating DDL schema statements based on domain types.</p>
</li>
<li>
<p><code>nullable</code> specifies if the column is created as <code>NOT NULL</code>.
This setting is only used when generating DDL schema statements based on domain types.</p>
</li>
<li>
<p><code>spannerType</code> is the Cloud Spanner column type you can optionally specify.
If this is not specified then a compatible column type is inferred from the Java property type.</p>
</li>
<li>
<p><code>spannerCommitTimestamp</code> is a boolean specifying if this property corresponds to an auto-populated commit timestamp column.
Any value set in this property will be ignored when writing to Cloud Spanner.</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_embedded_objects"><a class="link" href="#_embedded_objects">13.2.5. Embedded Objects</a></h4>
<div class="paragraph">
<p>If an object of type <code>B</code> is embedded as a property of <code>A</code>, then the columns of <code>B</code> will be saved in the same Cloud Spanner table as those of <code>A</code>.</p>
</div>
<div class="paragraph">
<p>If <code>B</code> has primary key columns, those columns will be included in the primary key of <code>A</code>. <code>B</code> can also have embedded properties.
Embedding allows reuse of columns between multiple entities, and can be useful for implementing parent-child situations, because Cloud Spanner requires child tables to include the key columns of their parents.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">class X {
@PrimaryKey
String grandParentId;
long age;
}
class A {
@PrimaryKey
@Embedded
X grandParent;
@PrimaryKey(keyOrder = 2)
String parentId;
String value;
}
@Table(name = "items")
class B {
@PrimaryKey
@Embedded
A parent;
@PrimaryKey(keyOrder = 2)
String id;
@Column(name = "child_value")
String value;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Entities of <code>B</code> can be stored in a table defined as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-sql hljs" data-lang="sql">CREATE TABLE items (
grandParentId STRING(MAX),
parentId STRING(MAX),
id STRING(MAX),
value STRING(MAX),
child_value STRING(MAX),
age INT64
) PRIMARY KEY (grandParentId, parentId, id)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note that embedded properties' column names must all be unique.</p>
</div>
</div>
<div class="sect3">
<h4 id="_relationships"><a class="link" href="#_relationships">13.2.6. Relationships</a></h4>
<div class="paragraph">
<p>Spring Data Cloud Spanner supports parent-child relationships using the Cloud Spanner <a href="https://cloud.google.com/spanner/docs/schema-and-data-model#creating-interleaved-tables">parent-child interleaved table mechanism</a>.
Cloud Spanner interleaved tables enforce the one-to-many relationship and provide efficient queries and operations on entities of a single domain parent entity.
These relationships can be up to 7 levels deep.
Cloud Spanner also provides automatic cascading delete or enforces the deletion of child entities before parents.</p>
</div>
<div class="paragraph">
<p>While one-to-one and many-to-many relationships can be implemented in Cloud Spanner and Spring Data Cloud Spanner using constructs of interleaved parent-child tables, only the parent-child relationship is natively supported.
Cloud Spanner does not support the foreign key constraint, though the parent-child key constraint enforces a similar requirement when used with interleaved tables.</p>
</div>
<div class="paragraph">
<p>For example, the following Java entities:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Table(name = "Singers")
class Singer {
@PrimaryKey
long SingerId;
String FirstName;
String LastName;
byte[] SingerInfo;
@Interleaved
List&lt;Album&gt; albums;
}
@Table(name = "Albums")
class Album {
@PrimaryKey
long SingerId;
@PrimaryKey(keyOrder = 2)
long AlbumId;
String AlbumTitle;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>These classes can correspond to an existing pair of interleaved tables.
The <code>@Interleaved</code> annotation may be applied to <code>Collection</code> properties and the inner type is resolved as the child entity type.
The schema needed to create them can also be generated using the <code>SpannerSchemaUtils</code> and executed using the <code>SpannerDatabaseAdminTemplate</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
SpannerSchemaUtils schemaUtils;
@Autowired
SpannerDatabaseAdminTemplate databaseAdmin;
...
// Get the create statmenets for all tables in the table structure rooted at Singer
List&lt;String&gt; createStrings = this.schemaUtils.getCreateTableDdlStringsForInterleavedHierarchy(Singer.class);
// Create the tables and also create the database if necessary
this.databaseAdmin.executeDdlStrings(createStrings, true);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>createStrings</code> list contains table schema statements using column names and types compatible with the provided Java type and any resolved child relationship types contained within based on the configured custom converters.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-sql hljs" data-lang="sql">CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX),
) PRIMARY KEY (SingerId);
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>ON DELETE CASCADE</code> clause indicates that Cloud Spanner will delete all Albums of a singer if the Singer is deleted.
The alternative is <code>ON DELETE NO ACTION</code>, where a Singer cannot be deleted until all of its Albums have already been deleted.
When using <code>SpannerSchemaUtils</code> to generate the schema strings, the <code>spring.cloud.gcp.spanner.createInterleavedTableDdlOnDeleteCascade</code> boolean setting determines if these schema are generated as <code>ON DELETE CASCADE</code> for <code>true</code> and <code>ON DELETE NO ACTION</code> for <code>false</code>.</p>
</div>
<div class="paragraph">
<p>Cloud Spanner restricts these relationships to 7 child layers.
A table may have multiple child tables.</p>
</div>
<div class="paragraph">
<p>On updating or inserting an object to Cloud Spanner, all of its referenced children objects are also updated or inserted in the same request, respectively.
On read, all of the interleaved child rows are also all read.</p>
</div>
<div class="sect4">
<h5 id="_lazy_fetch"><a class="link" href="#_lazy_fetch">Lazy Fetch</a></h5>
<div class="paragraph">
<p><code>@Interleaved</code> properties are retrieved eagerly by default, but can be fetched lazily for performance in both read and write:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Interleaved(lazy = true)
List&lt;Album&gt; albums;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Lazily-fetched interleaved properties are retrieved upon the first interaction with the property.
If a property marked for lazy fetching is never retrieved, then it is also skipped when saving the parent entity.</p>
</div>
<div class="paragraph">
<p>If used inside a transaction, subsequent operations on lazily-fetched properties use the same transaction context as that of the original parent entity.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_supported_types"><a class="link" href="#_supported_types">13.2.7. Supported Types</a></h4>
<div class="paragraph">
<p>Spring Data Cloud Spanner natively supports the following types for regular fields but also utilizes custom converters (detailed in following sections) and dozens of pre-defined Spring Data custom converters to handle other common Java types.</p>
</div>
<div class="paragraph">
<p>Natively supported types:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>com.google.cloud.ByteArray</code></p>
</li>
<li>
<p><code>com.google.cloud.Date</code></p>
</li>
<li>
<p><code>com.google.cloud.Timestamp</code></p>
</li>
<li>
<p><code>java.lang.Boolean</code>, <code>boolean</code></p>
</li>
<li>
<p><code>java.lang.Double</code>, <code>double</code></p>
</li>
<li>
<p><code>java.lang.Long</code>, <code>long</code></p>
</li>
<li>
<p><code>java.lang.Integer</code>, <code>int</code></p>
</li>
<li>
<p><code>java.lang.String</code></p>
</li>
<li>
<p><code>double[]</code></p>
</li>
<li>
<p><code>long[]</code></p>
</li>
<li>
<p><code>boolean[]</code></p>
</li>
<li>
<p><code>java.util.Date</code></p>
</li>
<li>
<p><code>java.util.Instant</code></p>
</li>
<li>
<p><code>java.sql.Date</code></p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_lists"><a class="link" href="#_lists">13.2.8. Lists</a></h4>
<div class="paragraph">
<p>Spanner supports <code>ARRAY</code> types for columns.
<code>ARRAY</code> columns are mapped to <code>List</code> fields in POJOS.</p>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;Double&gt; curve;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The types inside the lists can be any singular property type.</p>
</div>
</div>
<div class="sect3">
<h4 id="_lists_of_structs"><a class="link" href="#_lists_of_structs">13.2.9. Lists of Structs</a></h4>
<div class="paragraph">
<p>Cloud Spanner queries can <a href="https://cloud.google.com/spanner/docs/query-syntax#using-structs-with-select">construct STRUCT values</a> that appear as columns in the result.
Cloud Spanner requires STRUCT values appear in ARRAYs at the root level: <code>SELECT ARRAY(SELECT STRUCT(1 as val1, 2 as val2)) as pair FROM Users</code>.</p>
</div>
<div class="paragraph">
<p>Spring Data Cloud Spanner will attempt to read the column STRUCT values into a property that is an <code>Iterable</code> of an entity type compatible with the schema of the column STRUCT value.</p>
</div>
<div class="paragraph">
<p>For the previous array-select example, the following property can be mapped with the constructed <code>ARRAY&lt;STRUCT&gt;</code> column: <code>List&lt;TwoInts&gt; pair;</code> where the <code>TwoInts</code> type is defined:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">class TwoInts {
int val1;
int val2;
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_custom_types"><a class="link" href="#_custom_types">13.2.10. Custom types</a></h4>
<div class="paragraph">
<p>Custom converters can be used to extend the type support for user defined types.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Converters need to implement the <code>org.springframework.core.convert.converter.Converter</code> interface in both directions.</p>
</li>
<li>
<p>The user defined type needs to be mapped to one of the basic types supported by Spanner:</p>
<div class="ulist">
<ul>
<li>
<p><code>com.google.cloud.ByteArray</code></p>
</li>
<li>
<p><code>com.google.cloud.Date</code></p>
</li>
<li>
<p><code>com.google.cloud.Timestamp</code></p>
</li>
<li>
<p><code>java.lang.Boolean</code>, <code>boolean</code></p>
</li>
<li>
<p><code>java.lang.Double</code>, <code>double</code></p>
</li>
<li>
<p><code>java.lang.Long</code>, <code>long</code></p>
</li>
<li>
<p><code>java.lang.String</code></p>
</li>
<li>
<p><code>double[]</code></p>
</li>
<li>
<p><code>long[]</code></p>
</li>
<li>
<p><code>boolean[]</code></p>
</li>
<li>
<p><code>enum</code> types</p>
</li>
</ul>
</div>
</li>
<li>
<p>An instance of both Converters needs to be passed to a <code>ConverterAwareMappingSpannerEntityProcessor</code>, which then has to be made available as a <code>@Bean</code> for <code>SpannerEntityProcessor</code>.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="paragraph">
<p>We would like to have a field of type <code>Person</code> on our <code>Trade</code> POJO:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Table(name = "trades")
public class Trade {
//...
Person person;
//...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Where Person is a simple class:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class Person {
public String firstName;
public String lastName;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We have to define the two converters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> public class PersonWriteConverter implements Converter&lt;Person, String&gt; {
@Override
public String convert(Person person) {
return person.firstName + " " + person.lastName;
}
}
public class PersonReadConverter implements Converter&lt;String, Person&gt; {
@Override
public Person convert(String s) {
Person person = new Person();
person.firstName = s.split(" ")[0];
person.lastName = s.split(" ")[1];
return person;
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>That will be configured in our <code>@Configuration</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
public class ConverterConfiguration {
@Bean
public SpannerEntityProcessor spannerEntityProcessor(SpannerMappingContext spannerMappingContext) {
return new ConverterAwareMappingSpannerEntityProcessor(spannerMappingContext,
Arrays.asList(new PersonWriteConverter()),
Arrays.asList(new PersonReadConverter()));
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_custom_converter_for_struct_array_columns"><a class="link" href="#_custom_converter_for_struct_array_columns">13.2.11. Custom Converter for Struct Array Columns</a></h4>
<div class="paragraph">
<p>If a <code>Converter&lt;Struct, A&gt;</code> is provided, then properties of type <code>List&lt;A&gt;</code> can be used in your entity types.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_spanner_operations_template"><a class="link" href="#_spanner_operations_template">13.3. Spanner Operations &amp; Template</a></h3>
<div class="paragraph">
<p><code>SpannerOperations</code> and its implementation, <code>SpannerTemplate</code>, provides the Template pattern familiar to Spring developers.
It provides:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Resource management</p>
</li>
<li>
<p>One-stop-shop to Spanner operations with the Spring Data POJO mapping and conversion features</p>
</li>
<li>
<p>Exception conversion</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Using the <code>autoconfigure</code> provided by our Spring Boot Starter for Spanner, your Spring application context will contain a fully configured <code>SpannerTemplate</code> object that you can easily autowire in your application:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@SpringBootApplication
public class SpannerTemplateExample {
@Autowired
SpannerTemplate spannerTemplate;
public void doSomething() {
this.spannerTemplate.delete(Trade.class, KeySet.all());
//...
Trade t = new Trade();
//...
this.spannerTemplate.insert(t);
//...
List&lt;Trade&gt; tradesByAction = spannerTemplate.findAll(Trade.class);
//...
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Template API provides convenience methods for:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://cloud.google.com/spanner/docs/reads">Reads</a>, and by providing SpannerReadOptions and
SpannerQueryOptions</p>
<div class="ulist">
<ul>
<li>
<p>Stale read</p>
</li>
<li>
<p>Read with secondary indices</p>
</li>
<li>
<p>Read with limits and offsets</p>
</li>
<li>
<p>Read with sorting</p>
</li>
</ul>
</div>
</li>
<li>
<p><a href="https://cloud.google.com/spanner/docs/reads#execute_a_query">Queries</a></p>
</li>
<li>
<p>DML operations (delete, insert, update, upsert)</p>
</li>
<li>
<p>Partial reads</p>
<div class="ulist">
<ul>
<li>
<p>You can define a set of columns to be read into your entity</p>
</li>
</ul>
</div>
</li>
<li>
<p>Partial writes</p>
<div class="ulist">
<ul>
<li>
<p>Persist only a few properties from your entity</p>
</li>
</ul>
</div>
</li>
<li>
<p>Read-only transactions</p>
</li>
<li>
<p>Locking read-write transactions</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_sql_query"><a class="link" href="#_sql_query">13.3.1. SQL Query</a></h4>
<div class="paragraph">
<p>Cloud Spanner has SQL support for running read-only queries.
All the query related methods start with <code>query</code> on <code>SpannerTemplate</code>.
Using <code>SpannerTemplate</code> you can execute SQL queries that map to POJOs:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;Trade&gt; trades = this.spannerTemplate.query(Trade.class, Statement.of("SELECT * FROM trades"));</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_read"><a class="link" href="#_read">13.3.2. Read</a></h4>
<div class="paragraph">
<p>Spanner exposes a <a href="https://cloud.google.com/spanner/docs/reads">Read API</a> for reading single row or multiple rows in a table or in a secondary index.</p>
</div>
<div class="paragraph">
<p>Using <code>SpannerTemplate</code> you can execute reads, for example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;Trade&gt; trades = this.spannerTemplate.readAll(Trade.class);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Main benefit of reads over queries is reading multiple rows of a certain pattern of keys is much easier using the features of the <a href="https://github.com/GoogleCloudPlatform/google-cloud-java/blob/master/google-cloud-spanner/src/main/java/com/google/cloud/spanner/KeySet.java"><code>KeySet</code></a> class.</p>
</div>
</div>
<div class="sect3">
<h4 id="_advanced_reads"><a class="link" href="#_advanced_reads">13.3.3. Advanced reads</a></h4>
<div class="sect4">
<h5 id="_stale_read"><a class="link" href="#_stale_read">Stale read</a></h5>
<div class="paragraph">
<p>All reads and queries are <strong>strong reads</strong> by default.
A <strong>strong read</strong> is a read at a current time and is guaranteed to see all data that has been committed up until the start of this read.
An <strong>exact staleness read</strong> is read at a timestamp in the past.
Cloud Spanner allows you to determine how current the data should be when you read data.
With <code>SpannerTemplate</code> you can specify the <code>Timestamp</code> by setting it on <code>SpannerQueryOptions</code> or <code>SpannerReadOptions</code> to the appropriate read or query methods:</p>
</div>
<div class="paragraph">
<p>Reads:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// a read with options:
SpannerReadOptions spannerReadOptions = new SpannerReadOptions().setTimestamp(myTimestamp);
List&lt;Trade&gt; trades = this.spannerTemplate.readAll(Trade.class, spannerReadOptions);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Queries:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// a query with options:
SpannerQueryOptions spannerQueryOptions = new SpannerQueryOptions().setTimestamp(myTimestamp);
List&lt;Trade&gt; trades = this.spannerTemplate.query(Trade.class, Statement.of("SELECT * FROM trades"), spannerQueryOptions);</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also read with <a href="https://cloud.google.com/spanner/docs/timestamp-bounds"><strong>bounded staleness</strong></a> by setting <code>.setTimestampBound(TimestampBound.ofMinReadTimestamp(myTimestamp))</code> on the query and read options objects.
Bounded staleness lets Cloud Spanner choose any point in time later than or equal to the given timestampBound, but it cannot be used inside transactions.</p>
</div>
</div>
<div class="sect4">
<h5 id="_read_from_a_secondary_index"><a class="link" href="#_read_from_a_secondary_index">Read from a secondary index</a></h5>
<div class="paragraph">
<p>Using a <a href="https://cloud.google.com/spanner/docs/secondary-indexes">secondary index</a> is available for Reads via the Template API and it is also implicitly available via SQL for Queries.</p>
</div>
<div class="paragraph">
<p>The following shows how to read rows from a table using a <a href="https://cloud.google.com/spanner/docs/secondary-indexes">secondary index</a> simply by setting <code>index</code> on <code>SpannerReadOptions</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">SpannerReadOptions spannerReadOptions = new SpannerReadOptions().setIndex("TradesByTrader");
List&lt;Trade&gt; trades = this.spannerTemplate.readAll(Trade.class, spannerReadOptions);</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_read_with_offsets_and_limits"><a class="link" href="#_read_with_offsets_and_limits">Read with offsets and limits</a></h5>
<div class="paragraph">
<p>Limits and offsets are only supported by Queries.
The following will get only the first two rows of the query:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">SpannerQueryOptions spannerQueryOptions = new SpannerQueryOptions().setLimit(2).setOffset(3);
List&lt;Trade&gt; trades = this.spannerTemplate.query(Trade.class, Statement.of("SELECT * FROM trades"), spannerQueryOptions);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note that the above is equivalent of executing <code>SELECT * FROM trades LIMIT 2 OFFSET 3</code>.</p>
</div>
</div>
<div class="sect4">
<h5 id="_sorting"><a class="link" href="#_sorting">Sorting</a></h5>
<div class="paragraph">
<p>Reads by keys do not support sorting.
However, queries on the Template API support sorting through standard SQL and also via Spring Data Sort API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;Trade&gt; trades = this.spannerTemplate.queryAll(Trade.class, Sort.by("action"));</code></pre>
</div>
</div>
<div class="paragraph">
<p>If the provided sorted field name is that of a property of the domain type, then the column name corresponding to that property will be used in the query.
Otherwise, the given field name is assumed to be the name of the column in the Cloud Spanner table.
Sorting on columns of Cloud Spanner types STRING and BYTES can be done while ignoring case:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Sort.by(Order.desc("action").ignoreCase())</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_partial_read"><a class="link" href="#_partial_read">Partial read</a></h5>
<div class="paragraph">
<p>Partial read is only possible when using Queries.
In case the rows returned by the query have fewer columns than the entity that it will be mapped to, Spring Data will map the returned columns only.
This setting also applies to nested structs and their corresponding nested POJO properties.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;Trade&gt; trades = this.spannerTemplate.query(Trade.class, Statement.of("SELECT action, symbol FROM trades"),
new SpannerQueryOptions().setAllowMissingResultSetColumns(true));</code></pre>
</div>
</div>
<div class="paragraph">
<p>If the setting is set to <code>false</code>, then an exception will be thrown if there are missing columns in the query result.</p>
</div>
</div>
<div class="sect4">
<h5 id="_summary_of_options_for_query_vs_read"><a class="link" href="#_summary_of_options_for_query_vs_read">Summary of options for Query vs Read</a></h5>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Feature</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Query supports it</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Read supports it</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">SQL</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Partial read</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Limits</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Offsets</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Secondary index</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Read using index range</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Sorting</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_write_update"><a class="link" href="#_write_update">13.3.4. Write / Update</a></h4>
<div class="paragraph">
<p>The write methods of <code>SpannerOperations</code> accept a POJO and writes all of its properties to Spanner.
The corresponding Spanner table and entity metadata is obtained from the given object&#8217;s actual type.</p>
</div>
<div class="paragraph">
<p>If a POJO was retrieved from Spanner and its primary key properties values were changed and then written or updated, the operation will occur as if against a row with the new primary key values.
The row with the original primary key values will not be affected.</p>
</div>
<div class="sect4">
<h5 id="_insert"><a class="link" href="#_insert">Insert</a></h5>
<div class="paragraph">
<p>The <code>insert</code> method of <code>SpannerOperations</code> accepts a POJO and writes all of its properties to Spanner, which means the operation will fail if a row with the POJO&#8217;s primary key already exists in the table.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Trade t = new Trade();
this.spannerTemplate.insert(t);</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_update"><a class="link" href="#_update">Update</a></h5>
<div class="paragraph">
<p>The <code>update</code> method of <code>SpannerOperations</code> accepts a POJO and writes all of its properties to Spanner, which means the operation will fail if the POJO&#8217;s primary key does not already exist in the table.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// t was retrieved from a previous operation
this.spannerTemplate.update(t);</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_upsert"><a class="link" href="#_upsert">Upsert</a></h5>
<div class="paragraph">
<p>The <code>upsert</code> method of <code>SpannerOperations</code> accepts a POJO and writes all of its properties to Spanner using update-or-insert.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// t was retrieved from a previous operation or it's new
this.spannerTemplate.upsert(t);</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_partial_update"><a class="link" href="#_partial_update">Partial Update</a></h5>
<div class="paragraph">
<p>The update methods of <code>SpannerOperations</code> operate by default on all properties within the given object, but also accept <code>String[]</code> and <code>Optional&lt;Set&lt;String&gt;&gt;</code> of column names.
If the <code>Optional</code> of set of column names is empty, then all columns are written to Spanner.
However, if the Optional is occupied by an empty set, then no columns will be written.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// t was retrieved from a previous operation or it's new
this.spannerTemplate.update(t, "symbol", "action");</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_dml"><a class="link" href="#_dml">13.3.5. DML</a></h4>
<div class="paragraph">
<p>DML statements can be executed using <code>SpannerOperations.executeDmlStatement</code>.
Inserts, updates, and deletions can affect any number of rows and entities.</p>
</div>
<div class="paragraph">
<p>You can execute <a href="https://cloud.google.com/spanner/docs/dml-partitioned">partitioned DML</a> updates by using the <code>executePartitionedDmlStatement</code> method.
Partitioned DML queries have performance benefits but also have restrictions and cannot be used inside transactions.</p>
</div>
</div>
<div class="sect3">
<h4 id="_transactions"><a class="link" href="#_transactions">13.3.6. Transactions</a></h4>
<div class="paragraph">
<p><code>SpannerOperations</code> provides methods to run <code>java.util.Function</code> objects within a single transaction while making available the read and write methods from <code>SpannerOperations</code>.</p>
</div>
<div class="sect4">
<h5 id="_readwrite_transaction"><a class="link" href="#_readwrite_transaction">Read/Write Transaction</a></h5>
<div class="paragraph">
<p>Read and write transactions are provided by <code>SpannerOperations</code> via the <code>performReadWriteTransaction</code> method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
SpannerOperations mySpannerOperations;
public String doWorkInsideTransaction() {
return mySpannerOperations.performReadWriteTransaction(
transActionSpannerOperations -&gt; {
// Work with transActionSpannerOperations here.
// It is also a SpannerOperations object.
return "transaction completed";
}
);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>performReadWriteTransaction</code> method accepts a <code>Function</code> that is provided an instance of a <code>SpannerOperations</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>SpannerOperations</code> with a few exceptions:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Its read functionality cannot perform stale reads, because all reads and writes happen at the single point in time of the transaction.</p>
</li>
<li>
<p>It cannot perform sub-transactions via <code>performReadWriteTransaction</code> or <code>performReadOnlyTransaction</code>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>As these read-write transactions are locking, it is recommended that you use the <code>performReadOnlyTransaction</code> if your function does not perform any writes.</p>
</div>
</div>
<div class="sect4">
<h5 id="_read_only_transaction"><a class="link" href="#_read_only_transaction">Read-only Transaction</a></h5>
<div class="paragraph">
<p>The <code>performReadOnlyTransaction</code> method is used to perform read-only transactions using a <code>SpannerOperations</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
SpannerOperations mySpannerOperations;
public String doWorkInsideTransaction() {
return mySpannerOperations.performReadOnlyTransaction(
transActionSpannerOperations -&gt; {
// Work with transActionSpannerOperations here.
// It is also a SpannerOperations object.
return "transaction completed";
}
);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>performReadOnlyTransaction</code> method accepts a <code>Function</code> that is provided an instance of a
<code>SpannerOperations</code> object.
This method also accepts a <code>ReadOptions</code> object, but the only attribute used is the timestamp used to determine the snapshot in time to perform the reads in the transaction.
If the timestamp is not set in the read options the transaction is run against the current state of the database.
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>SpannerOperations</code> with
a few exceptions:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Its read functionality cannot perform stale reads (other than the staleness set on the entire transaction), because all reads happen at the single point in time of the transaction.</p>
</li>
<li>
<p>It cannot perform sub-transactions via <code>performReadWriteTransaction</code> or <code>performReadOnlyTransaction</code></p>
</li>
<li>
<p>It cannot perform any write operations.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Because read-only transactions are non-locking and can be performed on points in time in the past, these are recommended for functions that do not perform write operations.</p>
</div>
</div>
<div class="sect4">
<h5 id="_declarative_transactions_with_transactional_annotation"><a class="link" href="#_declarative_transactions_with_transactional_annotation">Declarative Transactions with @Transactional Annotation</a></h5>
<div class="paragraph">
<p>This feature requires a bean of <code>SpannerTransactionManager</code>, which is provided when using <code>spring-cloud-gcp-starter-data-spanner</code>.</p>
</div>
<div class="paragraph">
<p><code>SpannerTemplate</code> and <code>SpannerRepository</code> support running methods with the <code>@Transactional</code> [annotation](<a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative" class="bare">https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative</a>) as transactions.
If a method annotated with <code>@Transactional</code> calls another method also annotated, then both methods will work within the same transaction.
<code>performReadOnlyTransaction</code> and <code>performReadWriteTransaction</code> cannot be used in <code>@Transactional</code> annotated methods because Cloud Spanner does not support transactions within transactions.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_dml_statements"><a class="link" href="#_dml_statements">13.3.7. DML Statements</a></h4>
<div class="paragraph">
<p><code>SpannerTemplate</code> supports [DML](<a href="https://cloud.google.com/spanner/docs/dml-tasks" class="bare">https://cloud.google.com/spanner/docs/dml-tasks</a>) <code>Statements</code>.
DML statements can also be executed in transactions via <code>performReadWriteTransaction</code> or using the <code>@Transactional</code> annotation.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_repositories"><a class="link" href="#_repositories">13.4. Repositories</a></h3>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories">Spring Data Repositories</a> are a powerful abstraction that can save you a lot of boilerplate code.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TraderRepository extends SpannerRepository&lt;Trader, String&gt; {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Spring Data generates a working implementation of the specified interface, which can be conveniently autowired into an application.</p>
</div>
<div class="paragraph">
<p>The <code>Trader</code> type parameter to <code>SpannerRepository</code> refers to the underlying domain type.
The second type parameter, <code>String</code> in this case, refers to the type of the key of the domain type.</p>
</div>
<div class="paragraph">
<p>For POJOs with a composite primary key, this ID type parameter can be any descendant of <code>Object[]</code> compatible with all primary key properties, any descendant of <code>Iterable</code>, or <code>com.google.cloud.spanner.Key</code>.
If the domain POJO type only has a single primary key column, then the primary key property type can be used or the <code>Key</code> type.</p>
</div>
<div class="paragraph">
<p>For example in case of Trades, that belong to a Trader, <code>TradeRepository</code> would look like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TradeRepository extends SpannerRepository&lt;Trade, String[]&gt; {
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class MyApplication {
@Autowired
SpannerTemplate spannerTemplate;
@Autowired
StudentRepository studentRepository;
public void demo() {
this.tradeRepository.deleteAll();
String traderId = "demo_trader";
Trade t = new Trade();
t.symbol = stock;
t.action = action;
t.traderId = traderId;
t.price = 100.0;
t.shares = 12345.6;
this.spannerTemplate.insert(t);
Iterable&lt;Trade&gt; allTrades = this.tradeRepository.findAll();
int count = this.tradeRepository.countByAction("BUY");
}
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="_crud_repository"><a class="link" href="#_crud_repository">13.4.1. CRUD Repository</a></h4>
<div class="paragraph">
<p><code>CrudRepository</code> methods work as expected, with one thing Spanner specific: the <code>save</code> and <code>saveAll</code> methods work as update-or-insert.</p>
</div>
</div>
<div class="sect3">
<h4 id="_paging_and_sorting_repository"><a class="link" href="#_paging_and_sorting_repository">13.4.2. Paging and Sorting Repository</a></h4>
<div class="paragraph">
<p>You can also use <code>PagingAndSortingRepository</code> with Spanner Spring Data.
The sorting and pageable <code>findAll</code> methods available from this interface operate on the current state of the Spanner database.
As a result, beware that the state of the database (and the results) might change when moving page to page.</p>
</div>
</div>
<div class="sect3">
<h4 id="_spanner_repository"><a class="link" href="#_spanner_repository">13.4.3. Spanner Repository</a></h4>
<div class="paragraph">
<p>The <code>SpannerRepository</code> extends the <code>PagingAndSortingRepository</code>, but adds the read-only and the read-write transaction functionality provided by Spanner.
These transactions work very similarly to those of <code>SpannerOperations</code>, but is specific to the repository&#8217;s domain type and provides repository functions instead of template functions.</p>
</div>
<div class="paragraph">
<p>For example, this is a read-only transaction:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
SpannerRepository myRepo;
public String doWorkInsideTransaction() {
return myRepo.performReadOnlyTransaction(
transactionSpannerRepo -&gt; {
// Work with the single-transaction transactionSpannerRepo here.
// This is a SpannerRepository object.
return "transaction completed";
}
);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>When creating custom repositories for your own domain types and query methods, you can extend <code>SpannerRepository</code> to access Cloud Spanner-specific features as well as all features from <code>PagingAndSortingRepository</code> and <code>CrudRepository</code>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_query_methods"><a class="link" href="#_query_methods">13.5. Query Methods</a></h3>
<div class="paragraph">
<p><code>SpannerRepository</code> supports Query Methods.
Described in the following sections, these are methods residing in your custom repository interfaces of which implementations are generated based on their names and annotations.
Query Methods can read, write, and delete entities in Cloud Spanner.
Parameters to these methods can be any Cloud Spanner data type supported directly or via custom configured converters.
Parameters can also be of type <code>Struct</code> or POJOs.
If a POJO is given as a parameter, it will be converted to a <code>Struct</code> with the same type-conversion logic as used to create write mutations.
Comparisons using Struct parameters are limited to <a href="https://cloud.google.com/spanner/docs/data-types#limited-comparisons-for-struct">what is available with Cloud Spanner</a>.</p>
</div>
<div class="sect3">
<h4 id="_query_methods_by_convention"><a class="link" href="#_query_methods_by_convention">13.5.1. Query methods by convention</a></h4>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TradeRepository extends SpannerRepository&lt;Trade, String[]&gt; {
List&lt;Trade&gt; findByAction(String action);
int countByAction(String action);
// Named methods are powerful, but can get unwieldy
List&lt;Trade&gt; findTop3DistinctByActionAndSymbolIgnoreCaseOrTraderIdOrderBySymbolDesc(
String action, String symbol, String traderId);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the example above, the <a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories.query-methods">query methods</a> in <code>TradeRepository</code> are generated based on the name of the methods, using the <a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html#repositories.query-methods.query-creation">Spring Data Query creation naming convention</a>.</p>
</div>
<div class="paragraph">
<p><code>List&lt;Trade&gt; findByAction(String action)</code> would translate to a <code>SELECT * FROM trades WHERE action = ?</code>.</p>
</div>
<div class="paragraph">
<p>The function <code>List&lt;Trade&gt; findTop3DistinctByActionAndSymbolIgnoreCaseOrTraderIdOrderBySymbolDesc(String action, String symbol, String traderId);</code> will be translated as the equivalent of this SQL query:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-sql hljs" data-lang="sql">SELECT DISTINCT * FROM trades
WHERE ACTION = ? AND LOWER(SYMBOL) = LOWER(?) AND TRADER_ID = ?
ORDER BY SYMBOL DESC
LIMIT 3</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following filter options are supported:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Equality</p>
</li>
<li>
<p>Greater than or equals</p>
</li>
<li>
<p>Greater than</p>
</li>
<li>
<p>Less than or equals</p>
</li>
<li>
<p>Less than</p>
</li>
<li>
<p>Is null</p>
</li>
<li>
<p>Is not null</p>
</li>
<li>
<p>Is true</p>
</li>
<li>
<p>Is false</p>
</li>
<li>
<p>Like a string</p>
</li>
<li>
<p>Not like a string</p>
</li>
<li>
<p>Contains a string</p>
</li>
<li>
<p>Not contains a string</p>
</li>
<li>
<p>In</p>
</li>
<li>
<p>Not in</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Note that the phrase <code>SymbolIgnoreCase</code> is translated to <code>LOWER(SYMBOL) = LOWER(?)</code> indicating a non-case-sensitive matching.
The <code>IgnoreCase</code> phrase may only be appended to fields that correspond to columns of type STRING or BYTES.
The Spring Data "AllIgnoreCase" phrase appended at the end of the method name is not supported.</p>
</div>
<div class="paragraph">
<p>The <code>Like</code> or <code>NotLike</code> naming conventions:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;Trade&gt; findBySymbolLike(String symbolFragment);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The param <code>symbolFragment</code> can contain <a href="https://cloud.google.com/spanner/docs/functions-and-operators#comparison-operators">wildcard characters</a> for string matching such as <code>_</code> and <code>%</code>.</p>
</div>
<div class="paragraph">
<p>The <code>Contains</code> and <code>NotContains</code> naming conventions:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">List&lt;Trade&gt; findBySymbolContains(String symbolFragment);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The param <code>symbolFragment</code> is a <a href="https://cloud.google.com/spanner/docs/functions-and-operators#regexp_contains">regular expression</a> that is checked for occurrences.</p>
</div>
<div class="paragraph">
<p>The <code>In</code> and <code>NotIn</code> keywords must be used with <code>Iterable</code> corresponding parameters.</p>
</div>
<div class="paragraph">
<p>Delete queries are also supported.
For example, query methods such as <code>deleteByAction</code> or <code>removeByAction</code> delete entities found by <code>findByAction</code>.
The delete operation happens in a single transaction.</p>
</div>
<div class="paragraph">
<p>Delete queries can have the following return types:
* An integer type that is the number of entities deleted
* A collection of entities that were deleted
* <code>void</code></p>
</div>
</div>
<div class="sect3">
<h4 id="_custom_sqldml_query_methods"><a class="link" href="#_custom_sqldml_query_methods">13.5.2. Custom SQL/DML query methods</a></h4>
<div class="paragraph">
<p>The example above for <code>List&lt;Trade&gt; fetchByActionNamedQuery(String action)</code> does not match the <a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html#repositories.query-methods.query-creation">Spring Data Query creation naming convention</a>, so we have to map a parametrized Spanner SQL query to it.</p>
</div>
<div class="paragraph">
<p>The SQL query for the method can be mapped to repository methods in one of two ways:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>namedQueries</code> properties file</p>
</li>
<li>
<p>using the <code>@Query</code> annotation</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The names of the tags of the SQL correspond to the <code>@Param</code> annotated names of the method parameters.</p>
</div>
<div class="paragraph">
<p>Custom SQL query methods can accept a single <code>Sort</code> or <code>Pageable</code> parameter that is applied on top of any sorting or paging in the SQL:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> @Query("SELECT * FROM trades ORDER BY action DESC")
List&lt;Trade&gt; sortedTrades(Pageable pageable);
@Query("SELECT * FROM trades ORDER BY action DESC LIMIT 1")
Trade sortedTopTrade(Pageable pageable);</code></pre>
</div>
</div>
<div class="paragraph">
<p>This can be used:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> List&lt;Trade&gt; customSortedTrades = tradeRepository.sortedTrades(PageRequest
.of(2, 2, org.springframework.data.domain.Sort.by(Order.asc("id"))));</code></pre>
</div>
</div>
<div class="paragraph">
<p>The results would be sorted by "id" in ascending order.</p>
</div>
<div class="paragraph">
<p>Your query method can also return non-entity types:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> @Query("SELECT COUNT(1) FROM trades WHERE action = @action")
int countByActionQuery(String action);
@Query("SELECT EXISTS(SELECT COUNT(1) FROM trades WHERE action = @action)")
boolean existsByActionQuery(String action);
@Query("SELECT action FROM trades WHERE action = @action LIMIT 1")
String getFirstString(@Param("action") String action);
@Query("SELECT action FROM trades WHERE action = @action")
List&lt;String&gt; getFirstStringList(@Param("action") String action);</code></pre>
</div>
</div>
<div class="paragraph">
<p>DML statements can also be executed by query methods, but the only possible return value is a <code>long</code> representing the number of affected rows.
The <code>dmlStatement</code> boolean setting must be set on <code>@Query</code> to indicate that the query method is executed as a DML statement.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> @Query(value = "DELETE FROM trades WHERE action = @action", dmlStatement = true)
long deleteByActionQuery(String action);</code></pre>
</div>
</div>
<div class="sect4">
<h5 id="_query_methods_with_named_queries_properties"><a class="link" href="#_query_methods_with_named_queries_properties">Query methods with named queries properties</a></h5>
<div class="paragraph">
<p>By default, the <code>namedQueriesLocation</code> attribute on <code>@EnableSpannerRepositories</code> points to the <code>META-INF/spanner-named-queries.properties</code> file.
You can specify the query for a method in the properties file by providing the SQL as the value for the "interface.method" property:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-properties hljs" data-lang="properties">Trade.fetchByActionNamedQuery=SELECT * FROM trades WHERE trades.action = @tag0</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TradeRepository extends SpannerRepository&lt;Trade, String[]&gt; {
// This method uses the query from the properties file instead of one generated based on name.
List&lt;Trade&gt; fetchByActionNamedQuery(@Param("tag0") String action);
}</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_query_methods_with_annotation"><a class="link" href="#_query_methods_with_annotation">Query methods with annotation</a></h5>
<div class="paragraph">
<p>Using the <code>@Query</code> annotation:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TradeRepository extends SpannerRepository&lt;Trade, String[]&gt; {
@Query("SELECT * FROM trades WHERE trades.action = @tag0")
List&lt;Trade&gt; fetchByActionNamedQuery(@Param("tag0") String action);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Table names can be used directly.
For example, "trades" in the above example.
Alternatively, table names can be resolved from the <code>@Table</code> annotation on domain classes as well.
In this case, the query should refer to table names with fully qualified class names between <code>:</code>
characters: <code>:fully.qualified.ClassName:</code>.
A full example would look like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Query("SELECT * FROM :com.example.Trade: WHERE trades.action = @tag0")
List&lt;Trade&gt; fetchByActionNamedQuery(String action);</code></pre>
</div>
</div>
<div class="paragraph">
<p>This allows table names evaluated with SpEL to be used in custom queries.</p>
</div>
<div class="paragraph">
<p>SpEL can also be used to provide SQL parameters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Query("SELECT * FROM :com.example.Trade: WHERE trades.action = @tag0
AND price &gt; #{#priceRadius * -1} AND price &lt; #{#priceRadius * 2}")
List&lt;Trade&gt; fetchByActionNamedQuery(String action, Double priceRadius);</code></pre>
</div>
</div>
<div class="paragraph">
<p>When using the <code>IN</code> SQL clause, remember to use <code>IN UNNEST(@iterableParam)</code> to specify a single <code>Iterable</code> parameter.
You can also use a fixed number of singular parameters such as <code>IN (@stringParam1, @stringParam2)</code>.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_projections"><a class="link" href="#_projections">13.5.3. Projections</a></h4>
<div class="paragraph">
<p>Spring Data Spanner supports <a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#projections">projections</a>.
You can define projection interfaces based on domain types and add query methods that return them in your repository:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TradeProjection {
String getAction();
@Value("#{target.symbol + ' ' + target.action}")
String getSymbolAndAction();
}
public interface TradeRepository extends SpannerRepository&lt;Trade, Key&gt; {
List&lt;Trade&gt; findByTraderId(String traderId);
List&lt;TradeProjection&gt; findByAction(String action);
@Query("SELECT action, symbol FROM trades WHERE action = @action")
List&lt;TradeProjection&gt; findByQuery(String action);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Projections can be provided by name-convention-based query methods as well as by custom SQL queries.
If using custom SQL queries, you can further restrict the columns retrieved from Spanner to just those required by the projection to improve performance.</p>
</div>
<div class="paragraph">
<p>Properties of projection types defined using SpEL use the fixed name <code>target</code> for the underlying domain object.
As a result accessing underlying properties take the form <code>target.&lt;property-name&gt;</code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_rest_repositories"><a class="link" href="#_rest_repositories">13.5.4. REST Repositories</a></h4>
<div class="paragraph">
<p>When running with Spring Boot, repositories can be exposed as REST services by simply adding this dependency to your pom file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-data-rest&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you prefer to configure parameters (such as path), you can use <code>@RepositoryRestResource</code> annotation:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RepositoryRestResource(collectionResourceRel = "trades", path = "trades")
public interface TradeRepository extends SpannerRepository&lt;Trade, String[]&gt; {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For example, you can retrieve all <code>Trade</code> objects in the repository by using <code>curl http://&lt;server&gt;:&lt;port&gt;/trades</code>, or any specific trade via <code>curl http://&lt;server&gt;:&lt;port&gt;/trades/&lt;trader_id&gt;,&lt;trade_id&gt;</code>.</p>
</div>
<div class="paragraph">
<p>The separator between your primary key components, <code>id</code> and <code>trader_id</code> in this case, is a comma by default, but can be configured to any string not found in your key values by extending the <code>SpannerKeyIdConverter</code> class:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Component
class MySpecialIdConverter extends SpannerKeyIdConverter {
@Override
protected String getUrlIdSeparator() {
return ":";
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also write trades using <code>curl -XPOST -H"Content-Type: application/json" -<a href="mailto:d@test.json">d@test.json</a> http://&lt;server&gt;:&lt;port&gt;/trades/</code> where the file <code>test.json</code> holds the JSON representation of a <code>Trade</code> object.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_database_and_schema_admin"><a class="link" href="#_database_and_schema_admin">13.6. Database and Schema Admin</a></h3>
<div class="paragraph">
<p>Databases and tables inside Spanner instances can be created automatically from <code>SpannerPersistentEntity</code> objects:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
private SpannerSchemaUtils spannerSchemaUtils;
@Autowired
private SpannerDatabaseAdminTemplate spannerDatabaseAdminTemplate;
public void createTable(SpannerPersistentEntity entity) {
if(!spannerDatabaseAdminTemplate.tableExists(entity.tableName()){
// The boolean parameter indicates that the database will be created if it does not exist.
spannerDatabaseAdminTemplate.executeDdlStrings(Arrays.asList(
spannerSchemaUtils.getCreateTableDDLString(entity.getType())), true);
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Schemas can be generated for entire object hierarchies with interleaved relationships and composite keys.</p>
</div>
</div>
<div class="sect2">
<h3 id="_events"><a class="link" href="#_events">13.7. Events</a></h3>
<div class="paragraph">
<p>Spring Data Cloud Spanner publishes events extending the Spring Framework&#8217;s <code>ApplicationEvent</code> to the context that can be received by <code>ApplicationListener</code> beans you register.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Contents</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterReadEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after entities are read by key from Cloud Spanner by <code>SpannerTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The entities loaded. The read options and key-set originally specified for the load operation.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterQueryEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after entities are read by query from Cloud Spanner by <code>SpannerTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The entities loaded. The query options and query statement originally specified for the load operation.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>BeforeExecuteDmlEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately before DML statements are executed by <code>SpannerTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The DML statement to execute.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterExecuteDmlEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after DML statements are executed by <code>SpannerTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The DML statement to execute and the number of rows affected by the operation as reported by Cloud Spanner.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>BeforeSaveEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately before upsert/update/insert operations are executed by <code>SpannerTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The mutations to be sent to Cloud Spanner, the entities to be saved, and optionally the properties in those entities to save.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterSaveEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after upsert/update/insert operations are executed by <code>SpannerTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The mutations sent to Cloud Spanner, the entities to be saved, and optionally the properties in those entities to save.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>BeforeDeleteEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately before delete operations are executed by <code>SpannerTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The mutations to be sent to Cloud Spanner. The target entities, keys, or entity type originally specified for the delete operation.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterDeleteEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after delete operations are executed by <code>SpannerTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The mutations sent to Cloud Spanner. The target entities, keys, or entity type originally specified for the delete operation.</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_auditing"><a class="link" href="#_auditing">13.8. Auditing</a></h3>
<div class="paragraph">
<p>Spring Data Cloud Spanner supports the <code>@LastModifiedDate</code> and <code>@LastModifiedBy</code> auditing annotations for properties:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Table
public class SimpleEntity {
@PrimaryKey
String id;
@LastModifiedBy
String lastUser;
@LastModifiedDate
DateTime lastTouched;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Upon insert, update, or save, these properties will be set automatically by the framework before mutations are generated and saved to Cloud Spanner.</p>
</div>
<div class="paragraph">
<p>To take advantage of these features, add the <code>@EnableSpannerAuditing</code> annotation to your configuration class and provide a bean for an <code>AuditorAware&lt;A&gt;</code> implementation where the type <code>A</code> is the desired property type annotated by <code>@LastModifiedBy</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
@EnableSpannerAuditing
public class Config {
@Bean
public AuditorAware&lt;String&gt; auditorProvider() {
return () -&gt; Optional.of("YOUR_USERNAME_HERE");
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>AuditorAware</code> interface contains a single method that supplies the value for fields annotated by <code>@LastModifiedBy</code> and can be of any type.
One alternative is to use Spring Security&#8217;s <code>User</code> type:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">class SpringSecurityAuditorAware implements AuditorAware&lt;User&gt; {
public Optional&lt;User&gt; getCurrentAuditor() {
return Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getPrincipal)
.map(User.class::cast);
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also set a custom provider for properties annotated <code>@LastModifiedDate</code> by providing a bean for <code>DateTimeProvider</code> and providing the bean name to <code>@EnableSpannerAuditing(dateTimeProviderRef = "customDateTimeProviderBean")</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_multi_instance_usage"><a class="link" href="#_multi_instance_usage">13.9. Multi-Instance Usage</a></h3>
<div class="paragraph">
<p>Your application can be configured to use multiple Cloud Spanner instances or databases by providing a custom bean for <code>DatabaseIdProvider</code>.
The default bean uses the instance ID, database name, and project ID options you configured in <code>application.properties</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> @Bean
public DatabaseIdProvider databaseIdProvider() {
// return custom connection options provider
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>DatabaseId</code> given by this provider is used as the target database name and instance of each operation Spring Data Cloud Spanner executes.
By providing a custom implementation of this bean (for example, supplying a thread-local <code>DatabaseId</code>), you can direct your application to use multiple instances or databases.</p>
</div>
<div class="paragraph">
<p>Database administrative operations, such as creating tables using <code>SpannerDatabaseAdminTemplate</code>, will also utilize the provided <code>DatabaseId</code>.</p>
</div>
<div class="paragraph">
<p>If you would like to configure every aspect of each connection (such as pool size and retry settings), you can supply a bean for <code>Supplier&lt;DatabaseClient&gt;</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_sample_9"><a class="link" href="#_sample_9">13.10. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-spanner-sample">sample application</a> is available.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_data_cloud_datastore"><a class="link" href="#_spring_data_cloud_datastore">14. Spring Data Cloud Datastore</a></h2>
<div class="sectionbody">
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
This integration is fully compatible with <a href="https://cloud.google.com/datastore/docs/">Firestore in Datastore Mode</a>, but not with Firestore in Native Mode.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p><a href="https://projects.spring.io/spring-data/">Spring Data</a> is an abstraction for storing and retrieving POJOs in numerous storage technologies.
Spring Cloud GCP adds Spring Data support for <a href="https://cloud.google.com/firestore/">Google Cloud Firestore</a> in Datastore mode.</p>
</div>
<div class="paragraph">
<p>Maven coordinates for this module only, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-data-datastore&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-data-datastore'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We provide a <a href="../spring-cloud-gcp-starters/spring-cloud-gcp-starter-data-datastore">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>
</div>
<div class="paragraph">
<p>Maven:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-data-datastore&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-datastore'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This setup takes care of bringing in the latest compatible version of Cloud Java Cloud Datastore libraries as well.</p>
</div>
<div class="sect2">
<h3 id="_configuration_5"><a class="link" href="#_configuration_5">14.1. Configuration</a></h3>
<div class="paragraph">
<p>To setup Spring Data Cloud Datastore, you have to configure the following:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Setup the connection details to Google Cloud Datastore.</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_cloud_datastore_settings"><a class="link" href="#_cloud_datastore_settings">14.1.1. Cloud Datastore settings</a></h4>
<div class="paragraph">
<p>You can the use <a href="../spring-cloud-gcp-starters/spring-cloud-gcp-starter-data-datastore">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>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables the Cloud Datastore client</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.project-id</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GCP project ID where the Google Cloud Datastore API is hosted, if different from the one in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.credentials.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">OAuth2 credentials for authenticating with the Google Cloud Datastore API, if different from the ones in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.credentials.encoded-key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Base64-encoded OAuth2 credentials for authenticating with the Google Cloud Datastore API, if different from the ones in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.credentials.scopes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://developers.google.com/identity/protocols/googlescopes">OAuth2 scope</a> for Spring Cloud GCP Cloud Datastore credentials</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/datastore" class="bare">https://www.googleapis.com/auth/datastore</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.namespace</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Cloud Datastore namespace to use</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">the Default namespace of Cloud Datastore in your GCP project</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.host</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The <code>hostname:port</code> of the datastore service or emulator to connect to. Can be used to connect to a manually started <a href="https://cloud.google.com/datastore/docs/tools/datastore-emulator">Datastore Emulator</a>. If the autoconfigured emulator is enabled, this property will be ignored and <code>localhost:&lt;emulator_port&gt;</code> will be used.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.emulator.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">To enable the auto configuration to start a local instance of the Datastore Emulator.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.emulator.port</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The local port to use for the Datastore Emulator</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>8081</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.datastore.emulator.consistency</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The <a href="https://cloud.google.com/sdk/gcloud/reference/beta/emulators/datastore/start?#--consistency">consistency</a> to use for the Datastore Emulator instance</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>0.9</code></p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect3">
<h4 id="_repository_settings_2"><a class="link" href="#_repository_settings_2">14.1.2. Repository settings</a></h4>
<div class="paragraph">
<p>Spring Data Repositories can be configured via the <code>@EnableDatastoreRepositories</code> annotation on your main <code>@Configuration</code> class.
With our Spring Boot Starter for Spring Data Cloud Datastore, <code>@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 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"><code>@EnableDatastoreRepositories</code></a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_autoconfiguration_2"><a class="link" href="#_autoconfiguration_2">14.1.3. Autoconfiguration</a></h4>
<div class="paragraph">
<p>Our Spring Boot autoconfiguration creates the following beans available in the Spring application context:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>an instance of <code>DatastoreTemplate</code></p>
</li>
<li>
<p>an instance of all user defined repositories extending <code>CrudRepository</code>, <code>PagingAndSortingRepository</code>, and <code>DatastoreRepository</code> (an extension of <code>PagingAndSortingRepository</code> with additional Cloud Datastore features) when repositories are enabled</p>
</li>
<li>
<p>an instance of <code>Datastore</code> from the Google Cloud Java Client for Datastore, for convenience and lower level API access</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_datastore_emulator_autoconfiguration"><a class="link" href="#_datastore_emulator_autoconfiguration">14.1.4. Datastore Emulator Autoconfiguration</a></h4>
<div class="paragraph">
<p>This Spring Boot autoconfiguration can also configure and start a local Datastore Emulator server if enabled by property.</p>
</div>
<div class="paragraph">
<p>It is useful for integration testing, but not for production.</p>
</div>
<div class="paragraph">
<p>When enabled, the <code>spring.cloud.gcp.datastore.host</code> property will be ignored and the Datastore autoconfiguration itself will be forced to connect to the autoconfigured local emulator instance.</p>
</div>
<div class="paragraph">
<p>It will create an instance of <code>LocalDatastoreHelper</code> as a bean that stores the <code>DatastoreOptions</code> to get the <code>Datastore</code> client connection to the emulator for convenience and lower level API for local access.
The emulator will be properly stopped after the Spring application context shutdown.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_object_mapping_2"><a class="link" href="#_object_mapping_2">14.2. Object Mapping</a></h3>
<div class="paragraph">
<p>Spring Data Cloud Datastore allows you to map domain POJOs to Cloud Datastore kinds and entities via annotations:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Entity(name = "traders")
public class Trader {
@Id
@Field(name = "trader_id")
String traderId;
String firstName;
String lastName;
@Transient
Double temporaryNumber;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Spring Data Cloud Datastore will ignore any property annotated with <code>@Transient</code>.
These properties will not be written to or read from Cloud Datastore.</p>
</div>
<div class="sect3">
<h4 id="_constructors_2"><a class="link" href="#_constructors_2">14.2.1. Constructors</a></h4>
<div class="paragraph">
<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>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Entity(name = "traders")
public class Trader {
@Id
@Field(name = "trader_id")
String traderId;
String firstName;
String lastName;
@Transient
Double temporaryNumber;
public Trader(String traderId, String firstName) {
this.traderId = traderId;
this.firstName = firstName;
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_kind"><a class="link" href="#_kind">14.2.2. Kind</a></h4>
<div class="paragraph">
<p>The <code>@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>
<div class="sect3">
<h4 id="_keys"><a class="link" href="#_keys">14.2.3. Keys</a></h4>
<div class="paragraph">
<p><code>@Id</code> identifies the property corresponding to the ID value.</p>
</div>
<div class="paragraph">
<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>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Entity(name = "trades")
public class Trade {
@Id
@Field(name = "trade_id")
String tradeId;
@Field(name = "trader_id")
String traderId;
String action;
Double price;
Double shares;
String symbol;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Datastore can automatically allocate integer ID values.
If a POJO instance with a <code>Long</code> ID property is written to Cloud Datastore with <code>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>long</code> ID properties cannot be <code>null</code> and default to <code>0</code>, keys will not be allocated.</p>
</div>
</div>
<div class="sect3">
<h4 id="_fields"><a class="link" href="#_fields">14.2.4. Fields</a></h4>
<div class="paragraph">
<p>All accessible properties on POJOs are automatically recognized as a Cloud Datastore field.
Field naming is generated by the <code>PropertyNameFieldNamingStrategy</code> by default defined on the <code>DatastoreMappingContext</code> bean.
The <code>@Field</code> annotation optionally provides a different field name than that of the property.</p>
</div>
</div>
<div class="sect3">
<h4 id="_supported_types_2"><a class="link" href="#_supported_types_2">14.2.5. Supported Types</a></h4>
<div class="paragraph">
<p>Spring Data Cloud Datastore supports the following types for regular fields and elements of collections:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Stored as</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>com.google.cloud.Timestamp</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.TimestampValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>com.google.cloud.datastore.Blob</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.BlobValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>com.google.cloud.datastore.LatLng</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.LatLngValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>java.lang.Boolean</code>, <code>boolean</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.BooleanValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>java.lang.Double</code>, <code>double</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.DoubleValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>java.lang.Long</code>, <code>long</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.LongValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>java.lang.Integer</code>, <code>int</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.LongValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>java.lang.String</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.StringValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>com.google.cloud.datastore.Entity</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.EntityValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>com.google.cloud.datastore.Key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.KeyValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>byte[]</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.BlobValue</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Java <code>enum</code> values</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">com.google.cloud.datastore.StringValue</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>In addition, all types that can be converted to the ones listed in the table by
<code>org.springframework.core.convert.support.DefaultConversionService</code> are supported.</p>
</div>
</div>
<div class="sect3">
<h4 id="_custom_types_2"><a class="link" href="#_custom_types_2">14.2.6. Custom types</a></h4>
<div class="paragraph">
<p>Custom converters can be used extending the type support for user defined types.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Converters need to implement the <code>org.springframework.core.convert.converter.Converter</code> interface in both directions.</p>
</li>
<li>
<p>The user defined type needs to be mapped to one of the basic types supported by Cloud Datastore.</p>
</li>
<li>
<p>An instance of both Converters (read and write) needs to be passed to the <code>DatastoreCustomConversions</code> constructor, which then has to be made available as a <code>@Bean</code> for <code>DatastoreCustomConversions</code>.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="paragraph">
<p>We would like to have a field of type <code>Album</code> on our <code>Singer</code> POJO and want it to be stored as a string property:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Entity
public class Singer {
@Id
String singerId;
String name;
Album album;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Where Album is a simple class:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class Album {
String albumName;
LocalDate date;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We have to define the two converters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> //Converter to write custom Album type
static final Converter&lt;Album, String&gt; ALBUM_STRING_CONVERTER =
new Converter&lt;Album, String&gt;() {
@Override
public String convert(Album album) {
return album.getAlbumName() + " " + album.getDate().format(DateTimeFormatter.ISO_DATE);
}
};
//Converters to read custom Album type
static final Converter&lt;String, Album&gt; STRING_ALBUM_CONVERTER =
new Converter&lt;String, Album&gt;() {
@Override
public Album convert(String s) {
String[] parts = s.split(" ");
return new Album(parts[0], LocalDate.parse(parts[parts.length - 1], DateTimeFormatter.ISO_DATE));
}
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>That will be configured in our <code>@Configuration</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
public class ConverterConfiguration {
@Bean
public DatastoreCustomConversions datastoreCustomConversions() {
return new DatastoreCustomConversions(
Arrays.asList(
ALBUM_STRING_CONVERTER,
STRING_ALBUM_CONVERTER));
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_collections_and_arrays"><a class="link" href="#_collections_and_arrays">14.2.7. Collections and arrays</a></h4>
<div class="paragraph">
<p>Arrays and collections (types that implement <code>java.util.Collection</code>) of supported types are supported.
They are stored as <code>com.google.cloud.datastore.ListValue</code>.
Elements are converted to Cloud Datastore supported types individually. <code>byte[]</code> is an exception, it is converted to
<code>com.google.cloud.datastore.Blob</code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_custom_converter_for_collections"><a class="link" href="#_custom_converter_for_collections">14.2.8. Custom Converter for collections</a></h4>
<div class="paragraph">
<p>Users can provide converters from <code>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>
</div>
<div class="paragraph">
<p>Collection converters need to implement the <code>org.springframework.core.convert.converter.Converter</code> interface.</p>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="paragraph">
<p>Let&#8217;s improve the Singer class from the previous example.
Instead of a field of type <code>Album</code>, we would like to have a field of type <code>Set&lt;Album&gt;</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Entity
public class Singer {
@Id
String singerId;
String name;
Set&lt;Album&gt; albums;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We have to define a read converter only:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">static final Converter&lt;List&lt;?&gt;, Set&lt;?&gt;&gt; LIST_SET_CONVERTER =
new Converter&lt;List&lt;?&gt;, Set&lt;?&gt;&gt;() {
@Override
public Set&lt;?&gt; convert(List&lt;?&gt; source) {
return Collections.unmodifiableSet(new HashSet&lt;&gt;(source));
}
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>And add it to the list of custom converters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
public class ConverterConfiguration {
@Bean
public DatastoreCustomConversions datastoreCustomConversions() {
return new DatastoreCustomConversions(
Arrays.asList(
LIST_SET_CONVERTER,
ALBUM_STRING_CONVERTER,
STRING_ALBUM_CONVERTER));
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_inheritance_hierarchies"><a class="link" href="#_inheritance_hierarchies">14.2.9. Inheritance Hierarchies</a></h4>
<div class="paragraph">
<p>Java entity types related by inheritance can be stored in the same Kind.
When reading and querying entities using <code>DatastoreRepository</code> or <code>DatastoreTemplate</code> with a superclass as the type parameter, you can receive instances of subclasses if you annotate the superclass and its subclasses with <code>DiscriminatorField</code> and <code>DiscriminatorValue</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Entity(name = "pets")
@DiscriminatorField(field = "pet_type")
abstract class Pet {
@Id
Long id;
abstract String speak();
}
@DiscriminatorValue("cat")
class Cat extends Pet {
@Override
String speak() {
return "meow";
}
}
@DiscriminatorValue("dog")
class Dog extends Pet {
@Override
String speak() {
return "woof";
}
}
@DiscriminatorValue("pug")
class Pug extends Dog {
@Override
String speak() {
return "woof woof";
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Instances of all 3 types are stored in the <code>pets</code> Kind.
Because a single Kind is used, all classes in the hierarchy must share the same ID property and no two instances of any type in the hierarchy can share the same ID value.</p>
</div>
<div class="paragraph">
<p>Entity rows in Cloud Datastore store their respective types' <code>DiscriminatorValue</code> in a field specified by the root superclass&#8217;s <code>DiscriminatorField</code> (<code>pet_type</code> in this case).
Reads and queries using a given type parameter will match each entity with its specific type.
For example, reading a <code>List&lt;Pet&gt;</code> will produce a list containing instances of all 3 types.
However, reading a <code>List&lt;Dog&gt;</code> will produce a list containing only <code>Dog</code> and <code>Pug</code> instances.
You can include the <code>pet_type</code> discrimination field in your Java entities, but its type must be convertible to a collection or array of <code>String</code>.
Any value set in the discrimination field will be overwritten upon write to Cloud Datastore.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_relationships_2"><a class="link" href="#_relationships_2">14.3. Relationships</a></h3>
<div class="paragraph">
<p>There are three ways to represent relationships between entities that are described in this section:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Embedded entities stored directly in the field of the containing entity</p>
</li>
<li>
<p><code>@Descendant</code> annotated properties for one-to-many relationships</p>
</li>
<li>
<p><code>@Reference</code> annotated properties for general relationships without hierarchy</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_embedded_entities"><a class="link" href="#_embedded_entities">14.3.1. Embedded Entities</a></h4>
<div class="paragraph">
<p>Fields whose types are also annotated with <code>@Entity</code> are converted to <code>EntityValue</code> and stored inside the parent entity.</p>
</div>
<div class="paragraph">
<p>Here is an example of Cloud Datastore entity containing an embedded entity in JSON:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
"name" : "Alexander",
"age" : 47,
"child" : {"name" : "Philip" }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This corresponds to a simple pair of Java entities:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.cloud.gcp.data.datastore.core.mapping.Entity;
import org.springframework.data.annotation.Id;
@Entity("parents")
public class Parent {
@Id
String name;
Child child;
}
@Entity
public class Child {
String name;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>Child</code> entities are not stored in their own kind.
They are stored in their entirety in the <code>child</code> field of the <code>parents</code> kind.</p>
</div>
<div class="paragraph">
<p>Multiple levels of embedded entities are supported.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Embedded entities don&#8217;t need to have <code>@Id</code> field, it is only required for top level entities.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="paragraph">
<p>Entities can hold embedded entities that are their own type.
We can store trees in Cloud Datastore using this feature:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.cloud.gcp.data.datastore.core.mapping.Embedded;
import org.springframework.cloud.gcp.data.datastore.core.mapping.Entity;
import org.springframework.data.annotation.Id;
@Entity
public class EmbeddableTreeNode {
@Id
long value;
EmbeddableTreeNode left;
EmbeddableTreeNode right;
Map&lt;String, Long&gt; longValues;
Map&lt;String, List&lt;Timestamp&gt;&gt; listTimestamps;
public EmbeddableTreeNode(long value, EmbeddableTreeNode left, EmbeddableTreeNode right) {
this.value = value;
this.left = left;
this.right = right;
}
}</code></pre>
</div>
</div>
<div class="sect4">
<h5 id="_maps"><a class="link" href="#_maps">Maps</a></h5>
<div class="paragraph">
<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>
</div>
<div class="paragraph">
<p>Also, a collection of entities can be embedded; it will be converted to <code>ListValue</code> on write.</p>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="paragraph">
<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>List&lt;EmbeddableTreeNode&gt;</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.cloud.gcp.data.datastore.core.mapping.Embedded;
import org.springframework.data.annotation.Id;
public class EmbeddableTreeNode {
@Id
long value;
List&lt;EmbeddableTreeNode&gt; children;
Map&lt;String, EmbeddableTreeNode&gt; siblingNodes;
Map&lt;String, Set&lt;EmbeddableTreeNode&gt;&gt; subNodeGroups;
public EmbeddableTreeNode(List&lt;EmbeddableTreeNode&gt; children) {
this.children = children;
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Because Maps are stored as entities, they can further hold embedded entities:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Singular embedded objects in the value can be stored in the values of embedded Maps.</p>
</li>
<li>
<p>Collections of embedded objects in the value can also be stored as the values of embedded Maps.</p>
</li>
<li>
<p>Maps in the value are further stored as embedded entities with the same rules applied recursively for their values.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_ancestor_descendant_relationships"><a class="link" href="#_ancestor_descendant_relationships">14.3.2. Ancestor-Descendant Relationships</a></h4>
<div class="paragraph">
<p>Parent-child relationships are supported via the <code>@Descendants</code> annotation.</p>
</div>
<div class="paragraph">
<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>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.cloud.gcp.data.datastore.core.mapping.Descendants;
import org.springframework.cloud.gcp.data.datastore.core.mapping.Entity;
import org.springframework.data.annotation.Id;
@Entity("orders")
public class ShoppingOrder {
@Id
long id;
@Descendants
List&lt;Item&gt; items;
}
@Entity("purchased_item")
public class Item {
@Id
Key purchasedItemKey;
String name;
Timestamp timeAddedToOrder;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For example, an instance of a GQL key-literal representation for <code>Item</code> would also contain the parent <code>ShoppingOrder</code> ID value:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Key(orders, '12345', purchased_item, 'eggs')</pre>
</div>
</div>
<div class="paragraph">
<p>The GQL key-literal representation for the parent <code>ShoppingOrder</code> would be:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Key(orders, '12345')</pre>
</div>
</div>
<div class="paragraph">
<p>The Cloud Datastore entities exist separately in their own kinds.</p>
</div>
<div class="paragraph">
<p>The <code>ShoppingOrder</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>{
"id" : 12345
}</pre>
</div>
</div>
<div class="paragraph">
<p>The two items inside that order:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>{
"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>
</div>
</div>
<div class="paragraph">
<p>The parent-child relationship structure of objects is stored in Cloud Datastore using Datastore&#8217;s <a href="https://cloud.google.com/datastore/docs/concepts/entities#ancestor_paths">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>
</div>
<div class="paragraph">
<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>List</code>, arrays, <code>Set</code>, etc&#8230;&#8203;
Child items must have <code>Key</code> as their ID type because Cloud Datastore stores the ancestor relationship link inside the keys of the children.</p>
</div>
<div class="paragraph">
<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>@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>
</div>
<div class="paragraph">
<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>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>
<div class="sect3">
<h4 id="_key_reference_relationships"><a class="link" href="#_key_reference_relationships">14.3.3. Key Reference Relationships</a></h4>
<div class="paragraph">
<p>General relationships can be stored using the <code>@Reference</code> annotation.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.data.annotation.Reference;
import org.springframework.data.annotation.Id;
@Entity
public class ShoppingOrder {
@Id
long id;
@Reference
List&lt;Item&gt; items;
@Reference
Item specialSingleItem;
}
@Entity
public class Item {
@Id
Key purchasedItemKey;
String name;
Timestamp timeAddedToOrder;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>@Reference</code> relationships are between fully-formed entities residing in their own kinds.
The relationship between <code>ShoppingOrder</code> and <code>Item</code> entities are stored as a Key field inside <code>ShoppingOrder</code>, which are resolved to the underlying Java entity type by Spring Data Cloud Datastore:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>{
"id" : 12345,
"specialSingleItem" : Key(item, "milk"),
"items" : [ Key(item, "eggs"), Key(item, "sausage") ]
}</pre>
</div>
</div>
<div class="paragraph">
<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>
</div>
<div class="paragraph">
<p>Similar to the <code>@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>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>
<div class="sect2">
<h3 id="_datastore_operations_template"><a class="link" href="#_datastore_operations_template">14.4. Datastore Operations &amp; Template</a></h3>
<div class="paragraph">
<p><code>DatastoreOperations</code> and its implementation, <code>DatastoreTemplate</code>, provides the Template pattern familiar to Spring developers.</p>
</div>
<div class="paragraph">
<p>Using the auto-configuration provided by Spring Boot Starter for Datastore, your Spring application context will contain a fully configured <code>DatastoreTemplate</code> object that you can autowire in your application:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@SpringBootApplication
public class DatastoreTemplateExample {
@Autowired
DatastoreTemplate datastoreTemplate;
public void doSomething() {
this.datastoreTemplate.deleteAll(Trader.class);
//...
Trader t = new Trader();
//...
this.datastoreTemplate.save(t);
//...
List&lt;Trader&gt; traders = datastoreTemplate.findAll(Trader.class);
//...
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Template API provides convenience methods for:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Write operations (saving and deleting)</p>
</li>
<li>
<p>Read-write transactions</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_gql_query"><a class="link" href="#_gql_query">14.4.1. GQL Query</a></h4>
<div class="paragraph">
<p>In addition to retrieving entities by their IDs, you can also submit queries.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> &lt;T&gt; Iterable&lt;T&gt; query(Query&lt;? extends 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);</code></pre>
</div>
</div>
<div class="paragraph">
<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>
<div class="sect3">
<h4 id="_find_by_ids"><a class="link" href="#_find_by_ids">14.4.2. Find by ID(s)</a></h4>
<div class="paragraph">
<p>Using <code>DatastoreTemplate</code> you can find entities by id. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Trader trader = this.datastoreTemplate.findById("trader1", Trader.class);
List&lt;Trader&gt; traders = this.datastoreTemplate.findAllById(Arrays.asList("trader1", "trader2"), Trader.class);
List&lt;Trader&gt; allTraders = this.datastoreTemplate.findAll(Trader.class);</code></pre>
</div>
</div>
<div class="paragraph">
<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>Trader</code>.</p>
</div>
<div class="sect4">
<h5 id="_indexes"><a class="link" href="#_indexes">Indexes</a></h5>
<div class="paragraph">
<p>By default, all fields are indexed.
To disable indexing on a particular field, <code>@Unindexed</code> annotation can be used.</p>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.cloud.gcp.data.datastore.core.mapping.Unindexed;
public class ExampleItem {
long indexedField;
@Unindexed
long unindexedField;
@Unindexed
List&lt;String&gt; unindexedListField;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>When using queries directly or via Query Methods, Cloud Datastore requires <a href="https://cloud.google.com/datastore/docs/concepts/indexes">composite custom indexes</a> if the select statement is not <code>SELECT *</code> or if there is more than one filtering condition in the <code>WHERE</code> clause.</p>
</div>
</div>
<div class="sect4">
<h5 id="_read_with_offsets_limits_and_sorting"><a class="link" href="#_read_with_offsets_limits_and_sorting">Read with offsets, limits, and sorting</a></h5>
<div class="paragraph">
<p><code>DatastoreRepository</code> and custom-defined entity repositories implement the Spring Data <code>PagingAndSortingRepository</code>, which supports offsets and limits using page numbers and page sizes.
Paging and sorting options are also supported in <code>DatastoreTemplate</code> by supplying a <code>DatastoreQueryOptions</code> to <code>findAll</code>.</p>
</div>
</div>
<div class="sect4">
<h5 id="_partial_read_2"><a class="link" href="#_partial_read_2">Partial read</a></h5>
<div class="paragraph">
<p>This feature is not supported yet.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_write_update_2"><a class="link" href="#_write_update_2">14.4.3. Write / Update</a></h4>
<div class="paragraph">
<p>The write methods of <code>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>
</div>
<div class="paragraph">
<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>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Trader t = new Trader();
this.datastoreTemplate.save(t);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>save</code> method behaves as update-or-insert.</p>
</div>
<div class="sect4">
<h5 id="_partial_update_2"><a class="link" href="#_partial_update_2">Partial Update</a></h5>
<div class="paragraph">
<p>This feature is not supported yet.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_transactions_2"><a class="link" href="#_transactions_2">14.4.4. Transactions</a></h4>
<div class="paragraph">
<p>Read and write transactions are provided by <code>DatastoreOperations</code> via the <code>performTransaction</code> method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
DatastoreOperations myDatastoreOperations;
public String doWorkInsideTransaction() {
return myDatastoreOperations.performTransaction(
transactionDatastoreOperations -&gt; {
// Work with transactionDatastoreOperations here.
// It is also a DatastoreOperations object.
return "transaction completed";
}
);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>performTransaction</code> method accepts a <code>Function</code> that is provided an instance of a <code>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>DatastoreOperations</code> with an exception:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>It cannot perform sub-transactions.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Because of Cloud Datastore&#8217;s consistency guarantees, there are <a href="https://cloud.google.com/datastore/docs/concepts/transactions#what_can_be_done_in_a_transaction">limitations</a> to the operations and relationships among entities used inside transactions.</p>
</div>
<div class="sect4">
<h5 id="_declarative_transactions_with_transactional_annotation_2"><a class="link" href="#_declarative_transactions_with_transactional_annotation_2">Declarative Transactions with @Transactional Annotation</a></h5>
<div class="paragraph">
<p>This feature requires a bean of <code>DatastoreTransactionManager</code>, which is provided when using <code>spring-cloud-gcp-starter-data-datastore</code>.</p>
</div>
<div class="paragraph">
<p><code>DatastoreTemplate</code> and <code>DatastoreRepository</code> support running methods with the <code>@Transactional</code> <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative">annotation</a> as transactions.
If a method annotated with <code>@Transactional</code> calls another method also annotated, then both methods will work within the same transaction.
<code>performTransaction</code> cannot be used in <code>@Transactional</code> annotated methods because Cloud Datastore does not support transactions within transactions.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_read_write_support_for_maps"><a class="link" href="#_read_write_support_for_maps">14.4.5. Read-Write Support for Maps</a></h4>
<div class="paragraph">
<p>You can work with Maps of type <code>Map&lt;String, ?&gt;</code> instead of with entity objects by directly reading and writing them to and from Cloud Datastore.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
This is a different situation than using entity objects that contain Map properties.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<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 href="#_custom_types">Custom types</a> section).</p>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Map&lt;String, Long&gt; map = new HashMap&lt;&gt;();
map.put("field1", 1L);
map.put("field2", 2L);
map.put("field3", 3L);
keyForMap = datastoreTemplate.createKey("kindName", "id");
//write a map
datastoreTemplate.writeMap(keyForMap, map);
//read a map
Map&lt;String, Long&gt; loadedMap = datastoreTemplate.findByIdAsMap(keyForMap, Long.class);</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_repositories_2"><a class="link" href="#_repositories_2">14.5. Repositories</a></h3>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories">Spring Data Repositories</a> are an abstraction that can reduce boilerplate code.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TraderRepository extends DatastoreRepository&lt;Trader, String&gt; {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Spring Data generates a working implementation of the specified interface, which can be autowired into an application.</p>
</div>
<div class="paragraph">
<p>The <code>Trader</code> type parameter to <code>DatastoreRepository</code> refers to the underlying domain type.
The second type parameter, <code>String</code> in this case, refers to the type of the key of the domain type.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class MyApplication {
@Autowired
TraderRepository traderRepository;
public void demo() {
this.traderRepository.deleteAll();
String traderId = "demo_trader";
Trader t = new Trader();
t.traderId = traderId;
this.tradeRepository.save(t);
Iterable&lt;Trader&gt; allTraders = this.traderRepository.findAll();
int count = this.traderRepository.count();
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<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>
<div class="sect3">
<h4 id="_query_methods_by_convention_2"><a class="link" href="#_query_methods_by_convention_2">14.5.1. Query methods by convention</a></h4>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TradeRepository extends DatastoreRepository&lt;Trade, String[]&gt; {
List&lt;Trader&gt; findByAction(String action);
//throws an exception if no results
Trader findOneByAction(String action);
//because of the annotation, returns null if no results
@Nullable
Trader getByAction(String action);
Optional&lt;Trader&gt; getOneByAction(String action);
int countByAction(String action);
boolean existsByAction(String action);
List&lt;Trade&gt; findTop3ByActionAndSymbolAndPriceGreaterThanAndPriceLessThanOrEqualOrderBySymbolDesc(
String action, String symbol, double priceFloor, double 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);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the example above the <a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories.query-methods">query methods</a> in <code>TradeRepository</code> are generated based on the name of the methods using the <a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html#repositories.query-methods.query-creation">Spring Data Query creation naming convention</a>.</p>
</div>
<div class="paragraph">
<p>Cloud Datastore only supports filter components joined by AND, and the following operations:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>equals</code></p>
</li>
<li>
<p><code>greater than or equals</code></p>
</li>
<li>
<p><code>greater than</code></p>
</li>
<li>
<p><code>less than or equals</code></p>
</li>
<li>
<p><code>less than</code></p>
</li>
<li>
<p><code>is null</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<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>find</code> name-based query methods are run as <code>SELECT *</code>.</p>
</div>
<div class="paragraph">
<p>Delete queries are also supported.
For example, query methods such as <code>deleteByAction</code> or <code>removeByAction</code> delete entities found by <code>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>removeBy</code> and <code>deleteBy</code> name-convention query methods cannot be used inside transactions via either <code>performInTransaction</code> or <code>@Transactional</code> annotation.</p>
</div>
<div class="paragraph">
<p>Delete queries can have the following return types:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>An integer type that is the number of entities deleted</p>
</li>
<li>
<p>A collection of entities that were deleted</p>
</li>
<li>
<p>'void'</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Methods can have <code>org.springframework.data.domain.Pageable</code> parameter to control pagination and sorting, or <code>org.springframework.data.domain.Sort</code> parameter to control sorting only.
See <a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories.query-methods">Spring Data documentation</a> for details.</p>
</div>
<div class="paragraph">
<p>For returning multiple items in a repository method, we support Java collections as well as <code>org.springframework.data.domain.Page</code> and <code>org.springframework.data.domain.Slice</code>.
If a method&#8217;s return type is <code>org.springframework.data.domain.Page</code>, the returned object will include current page, total number of results and total number of pages.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Methods that return <code>Page</code> execute an additional query to compute total number of pages.
Methods that return <code>Slice</code>, on the other hand, don&#8217;t execute any additional queries and therefore are much more efficient.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_query_by_example"><a class="link" href="#_query_by_example">14.5.2. Query by example</a></h4>
<div class="paragraph">
<p>Query by Example is an alternative querying technique.
It enables dynamic query generation based on a user-provided object. See <a href="https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#query-by-example">Spring Data Documentation</a> for details.</p>
</div>
<div class="sect4">
<h5 id="_unsupported_features"><a class="link" href="#_unsupported_features">Unsupported features:</a></h5>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Currently, only equality queries are supported (no ignore-case matching, regexp matching, etc.).</p>
</li>
<li>
<p>Per-field matchers are not supported.</p>
</li>
<li>
<p>Embedded entities matching is not supported.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>For example, if you want to find all users with the last name "Smith", you would use the following code:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">userRepository.findAll(
Example.of(new User(null, null, "Smith"))</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>null</code> fields are not used in the filter by default. If you want to include them, you would use the following code:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">userRepository.findAll(
Example.of(new User(null, null, "Smith"), ExampleMatcher.matching().withIncludeNullValues())</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_custom_gql_query_methods"><a class="link" href="#_custom_gql_query_methods">14.5.3. Custom GQL query methods</a></h4>
<div class="paragraph">
<p>Custom GQL queries can be mapped to repository methods in one of two ways:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>namedQueries</code> properties file</p>
</li>
<li>
<p>using the <code>@Query</code> annotation</p>
</li>
</ul>
</div>
<div class="sect4">
<h5 id="_query_methods_with_annotation_2"><a class="link" href="#_query_methods_with_annotation_2">Query methods with annotation</a></h5>
<div class="paragraph">
<p>Using the <code>@Query</code> annotation:</p>
</div>
<div class="paragraph">
<p>The names of the tags of the GQL correspond to the <code>@Param</code> annotated names of the method parameters.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TraderRepository extends DatastoreRepository&lt;Trader, String&gt; {
@Query("SELECT * FROM traders WHERE name = @trader_name")
List&lt;Trader&gt; tradersByName(@Param("trader_name") String traderName);
@Query("SELECT * FROM test_entities_ci WHERE name = @trader_name")
TestEntity getOneTestEntity(@Param("trader_name") String traderName);
@Query("SELECT * FROM traders WHERE name = @trader_name")
List&lt;Trader&gt; tradersByNameSort(@Param("trader_name") String traderName, Sort sort);
@Query("SELECT * FROM traders WHERE name = @trader_name")
Slice&lt;Trader&gt; tradersByNameSlice(@Param("trader_name") String traderName, Pageable pageable);
@Query("SELECT * FROM traders WHERE name = @trader_name")
Page&lt;Trader&gt; tradersByNamePage(@Param("trader_name") String traderName, Pageable pageable);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>When the return type is <code>Slice</code> or <code>Pageable</code>, the result set cursor that points to the position just after the page is preserved in the returned <code>Slice</code> or <code>Page</code> object. To take advantage of the cursor to query for the next page or slice, use <code>result.getPageable().next()</code>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<code>Page</code> requires the total count of entities produced by the query. Therefore, the first query will have to retrieve all of the records just to count them. Instead, we recommend using the <code>Slice</code> return type, because it does not require an additional count query.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> Slice&lt;Trader&gt; slice1 = tradersByNamePage("Dave", PageRequest.of(0, 5));
Slice&lt;Trader&gt; slice2 = tradersByNamePage("Dave", slice1.getPageable().next());</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
You cannot use these Query Methods in repositories where the type parameter is a subclass of another class
annotated with <code>DiscriminatorField</code>.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The following parameter types are supported:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>com.google.cloud.Timestamp</code></p>
</li>
<li>
<p><code>com.google.cloud.datastore.Blob</code></p>
</li>
<li>
<p><code>com.google.cloud.datastore.Key</code></p>
</li>
<li>
<p><code>com.google.cloud.datastore.Cursor</code></p>
</li>
<li>
<p><code>java.lang.Boolean</code></p>
</li>
<li>
<p><code>java.lang.Double</code></p>
</li>
<li>
<p><code>java.lang.Long</code></p>
</li>
<li>
<p><code>java.lang.String</code></p>
</li>
<li>
<p><code>enum</code> values.
These are queried as <code>String</code> values.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>With the exception of <code>Cursor</code>, array forms of each of the types are also supported.</p>
</div>
<div class="paragraph">
<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>count = true</code> or <code>exists = true</code> properties of the <code>@Query</code> annotation, respectively.
The return type of the query method in these cases should be an integer type or a boolean type.</p>
</div>
<div class="paragraph">
<p>Cloud Datastore provides provides the <code>SELECT __key__ FROM &#8230;&#8203;</code> special column for all kinds that retrieves the <code>Key</code> of each row.
Selecting this special <code>__key__</code> column is especially useful and efficient for <code>count</code> and <code>exists</code> queries.</p>
</div>
<div class="paragraph">
<p>You can also query for non-entity types:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Query(value = "SELECT __key__ from test_entities_ci")
List&lt;Key&gt; getKeys();
@Query(value = "SELECT __key__ from test_entities_ci limit 1")
Key getKey();</code></pre>
</div>
</div>
<div class="paragraph">
<p>In order to use <code>@Id</code> annotated fields in custom queries, use <code>__key__</code> keyword for the field name. The parameter type should be of <code>Key</code>, as in the following example.</p>
</div>
<div class="paragraph">
<p>Repository method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Query("select * from test_entities_ci where size = @size and __key__ = @id")
LinkedList&lt;TestEntity&gt; findEntities(@Param("size") long size, @Param("id") Key id);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Generate a key from id value using <code>DatastoreTemplate.createKey</code> method and use it as a parameter for the repository method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">this.testEntityRepository.findEntities(1L, datastoreTemplate.createKey(TestEntity.class, 1L))</code></pre>
</div>
</div>
<div class="paragraph">
<p>SpEL can be used to provide GQL parameters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Query("SELECT * FROM |com.example.Trade| WHERE trades.action = @act
AND price &gt; :#{#priceRadius * -1} AND price &lt; :#{#priceRadius * 2}")
List&lt;Trade&gt; fetchByActionNamedQuery(@Param("act") String action, @Param("priceRadius") Double r);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Kind names can be directly written in the GQL annotations.
Kind names can also be resolved from the <code>@Entity</code> annotation on domain classes.</p>
</div>
<div class="paragraph">
<p>In this case, the query should refer to table names with fully qualified class names surrounded by <code>|</code> characters: <code>|fully.qualified.ClassName|</code>.
This is useful when SpEL expressions appear in the kind name provided to the <code>@Entity</code> annotation.
For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Query("SELECT * FROM |com.example.Trade| WHERE trades.action = @act")
List&lt;Trade&gt; fetchByActionNamedQuery(@Param("act") String action);</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_query_methods_with_named_queries_properties_2"><a class="link" href="#_query_methods_with_named_queries_properties_2">Query methods with named queries properties</a></h5>
<div class="paragraph">
<p>You can also specify queries with Cloud Datastore parameter tags and SpEL expressions in properties files.</p>
</div>
<div class="paragraph">
<p>By default, the <code>namedQueriesLocation</code> attribute on <code>@EnableDatastoreRepositories</code> points to the <code>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>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
You cannot use these Query Methods in repositories where the type parameter is a subclass of another class
annotated with <code>DiscriminatorField</code>.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-properties hljs" data-lang="properties">Trader.fetchByName=SELECT * FROM traders WHERE name = @tag0</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TraderRepository extends DatastoreRepository&lt;Trader, String&gt; {
// This method uses the query from the properties file instead of one generated based on name.
List&lt;Trader&gt; fetchByName(@Param("tag0") String traderName);
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_transactions_3"><a class="link" href="#_transactions_3">14.5.4. Transactions</a></h4>
<div class="paragraph">
<p>These transactions work very similarly to those of <code>DatastoreOperations</code>, but is specific to the repository&#8217;s domain type and provides repository functions instead of template functions.</p>
</div>
<div class="paragraph">
<p>For example, this is a read-write transaction:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
DatastoreRepository myRepo;
public String doWorkInsideTransaction() {
return myRepo.performTransaction(
transactionDatastoreRepo -&gt; {
// Work with the single-transaction transactionDatastoreRepo here.
// This is a DatastoreRepository object.
return "transaction completed";
}
);
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_projections_2"><a class="link" href="#_projections_2">14.5.5. Projections</a></h4>
<div class="paragraph">
<p>Spring Data Cloud Datastore supports <a href="https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#projections">projections</a>.
You can define projection interfaces based on domain types and add query methods that return them in your repository:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public interface TradeProjection {
String getAction();
@Value("#{target.symbol + ' ' + target.action}")
String getSymbolAndAction();
}
public interface TradeRepository extends DatastoreRepository&lt;Trade, Key&gt; {
List&lt;Trade&gt; findByTraderId(String traderId);
List&lt;TradeProjection&gt; findByAction(String action);
@Query("SELECT action, symbol FROM trades WHERE action = @action")
List&lt;TradeProjection&gt; findByQuery(String action);
}</code></pre>
</div>
</div>
<div class="paragraph">
<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>SELECT *</code>) require composite indexes containing the selected fields.</p>
</div>
<div class="paragraph">
<p>Properties of projection types defined using SpEL use the fixed name <code>target</code> for the underlying domain object.
As a result, accessing underlying properties take the form <code>target.&lt;property-name&gt;</code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_rest_repositories_2"><a class="link" href="#_rest_repositories_2">14.5.6. REST Repositories</a></h4>
<div class="paragraph">
<p>When running with Spring Boot, repositories can be exposed as REST services by simply adding this dependency to your pom file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-data-rest&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you prefer to configure parameters (such as path), you can use <code>@RepositoryRestResource</code> annotation:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RepositoryRestResource(collectionResourceRel = "trades", path = "trades")
public interface TradeRepository extends DatastoreRepository&lt;Trade, String[]&gt; {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For example, you can retrieve all <code>Trade</code> objects in the repository by using <code>curl http://&lt;server&gt;:&lt;port&gt;/trades</code>, or any specific trade via <code>curl http://&lt;server&gt;:&lt;port&gt;/trades/&lt;trader_id&gt;</code>.</p>
</div>
<div class="paragraph">
<p>You can also write trades using <code>curl -XPOST -H"Content-Type: application/json" -<a href="mailto:d@test.json">d@test.json</a> http://&lt;server&gt;:&lt;port&gt;/trades/</code> where the file <code>test.json</code> holds the JSON representation of a <code>Trade</code> object.</p>
</div>
<div class="paragraph">
<p>To delete trades, you can use <code>curl -XDELETE http://&lt;server&gt;:&lt;port&gt;/trades/&lt;trader_id&gt;</code></p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_events_2"><a class="link" href="#_events_2">14.6. Events</a></h3>
<div class="paragraph">
<p>Spring Data Cloud Datastore publishes events extending the Spring Framework&#8217;s <code>ApplicationEvent</code> to the context that can be received by <code>ApplicationListener</code> beans you register.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Contents</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterFindByKeyEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after read by-key operations are executed by <code>DatastoreTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The entities read from Cloud Datastore and the original keys in the request.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterQueryEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after read byquery operations are executed by <code>DatastoreTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The entities read from Cloud Datastore and the original query in the request.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>BeforeSaveEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately before save operations are executed by <code>DatastoreTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The entities to be sent to Cloud Datastore and the original Java objects being saved.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterSaveEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after save operations are executed by <code>DatastoreTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The entities sent to Cloud Datastore and the original Java objects being saved.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>BeforeDeleteEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately before delete operations are executed by <code>DatastoreTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The keys to be sent to Cloud Datastore. The target entities, ID values, or entity type originally specified for the delete operation.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>AfterDeleteEvent</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Published immediately after delete operations are executed by <code>DatastoreTemplate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The keys sent to Cloud Datastore. The target entities, ID values, or entity type originally specified for the delete operation.</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_auditing_2"><a class="link" href="#_auditing_2">14.7. Auditing</a></h3>
<div class="paragraph">
<p>Spring Data Cloud Datastore supports the <code>@LastModifiedDate</code> and <code>@LastModifiedBy</code> auditing annotations for properties:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Entity
public class SimpleEntity {
@Id
String id;
@LastModifiedBy
String lastUser;
@LastModifiedDate
DateTime lastTouched;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Upon insert, update, or save, these properties will be set automatically by the framework before Datastore entities are generated and saved to Cloud Datastore.</p>
</div>
<div class="paragraph">
<p>To take advantage of these features, add the <code>@EnableDatastoreAuditing</code> annotation to your configuration class and provide a bean for an <code>AuditorAware&lt;A&gt;</code> implementation where the type <code>A</code> is the desired property type annotated by <code>@LastModifiedBy</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
@EnableDatastoreAuditing
public class Config {
@Bean
public AuditorAware&lt;String&gt; auditorProvider() {
return () -&gt; Optional.of("YOUR_USERNAME_HERE");
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>AuditorAware</code> interface contains a single method that supplies the value for fields annotated by <code>@LastModifiedBy</code> and can be of any type.
One alternative is to use Spring Security&#8217;s <code>User</code> type:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">class SpringSecurityAuditorAware implements AuditorAware&lt;User&gt; {
public Optional&lt;User&gt; getCurrentAuditor() {
return Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getPrincipal)
.map(User.class::cast);
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also set a custom provider for properties annotated <code>@LastModifiedDate</code> by providing a bean for <code>DateTimeProvider</code> and providing the bean name to <code>@EnableDatastoreAuditing(dateTimeProviderRef = "customDateTimeProviderBean")</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_partitioning_data_by_namespace"><a class="link" href="#_partitioning_data_by_namespace">14.8. Partitioning Data by Namespace</a></h3>
<div class="paragraph">
<p>You can <a href="https://cloud.google.com/datastore/docs/concepts/multitenancy">partition your data by using more than one namespace</a>.
This is the recommended method for multi-tenancy in Cloud Datastore.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> @Bean
public DatastoreNamespaceProvider namespaceProvider() {
// return custom Supplier of a namespace string.
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>DatastoreNamespaceProvider</code> is a synonym for <code>Supplier&lt;String&gt;</code>.
By providing a custom implementation of this bean (for example, supplying a thread-local namespace name), you can direct your application to use multiple namespaces.
Every read, write, query, and transaction you perform will utilize the namespace provided by this supplier.</p>
</div>
<div class="paragraph">
<p>Note that your provided namespace in <code>application.properties</code> will be ignored if you define a namespace provider bean.</p>
</div>
</div>
<div class="sect2">
<h3 id="_spring_boot_actuator_support"><a class="link" href="#_spring_boot_actuator_support">14.9. Spring Boot Actuator Support</a></h3>
<div class="sect3">
<h4 id="_cloud_datastore_health_indicator"><a class="link" href="#_cloud_datastore_health_indicator">14.9.1. Cloud Datastore Health Indicator</a></h4>
<div class="paragraph">
<p>If you are using Spring Boot Actuator, you can take advantage of the Cloud Datastore health indicator called <code>datastore</code>.
The health indicator will verify whether Cloud Datastore is up and accessible by your application.
To enable it, all you need to do is add the <a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready">Spring Boot Actuator</a> to your project.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_sample_10"><a class="link" href="#_sample_10">14.10. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-basic-sample">Simple Spring Boot Application</a> and more advanced <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-sample">Sample Spring Boot Application</a> are provided to show how to use the Spring Data Cloud Datastore starter and template.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_cloud_firestore"><a class="link" href="#_cloud_firestore">15. Cloud Firestore</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring Cloud GCP provides a convenience starter which automatically configures authentication settings and client objects needed to begin using <a href="https://cloud.google.com/firestore/">Google Cloud Firestore</a> in native mode.</p>
</div>
<div class="paragraph">
<p>See <a href="https://cloud.google.com/firestore/docs/">documentation</a> to learn more about Cloud Firestore.</p>
</div>
<div class="paragraph">
<p>To begin using this library, add the <code>spring-cloud-gcp-starter-firestore</code> artifact to your project.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-firestore&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-firestore'
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_using_cloud_firestore"><a class="link" href="#_using_cloud_firestore">15.1. Using Cloud Firestore</a></h3>
<div class="paragraph">
<p>The starter automatically configures and registers a <code>Firestore</code> bean in the Spring application context. To start using it, simply use the <code>@Autowired</code> annotation.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
Firestore firestore;
void writeDocumentFromObject() throws ExecutionException, InterruptedException {
// Add document data with id "joe" using a custom User class
User data = new User("Joe",
Arrays.asList(
new Phone(12345, PhoneType.CELL),
new Phone(54321, PhoneType.WORK)));
// .get() blocks on response
WriteResult writeResult = this.firestore.document("users/joe").set(data).get();
LOGGER.info("Update time: " + writeResult.getUpdateTime());
}
User readDocumentToObject() throws ExecutionException, InterruptedException {
ApiFuture&lt;DocumentSnapshot&gt; documentFuture =
this.firestore.document("users/joe").get();
User user = documentFuture.get().toObject(User.class);
LOGGER.info("read: " + user);
return user;
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_configuration_6"><a class="link" href="#_configuration_6">15.2. Configuration</a></h3>
<div class="paragraph">
<p>The Spring Boot starter for Google Cloud Firestore provides the following configuration options:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.firestore.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables or disables Pub/Sub auto-configuration</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.firestore.project-id</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GCP project ID where the Google Cloud Firestore API is hosted, if different from the one in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.firestore.credentials.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">OAuth2 credentials for authenticating with the Google Cloud Datastore API, if different from the ones in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.firestore.credentials.encoded-key</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Base64-encoded OAuth2 credentials for authenticating with the Google Cloud Datastore API, if different from the ones in the <a href="#spring-cloud-gcp-core">Spring Cloud GCP Core Module</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.firestore.credentials.scopes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://developers.google.com/identity/protocols/googlescopes">OAuth2 scope</a> for Spring Cloud GCP Cloud Datastore credentials</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.googleapis.com/auth/datastore" class="bare">https://www.googleapis.com/auth/datastore</a></p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_sample_11"><a class="link" href="#_sample_11">15.3. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud-gcp/spring-cloud-gcp-samples/spring-cloud-gcp-firestore-sample">sample application</a> is available.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_cloud_memorystore_for_redis"><a class="link" href="#_cloud_memorystore_for_redis">16. Cloud Memorystore for Redis</a></h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_spring_caching"><a class="link" href="#_spring_caching">16.1. Spring Caching</a></h3>
<div class="paragraph">
<p><a href="https://cloud.google.com/memorystore/">Cloud Memorystore for Redis</a> provides a fully managed in-memory data store service.
Cloud Memorystore is compatible with the Redis protocol, allowing easy integration with <a href="https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html">Spring Caching</a>.</p>
</div>
<div class="paragraph">
<p>All you have to do is create a Cloud Memorystore instance and use its IP address in <code>application.properties</code> file as <code>spring.redis.host</code> property value.
Everything else is exactly the same as setting up redis-backed Spring caching.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Memorystore instances and your application instances have to be located in the same region.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In short, the following dependencies are needed:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-cache&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>And then you can use <code>org.springframework.cache.annotation.Cacheable</code> annotation for methods you&#8217;d like to be cached.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Cacheable("cache1")
public String hello(@PathVariable String name) {
....
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you are interested in a detailed how-to guide, please check <a href="https://codelabs.developers.google.com/codelabs/cloud-spring-cache-memorystore/">Spring Boot Caching using Cloud Memorystore codelab</a>.</p>
</div>
<div class="paragraph">
<p>Cloud Memorystore documentation can be found <a href="https://cloud.google.com/memorystore/docs/redis/">here</a>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_cloud_identity_aware_proxy_iap_authentication"><a class="link" href="#_cloud_identity_aware_proxy_iap_authentication">17. Cloud Identity-Aware Proxy (IAP) Authentication</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://cloud.google.com/iap/">Cloud Identity-Aware Proxy (IAP)</a> provides a security layer over applications deployed to Google Cloud.</p>
</div>
<div class="paragraph">
<p>The IAP starter uses <a href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2resourceserver">Spring Security OAuth 2.0 Resource Server</a> functionality to automatically extract user identity from the proxy-injected <code>x-goog-iap-jwt-assertion</code> HTTP header.</p>
</div>
<div class="paragraph">
<p>The following claims are validated automatically:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Issue time</p>
</li>
<li>
<p>Expiration time</p>
</li>
<li>
<p>Issuer</p>
</li>
<li>
<p>Audience</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The <em>audience</em> (<code>"aud"</code> claim) validation string is automatically determined when the application is running on App Engine Standard or App Engine Flexible.
This functionality relies on Cloud Resource Manager API to retrieve project details, so the following setup is needed:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Enable Cloud Resource Manager API in <a href="https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com">GCP Console</a>.</p>
</li>
<li>
<p>Make sure your application has <code>resourcemanager.projects.get</code> permission.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>App Engine automatic <em>audience</em> determination can be overridden by using <code>spring.cloud.gcp.security.iap.audience</code> property.</p>
</div>
<div class="paragraph">
<p>For Compute Engine or Kubernetes Engine <code>spring.cloud.gcp.security.iap.audience</code> property <strong>must</strong> be provided, as the <em>audience</em> string depends on the specific Backend Services setup and cannot be inferred automatically.
To determine the <em>audience</em> value, follow directions in IAP <a href="https://cloud.google.com/iap/docs/signed-headers-howto#verify_the_jwt_payload">Verify the JWT payload</a> guide.
If <code>spring.cloud.gcp.security.iap.audience</code> is not provided, the application will fail to start the following message:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>No qualifying bean of type 'org.springframework.cloud.gcp.security.iap.AudienceProvider' available.</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If you create a custom <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html"><code>WebSecurityConfigurerAdapter</code></a>, enable extracting user identity by adding <code>.oauth2ResourceServer().jwt()</code> configuration to the <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/builders/HttpSecurity.html"><code>HttpSecurity</code></a> object.
If no custom <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html"><code>WebSecurityConfigurerAdapter</code></a> is present, nothing needs to be done because Spring Boot will add this customization by default.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Starter Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-security-iap&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Starter Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-security-iap'
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_configuration_7"><a class="link" href="#_configuration_7">17.1. Configuration</a></h3>
<div class="paragraph">
<p>The following properties are available.</p>
</div>
<div class="admonitionblock caution">
<table>
<tr>
<td class="icon">
<i class="fa icon-caution" title="Caution"></i>
</td>
<td class="content">
Modifying registry, algorithm, and header properties might be useful for testing, but the defaults should not be changed in production.
</td>
</tr>
</table>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.security.iap.registry</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Link to JWK public key registry.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><a href="https://www.gstatic.com/iap/verify/public_key-jwk" class="bare">https://www.gstatic.com/iap/verify/public_key-jwk</a></code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.security.iap.algorithm</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Encryption algorithm used to sign the JWK token.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ES256</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.security.iap.header</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Header from which to extract the JWK key.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>x-goog-iap-jwt-assertion</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.security.iap.issuer</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">JWK issuer to verify.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><a href="https://cloud.google.com/iap" class="bare">https://cloud.google.com/iap</a></code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.security.iap.audience</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom JWK audience to verify.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false on App Engine; true on GCE/GKE</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_sample_12"><a class="link" href="#_sample_12">17.2. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-security-iap-sample">sample application</a> is available.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_google_cloud_vision"><a class="link" href="#_google_cloud_vision">18. Google Cloud Vision</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <a href="https://cloud.google.com/vision/">Google Cloud Vision API</a> allows users to leverage machine learning algorithms for processing images and documents including: image classification, face detection, text extraction, optical character recognition, and others.</p>
</div>
<div class="paragraph">
<p>Spring Cloud GCP provides:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A convenience starter which automatically configures authentication settings and client objects needed to begin using the <a href="https://cloud.google.com/vision/">Google Cloud Vision API</a>.</p>
</li>
<li>
<p><code>CloudVisionTemplate</code> which simplifies interactions with the Cloud Vision API.</p>
<div class="ulist">
<ul>
<li>
<p>Allows you to easily send images to the API as Spring Resources.</p>
</li>
<li>
<p>Offers convenience methods for common operations, such as classifying content of an image.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>DocumentOcrTemplate</code> which offers convenient methods for running <a href="https://cloud.google.com/vision/docs/pdf">optical character recognition (OCR)</a> on PDF and TIFF documents.</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_dependency_setup"><a class="link" href="#_dependency_setup">18.1. Dependency Setup</a></h3>
<div class="paragraph">
<p>To begin using this library, add the <code>spring-cloud-gcp-starter-vision</code> artifact to your project.</p>
</div>
<div class="paragraph">
<p>Maven coordinates, using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-vision&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-vision'
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="_cloud_vision_ocr_dependencies"><a class="link" href="#_cloud_vision_ocr_dependencies">18.1.1. Cloud Vision OCR Dependencies</a></h4>
<div class="paragraph">
<p>If you are interested in applying optical character recognition (OCR) on documents for your project, you&#8217;ll need to add both <code>spring-cloud-gcp-starter-vision</code> and <code>spring-cloud-gcp-starter-storage</code> to your dependencies.
The storage starter is necessary because the Cloud Vision API will process your documents and write OCR output files all within your Google Cloud Storage buckets.</p>
</div>
<div class="paragraph">
<p>Maven coordinates using <a href="#_bill_of_materials">Spring Cloud GCP BOM</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-vision&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-gcp-starter-storage&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gradle coordinates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code>dependencies {
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-vision'
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-storage'
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_image_analysis"><a class="link" href="#_image_analysis">18.2. Image Analysis</a></h3>
<div class="paragraph">
<p>The <code>CloudVisionTemplate</code> allows you to easily analyze images; it provides the following method for interfacing with Cloud Vision:</p>
</div>
<div class="paragraph">
<p><code>public AnnotateImageResponse analyzeImage(Resource imageResource, Feature.Type&#8230;&#8203; featureTypes)</code></p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>Resource imageResource</code> refers to the Spring Resource of the image object you wish to analyze.
The Google Cloud Vision documentation provides a <a href="https://cloud.google.com/vision/docs/supported-files">list of the image types that they support</a>.</p>
</li>
<li>
<p><code>Feature.Type&#8230;&#8203; featureTypes</code> refers to a var-arg array of Cloud Vision Features to extract from the image.
A feature refers to a kind of image analysis one wishes to perform on an image, such as label detection, OCR recognition, facial detection, etc.
One may specify multiple features to analyze within one request.
A full list of Cloud Vision Features is provided in the <a href="https://cloud.google.com/vision/docs/features">Cloud Vision Feature docs</a>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#google.cloud.vision.v1.AnnotateImageResponse"><code>AnnotateImageResponse</code></a> contains the results of all the feature analyses that were specified in the request.
For each feature type that you provide in the request, <code>AnnotateImageResponse</code> provides a getter method to get the result of that feature analysis.
For example, if you analyzed an image using the <code>LABEL_DETECTION</code> feature, you would retrieve the results from the response using <code>annotateImageResponse.getLabelAnnotationsList()</code>.</p>
<div class="paragraph">
<p><code>AnnotateImageResponse</code> is provided by the Google Cloud Vision libraries; please consult the <a href="https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#google.cloud.vision.v1.AnnotateImageResponse">RPC reference</a> or <a href="https://googleapis.github.io/googleapis/java/all/latest/apidocs/com/google/cloud/vision/v1/AnnotateImageResponse.html">Javadoc</a> for more details.
Additionally, you may consult the <a href="https://cloud.google.com/vision/docs/">Cloud Vision docs</a> to familiarize yourself with the concepts and features of the API.</p>
</div>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_detect_image_labels_example"><a class="link" href="#_detect_image_labels_example">18.2.1. Detect Image Labels Example</a></h4>
<div class="paragraph">
<p><a href="https://cloud.google.com/vision/docs/detecting-labels">Image labeling</a> refers to producing labels that describe the contents of an image.
Below is a code sample of how this is done using the Cloud Vision Spring Template.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired
private ResourceLoader resourceLoader;
@Autowired
private CloudVisionTemplate cloudVisionTemplate;
public void processImage() {
Resource imageResource = this.resourceLoader.getResource("my_image.jpg");
AnnotateImageResponse response = this.cloudVisionTemplate.analyzeImage(
imageResource, Type.LABEL_DETECTION);
System.out.println("Image Classification results: " + response.getLabelAnnotationsList());
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_document_ocr_template"><a class="link" href="#_document_ocr_template">18.3. Document OCR Template</a></h3>
<div class="paragraph">
<p>The <code>DocumentOcrTemplate</code> allows you to easily run <a href="https://cloud.google.com/vision/docs/pdf">optical character recognition (OCR)</a> on your PDF and TIFF documents stored in your Google Storage bucket.</p>
</div>
<div class="paragraph">
<p>First, you will need to create a bucket in <a href="https://console.cloud.google.com/storage">Google Cloud Storage</a> and <a href="https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-java">upload the documents you wish to process into the bucket</a>.</p>
</div>
<div class="sect3">
<h4 id="_running_ocr_on_a_document"><a class="link" href="#_running_ocr_on_a_document">18.3.1. Running OCR on a Document</a></h4>
<div class="paragraph">
<p>When OCR is run on a document, the Cloud Vision APIs will output a collection of OCR output files in JSON which describe the text content, bounding rectangles of words and letters, and other information about the document.</p>
</div>
<div class="paragraph">
<p>The <code>DocumentOcrTemplate</code> provides the following method for running OCR on a document saved in Google Cloud Storage:</p>
</div>
<div class="paragraph">
<p><code>ListenableFuture&lt;DocumentOcrResultSet&gt; runOcrForDocument(GoogleStorageLocation document, GoogleStorageLocation outputFilePathPrefix)</code></p>
</div>
<div class="paragraph">
<p>The method allows you to specify the location of the document and the output location for where all the JSON output files will be saved in Google Cloud Storage.
It returns a <code>ListenableFuture</code> containing <code>DocumentOcrResultSet</code> which contains the OCR content of the document.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Running OCR on a document is an operation that can take between several minutes to several hours depending on how large the document is.
It is recommended to register callbacks to the returned ListenableFuture or ignore it and process the JSON output files at a later point in time using <code>readOcrOutputFile</code> or <code>readOcrOutputFileSet</code>.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_running_ocr_example"><a class="link" href="#_running_ocr_example">18.3.2. Running OCR Example</a></h4>
<div class="paragraph">
<p>Below is a code snippet of how to run OCR on a document stored in a Google Storage bucket and read the text in the first page of the document.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>@Autowired
private DocumentOcrTemplate documentOcrTemplate;
public void runOcrOnDocument() {
GoogleStorageLocation document = GoogleStorageLocation.forFile(
"your-bucket", "test.pdf");
GoogleStorageLocation outputLocationPrefix = GoogleStorageLocation.forFolder(
"your-bucket", "output_folder/test.pdf/");
ListenableFuture&lt;DocumentOcrResultSet&gt; result =
this.documentOcrTemplate.runOcrForDocument(
document, outputLocationPrefix);
DocumentOcrResultSet ocrPages = result.get(5, TimeUnit.MINUTES);
String page1Text = ocrPages.getPage(1).getText();
System.out.println(page1Text);
}</pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_reading_ocr_output_files"><a class="link" href="#_reading_ocr_output_files">18.3.3. Reading OCR Output Files</a></h4>
<div class="paragraph">
<p>In some use-cases, you may need to directly read OCR output files stored in Google Cloud Storage.</p>
</div>
<div class="paragraph">
<p><code>DocumentOcrTemplate</code> offers the following methods for reading and processing OCR output files:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>readOcrOutputFileSet(GoogleStorageLocation jsonOutputFilePathPrefix)</code>:
Reads a collection of OCR output files under a file path prefix and returns the parsed contents.
All of the files under the path should correspond to the same document.</p>
</li>
<li>
<p><code>readOcrOutputFile(GoogleStorageLocation jsonFile)</code>:
Reads a single OCR output file and returns the parsed contents.</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_reading_ocr_output_files_example"><a class="link" href="#_reading_ocr_output_files_example">18.3.4. Reading OCR Output Files Example</a></h4>
<div class="paragraph">
<p>The code snippet below describes how to read the OCR output files of a single document.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>@Autowired
private DocumentOcrTemplate documentOcrTemplate;
// Parses the OCR output files corresponding to a single document in a directory
public void parseOutputFileSet() {
GoogleStorageLocation ocrOutputPrefix = GoogleStorageLocation.forFolder(
"your-bucket", "json_output_set/");
DocumentOcrResultSet result = this.documentOcrTemplate.readOcrOutputFileSet(ocrOutputPrefix);
System.out.println("Page 2 text: " + result.getPage(2).getText());
}
// Parses a single OCR output file
public void parseSingleOutputFile() {
GoogleStorageLocation ocrOutputFile = GoogleStorageLocation.forFile(
"your-bucket", "json_output_set/test_output-2-to-2.json");
DocumentOcrResultSet result = this.documentOcrTemplate.readOcrOutputFile(ocrOutputFile);
System.out.println("Page 2 text: " + result.getPage(2).getText());
}</pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_configuration_8"><a class="link" href="#_configuration_8">18.4. Configuration</a></h3>
<div class="paragraph">
<p>The following options may be configured with Spring Cloud GCP Vision libraries.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.vision.enabled</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables or disables Cloud Vision autoconfiguration</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.vision.executors-threads-count</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of threads used during document OCR processing for waiting on long-running OCR operations</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>spring.cloud.gcp.vision.json-output-batch-size</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of document pages to include in each OCR output file.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">20</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_sample_13"><a class="link" href="#_sample_13">18.5. Sample</a></h3>
<div class="paragraph">
<p>Samples are provided to show example usages of Spring Cloud GCP with Google Cloud Vision.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-vision-api-sample">Image Labeling Sample</a> shows you how to use image labelling in your Spring application.
The application generates labels describing the content inside the images you specify in the application.</p>
</li>
<li>
<p>The <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-vision-ocr-demo">Document OCR demo</a> shows how you can apply OCR processing on your PDF/TIFF documents in order to extract their text contents.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_cloud_foundry"><a class="link" href="#_cloud_foundry">19. Cloud Foundry</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring Cloud GCP provides support for Cloud Foundry&#8217;s <a href="https://docs.pivotal.io/partners/gcp-sb/index.html">GCP Service Broker</a>.
Our Pub/Sub, Cloud Spanner, Storage, Stackdriver Trace and Cloud SQL MySQL and PostgreSQL starters are Cloud Foundry aware and retrieve properties like project ID, credentials, etc., that are used in auto configuration from the Cloud Foundry environment.</p>
</div>
<div class="paragraph">
<p>In cases like Pub/Sub&#8217;s topic and subscription, or Storage&#8217;s bucket name, where those parameters are not used in auto configuration, you can fetch them using the VCAP mapping provided by Spring Boot.
For example, to retrieve the provisioned Pub/Sub topic, you can use the <code>vcap.services.mypubsub.credentials.topic_name</code> property from the application environment.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If the same service is bound to the same application more than once, the auto configuration will not be able to choose among bindings and will not be activated for that service.
This includes both MySQL and PostgreSQL bindings to the same app.
</td>
</tr>
</table>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
In order for the Cloud SQL integration to work in Cloud Foundry, auto-reconfiguration must be disabled.
You can do so using the <code>cf set-env &lt;APP&gt; JBP_CONFIG_SPRING_AUTO_RECONFIGURATION '{enabled: false}'</code> command.
Otherwise, Cloud Foundry will produce a <code>DataSource</code> with an invalid JDBC URL (i.e., <code>jdbc:mysql://null/null</code>).
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_kotlin_support"><a class="link" href="#_kotlin_support">20. Kotlin Support</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The latest version of the Spring Framework provides first-class support for Kotlin.
For Kotlin users of Spring, the Spring Cloud GCP libraries work out-of-the-box and are fully interoperable with Kotlin applications.</p>
</div>
<div class="paragraph">
<p>For more information on building a Spring application in Kotlin, please consult the <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/languages.html#kotlin">Spring Kotlin documentation</a>.</p>
</div>
<div class="sect2">
<h3 id="_prerequisites_2"><a class="link" href="#_prerequisites_2">20.1. Prerequisites</a></h3>
<div class="paragraph">
<p>Ensure that your Kotlin application is properly set up.
Based on your build system, you will need to include the correct Kotlin build plugin in your project:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://kotlinlang.org/docs/reference/using-maven.html">Kotlin Maven Plugin</a></p>
</li>
<li>
<p><a href="https://kotlinlang.org/docs/reference/using-gradle.html">Kotlin Gradle Plugin</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Depending on your application&#8217;s needs, you may need to augment your build configuration with compiler plugins:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://kotlinlang.org/docs/reference/compiler-plugins.html#spring-support">Kotlin Spring Plugin</a>: Makes your Spring configuration classes/members non-final for convenience.</p>
</li>
<li>
<p><a href="https://kotlinlang.org/docs/reference/compiler-plugins.html#jpa-support">Kotlin JPA Plugin</a>: Enables using JPA in Kotlin applications.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Once your Kotlin project is properly configured, the Spring Cloud GCP libraries will work within your application without any additional setup.</p>
</div>
</div>
<div class="sect2">
<h3 id="_sample_14"><a class="link" href="#_sample_14">20.2. Sample</a></h3>
<div class="paragraph">
<p>A <a href="https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples/spring-cloud-gcp-kotlin-app-sample">Kotlin sample application</a> is provided to demonstrate a working Maven setup and various Spring Cloud GCP integrations from within Kotlin.</p>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="js/tocbot/tocbot.min.js"></script>
<script type="text/javascript" src="js/toc.js"></script>
<link rel="stylesheet" href="js/highlight/styles/atom-one-dark-reasonable.min.css">
<script src="js/highlight/highlight.min.js"></script>
<script>hljs.initHighlighting()</script>
</body>
</html>