Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spanner Features Sample #225

Merged
merged 7 commits into from Nov 4, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,25 @@
= Cloud Spanner Hibernate Example

This sample application demonstrates using https://hibernate.org/orm/releases/5.4/[Hibernate 5.4] with https://cloud.google.com/spanner/[Google Cloud Spanner].


== Setup & Configuration
1. Create a Google Cloud Platform Project
2. https://cloud.google.com/docs/authentication/getting-started#creating_the_service_account[Create a service account] with Cloud Spanner permission.
Furnish a new JSON key and then set the credentials using the `GOOGLE_APPLICATION_CREDENTIALS` environment variable.
+
Alternatively, have the https://cloud.google.com/sdk/[Google Cloud SDK] installed and initialized and logged in with https://developers.google.com/identity/protocols/application-default-credentials[application default credentials].

3. Enable the https://console.cloud.google.com/apis/api/spanner.googleapis.com/overview[Cloud Spanner API].

4. Create a Cloud Spanner instance and database in your project and save those details for the next step.

== Run the Example

Run the example `main` method in `SpannerFeatureSampleApplication.java`.
Replace the `PROJECT_ID`, `INSTANCE_ID`, and `DATABASE_ID` parameters in the command to specify the Spanner database you will use for the sample.

----
mvn exec:java -Dexec.mainClass="com.example.SpannerFeatureSampleApplication" -Dhibernate.connection.url="jdbc:cloudspanner:/projects/{YOUR_PROJECT_ID}/instances/{YOUR_INSTANCE_ID}/databases/{YOUR_DATABASE_ID}"
----

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>google-cloud-spanner-hibernate-samples</artifactId>
<groupId>com.google.cloud</groupId>
<version>1.5.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>basic-spanner-features-sample</artifactId>

<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
</dependencies>

</project>
@@ -0,0 +1,57 @@
/*
* Copyright 2019-2020 Google LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/

package com.example;

import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

/**
* An sample Hibernate application demonstrating how to use deeper features of Cloud Spanner.
*
* <p>These include:
* - Different Spanner transaction types
* - Stale reads
*/
public class SpannerFeatureSampleApplication {

/**
* Entry point to the sample application.
*/
public static void main(String[] args) {
StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure()
.build();
SessionFactory sessionFactory = new MetadataSources(registry).buildMetadata()
.buildSessionFactory();

try {
// Run the stale reads demo.
StaleReadsDemo.runStaleReads(sessionFactory);

// Demos using different Cloud Spanner transaction types
TransactionTypeDemo.runReadOnlyTransaction(sessionFactory);

} finally {
sessionFactory.close();
}
}

}
@@ -0,0 +1,79 @@
/*
* Copyright 2019-2020 Google LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/

package com.example;

import com.example.entities.Book;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

/**
* Code samples for using Stale Reads in Hibernate.
*
* <p>Stale reads allow you to read from the Spanner database at a timestamp in the past.
* See: https://cloud.google.com/spanner/docs/reads
*/
public class StaleReadsDemo {

static void runStaleReads(SessionFactory sessionFactory) {
System.out.println("======== Stale Reads Demo ========");

Book book;

try (Session session = sessionFactory.openSession()) {
// First save a book record in the database.
session.beginTransaction();
book = new Book("Super Book", "Bob Blob");
session.save(book);
session.getTransaction().commit();
System.out.println("Saved book to database: " + book);

// Perform a strong read. One book is returned.
List<Book> booksInTable =
session.createQuery("from Book b where b.id = :id", Book.class)
.setParameter("id", book.getId())
.list();
System.out.println("Executing a strong read: " + booksInTable);
}

try (Session session = sessionFactory.openSession()) {
session.beginTransaction();

// Configure the connection to do a stale read.
session.doWork(conn -> {
// Must set to read-only connection to use stale reads.
conn.createStatement().execute("SET TRANSACTION READ ONLY");

// Set the stale read settings through the JDBC connection.
conn.createStatement().execute(
"SET READ_ONLY_STALENESS = 'EXACT_STALENESS 600s'");
});

List<Book> booksInTable =
session.createQuery("from Book b where b.id = :id", Book.class)
.setParameter("id", book.getId())
.list();
System.out.println(
"Executing a exact stale read 10 minutes in the past (no books should be found): "
+ booksInTable);
}

System.out.println("==========================");
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2019-2020 Google LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/

package com.example;

import com.example.entities.Book;
import javax.persistence.PersistenceException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

/**
* Code samples specifying a different Cloud Spanner Transaction Type.
*/
public class TransactionTypeDemo {

/**
* This code sample demonstrates an error when trying to perform an update in a read-only
* transaction.
*/
static void runReadOnlyTransaction(SessionFactory sessionFactory) {
System.out.println("======== Read-only Transaction Demo ========");

try (Session session = sessionFactory.openSession()) {
session.beginTransaction();

// Set transaction to read-only.
session.doWork(conn -> {
conn.createStatement().execute("SET TRANSACTION READ ONLY");
});

Book book = new Book("Programming Guide", "Author");
session.save(book);

session.getTransaction().commit();
} catch (PersistenceException ex) {
System.out.println("You will get the following error if you try to modify the tables in "
+ "a read-only transaction: ");

Throwable throwable = ex;
while (throwable != null) {
System.out.println("\t" + throwable.getMessage());
throwable = throwable.getCause();
}
}

System.out.println("==========================");
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2019-2020 Google LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/

package com.example.entities;

import java.util.UUID;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.hibernate.annotations.Type;

/**
* Simple Hibernate Entity used by the sample application.
*/
@Entity
public class Book {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Type(type = "uuid-char")
private UUID id;

private String title;

private String author;

public Book() {

}

public Book(String title, String author) {
this.title = title;
this.author = author;
}

public UUID getId() {
return id;
}

public void setId(UUID id) {
this.id = id;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

@Override
public String toString() {
return "Book{"
+ "id=" + id
+ ", title='" + title + '\''
+ ", author='" + author + '\''
+ '}';
}
}
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>

<!-- Connection settings -->
<property name="hibernate.connection.driver_class">com.google.cloud.spanner.jdbc.JdbcDriver</property>

<!-- Uncomment this property and set the URL for the database,
unless you're setting it via a system property or hibernate.properties file. -->
<!--
<property name="hibernate.connection.url">jdbc:cloudspanner:/projects/{YOUR_PROJECT_ID}/instances/{YOUR_INSTANCE_ID}/databases/{YOUR_DATABASE_ID}</property>
-->

<!-- SQL dialect -->
<property name="hibernate.dialect">com.google.cloud.spanner.hibernate.SpannerDialect</property>

<!-- Print executed SQL to stdout -->
<property name="show_sql">true</property>

<!-- Update database on startup -->
<property name="hibernate.hbm2ddl.auto">update</property>

<!-- Annotated entity classes -->
<mapping class="com.example.entities.Book"/>

</session-factory>
</hibernate-configuration>
1 change: 1 addition & 0 deletions google-cloud-spanner-hibernate-samples/pom.xml
Expand Up @@ -29,6 +29,7 @@
<module>microprofile-jpa-sample</module>
<module>quarkus-jpa-sample</module>
<module>spanner-hibernate-codelab</module>
<module>basic-spanner-features-sample</module>
</modules>

<build>
Expand Down