Skip to content
The official Google Cloud Spanner Dialect for Hibernate ORM
Java Other
  1. Java 98.5%
  2. Other 1.5%
Branch: master
Clone or download
dzou Add support for Unique Constraint in Hibernate (#173)
This adds support for Hibernate to generate unique indices when uniqueness is invoked such as through annotating an entity column with @column(unique = true). Fixes #165.
Latest commit 59a7c30 Dec 5, 2019

README.adoc

Google Cloud Spanner Dialect for Hibernate ORM

This is a dialect compatible with Hibernate 5.4 for the Google Cloud Spanner database service. The SpannerDialect produces SQL, DML, and DDL statements for most common entity types and relationships using standard Hibernate and Java Persistence annotations.

Please see the following sections for important details about dialect differences due to the unique features and limitations of Cloud Spanner.

Quick Set-Up

Maven coordinates for the dialect:

<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
  <version>1.0.0</version>
</dependency>

Maven coordinates for the official open source Cloud Spanner JDBC Driver.

<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner-jdbc</artifactId>
  <version>1.11.0</version>
</dependency>
Note
Hibernate ORM with Cloud Spanner is officially supported only with the open source Cloud Spanner JDBC Driver. It does not support the Simba JDBC driver at this time.

If you’re using a SNAPSHOT version of the dialect, please add the Sonatype Snapshots repository to your pom.xml:

<repository>
  <id>snapshots-repo</id>
  <url>https://oss.sonatype.org/content/repositories/snapshots</url>
  <releases><enabled>false</enabled></releases>
  <snapshots><enabled>true</enabled></snapshots>
</repository>

Configuring the SpannerDialect and a Cloud Spanner Driver class is typical of all Hibernate dialects in the hibernate.properties file:

hibernate.dialect=com.google.cloud.spanner.hibernate.SpannerDialect
hibernate.connection.driver_class=com.google.cloud.spanner.jdbc.JdbcDriver
hibernate.connection.url=jdbc:cloudspanner:/projects/{INSERT_PROJECT_ID}/instances/{INSERT_INSTANCE_ID}/databases/{INSERT_DATABASE_ID}

The service account JSON credentials file location should be in the GOOGLE_APPLICATION_CREDENTIALS environment variable. The driver will use default credentials set in the Google Cloud SDK gcloud application otherwise.

You are now ready to begin using Hibernate with Cloud Spanner.

User Guide

The following section describes recommendations and best practices for how to best use the Cloud Spanner Hibernate Dialect.

Use Generated UUIDs for ID Generation

The Universally Unique Identifier (UUID) is the preferred ID type in Cloud Spanner because it avoids hotspots as the system divides data among servers by key ranges. A monotonically increasing integer key would also work, but can be less performant.

For this reason, if you are using generated IDs in your code, you should configure UUID generation like this:

@Entity
public class Employee {

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

The @Type(type="uuid-char") annotation specifies that this UUID value will be stored in Cloud Spanner as a STRING column. Leaving out this annotation causes a BYTES column to be used.

Hibernate’s @GeneratedValue annotation for numeric fields is supported but not recommended:

@Entity
public class Employee {

  @Id
  @GeneratedValue   // Not Recommended.
  public Long id;
}

This results in sequential IDs that are not optimal for Cloud Spanner and makes use of the hibernate_sequence table for generating IDs.

Limit Large DML Transactions

Cloud Spanner has a mutation limit on each transaction - each Spanner transaction may only have up to 20,000 operations which modify rows of a table.

Note
Deleting a row counts as one operation and inserting/updating rows will count as a number of operations equal to the number of affected columns. For example if one inserts a row that contains 5 columns, it counts as 5 modify operations for the insert.

Consequently, users must take care to avoid encountering these constraints.

  1. We recommend being careful with the use of CASCADE_TYPE.ALL in Entity annotations because, depending on the application, it might trigger a large number of entities to be deleted in a single transaction and bring you over the 20,000 limit.

  2. Also, when persisting a collection of entities, be mindful of the 20,000 mutations per transaction constraint.

Enable Hibernate Batching

Batching SQL statements together allows you to optimize the performance of your application by including a group of SQL statements in a single remote call. This allows you to reduce the number of round-trips between your application and Cloud Spanner.

By default, Hibernate does not batch the statements that it sends to the Cloud Spanner JDBC driver.

Batching can be enabled by configuring hibernate.jdbc.batch_size in your Hibernate configuration file:

<property name="hibernate.jdbc.batch_size">100</property>

The property is set to 100 as an example; you may experiment with the batch size to see what works best for your application.

Query Optimization

The Cloud Spanner SQL syntax offers a variety of query hints to tune and optimize the performance of queries. If you find that you need to take advantage of this feature, you can achieve this in Hibernate using native SQL queries.

This is an example of using the @{FORCE_JOIN_ORDER=TRUE} hint in a native Spanner SQL query.

SQLQuery query = session.createSQLQuery("SELECT * FROM Singers AS s
                                         JOIN@{FORCE_JOIN_ORDER=TRUE} Albums AS a
                                         ON s.SingerId = a.Singerid
                                         WHERE s.LastName LIKE '%x%'
                                         AND a.AlbumTitle LIKE '%love%';");

// Executes the query.
List<Object[]> entities = query.list();

Also, you may consult the Cloud Spanner documentation on general recommendations for optimizing performance.

Schema Generation

It is often useful to generate the schema for your database, such as during the early stages of development. The Spanner dialect supports Hibernate’s hibernate.hbm2ddl.auto setting which controls the framework’s schema generation behavior on start-up.

The following settings are available:

  • none: Do nothing.

  • validate: Validate the schema, makes no changes to the database.

  • update: Create or update the schema.

  • create: Create the schema, destroying previous data.

  • create-drop: Drop the schema when the SessionFactory is closed explicitly, typically when the application is stopped.

Hibernate performs schema updates on each table and entity type on startup, which can take more than several minutes if there are many tables. To avoid schema updates keeping Hibernate from starting for several minutes, you can update schemas separately and use the none or validate settings.

Cloud Spanner Hibernate ORM Limitations

The Cloud Spanner Hibernate Dialect supports most of the standard Hibernate and Java Persistence annotations, but there are important differences in features because of differences in Cloud Spanner’s data model from traditional SQL databases.

The following sections below describe the feature set of the Cloud Spanner Hibernate Dialect as well as its limitations.

Unsupported Feature Description

Foreign Key Constraints

Hibernate will respect database constraints but cannot enforce the foreign key constraint because this feature does not exist in Spanner.

Catalog and schema scoping for table names

Tables name references cannot contain periods or other punctuation.

Column default values

The dialect does not currently set default values based on the @ColumnDefault annotation, because Cloud Spanner does not support column defaults in the DDL.

Big-decimal or arbitrary-precision numbers

The dialect does not support java.math.BigDecimal and java.math.BigInteger because Cloud Spanner does not provide native support for arbitrary-precision decimal numbers, such as NUMERIC or DECIMAL.

Schema Generation for Interleaved Tables

The dialect currently does not support creating interleaved tables during schema generation.

Simba JDBC Driver

Hibernate ORM with Cloud Spanner is officially supported only with the open-source Cloud Spanner JDBC Driver. It does not support the Simba JDBC driver at this time.

Constraints and Entity Relationships

The dialect supports all of the standard entity relationships:

  • @OneToOne

  • @OneToMany

  • @ManyToOne

  • @ManyToMany

These can be used via @JoinTable or @JoinColumn.

The dialect also supports unique column constraints applied through @Column(unique = true) or @UniqueConstraint. In these cases, the dialect will create a unique index to enforce uniqueness on the specified columns.

However, Cloud Spanner does not support foreign key constraints, so the dialect will not generate foreign keys during schema generation. Note that this does not affect how Hibernate works because Hibernate does not rely on the existence of constraints to perform its operations.

The lack of foreign key constraints also means that database-side cascading deletes are not supported via the @OnDelete(action = OnDeleteAction.CASCADE). However, Hibernate-side cascading operations such as @ManyToOne(cascade = {CascadeType.ALL}) are supported.

Spanner Table Names

The Cloud Spanner Dialect only supports @Table with the name attribute. It does not support table names with catalog and schema components:

// Supported.
@Table(
  name = "book"
)

// Not supported: `public.store.book` is not a valid Cloud Spanner table name reference.
@Table(
  catalog = "public",
  schema = "store",
  name = "book"
)

Column Default Values

The dialect does not support the @ColumnDefault annotation because Cloud Spanner does not offer a way of setting a default value for a column during table creation through DDL statements.

Decimal and Numeric Types

Cloud Spanner does not provide native support for arbitrary-precision decimal numbers, such as NUMERIC and DECIMAL. As a result, the dialect does not support decimal and arbitrary-precision Java types such as java.math.BigInteger and java.math.BigDecimal.

Interleaved Tables Schema Generation

Currently there is no support in the Cloud Spanner Hibernate dialect for generating interleaved tables in Spanner during schema generation.

If your application would benefit from interleaved tables, we recommend that you manually create them and use types mapped to them in Hibernate. This enables the performance and database-enforced-constraint benefits from interleaved tables in Cloud Spanner, even though the Hibernate framework will not be aware of their relationship or any restrictions on operations between interleaved tables.

You can’t perform that action at this time.