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.
Maven coordinates for the dialect:
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
<version>0.1.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.7.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 BUILD-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.
The dialect and driver are compatible with all values (create
, create-drop
, and update
) of the hibernate.hbm2ddl.auto
setting.
The SpannerDialect
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.
These are unsupported features in the Cloud Spanner Hibernate Dialect. Please see the following sections for details:
Unsupported Feature | Description |
---|---|
Constraints |
No support for |
Catalog and schema scoping for table names |
Tables name references cannot contain periods or other punctuation. |
Column default values |
Cloud Spanner treats |
Big-decimal or arbitrary-precision numbers |
The dialect does not support |
The dialect supports all of the standard entity relationships:
-
@OneToOne
-
@OneToMany
-
@ManyToOne
-
@ManyToMany
These can be used via @JoinTable
or @JoinColumn
.
However, because Cloud Spanner does not support foreign key constraints, foreign-key-columns are just regular columns in Cloud Spanner.
Note
|
The lack of foreign key constraints also means database-side cascading deletes are not supported via the @OnDelete(action = OnDeleteAction.CASCADE) annotation because there is no ON DELETE CASCADE constraint in Cloud Spanner DDL.
However, Hibernate-side cascading operations such as @ManyToOne(cascade = {CascadeType.ALL}) are supported.
|
To take advantage of Cloud Spanner’s full performance, consider using its best practices.
Because Hibernate’s built-in HQL does not account for Spanner-specific features, you can construct queries directly from Cloud Spanner SQL instead:
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%';");
Currently there is no support for Cloud Spanner’s interleaved table feature. Hibernate ORM relationships are always constructed using join-tables and foreign keys by the framework. 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.
Cloud Spanner does not support database constraints.
As a result, SpannerDialect
does not currently support any constraints such as FOREIGN KEY
, UNIQUE
, or ON DELETE CASCADE
.
Note
|
Hibernate doesn’t directly rely on the existence of constraints to perform its operations and leaves the enforcement of relationship links to the database. |
The lack of the foreign key constraint affects relationships and collection properties annotated with @ElementCollection
.
Hibernate’s @GeneratedValue
annotation for long
properties is supported but not recommended:
@Entity
public class Employee {
@Id
@GeneratedValue // DON'T DO THIS, IT'S SLOW
public Long id;
}
This results in sequential IDs that are not optimal for Cloud Spanner and requires locking of the hibernate_sequence
table for inserts.
You should use locally-generated UUID key values instead:
@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.
Cloud Spanner does not support table names with catalog and schema components:
// Not supported: `public.store.book` is not a valid Cloud Spanner table name reference.
@Table(
catalog = "public",
schema = "store",
name = "book"
)
// Supported.
@Table(
name = "book"
)
Hibernate’s hibernate.hbm2ddl.auto
setting controls the framework’s schema modification behavior that occurs during start-up.
The following settings are available:
-
none: do nothing.
-
validate: validate the schema, makes no changes to the database.
-
update: update the schema.
-
create: creates 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.
The dialect does not currently set default values based on the @ColumnDefault
annotation,
because NULL
values aren’t specially handled and are stored just like other values by Cloud Spanner and its driver.
Cloud Spanner does not provide native support for arbitrary-precision decimal numbers, such as NUMERIC
and DECIMAL
.
As a result, the driver and dialect do not support decimal and arbitrary-precision Java types such as java.math.BigInteger
and java.math.BigDecimal
.
If you are using entities that are related by inheritance with the @Inheritance(strategy = InheritanceType.JOINED)
:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Payment {
@Id
@GeneratedValue
private Long id;
private Long amount;
}
@Entity
public class WireTransferPayment extends Payment {
}
@Entity
public class CreditCardPayment extends Payment {
}
You must set the hibernate.hql.bulk_id_strategy
setting in hibernate.properties
to org.hibernate.hql.spi.id.inline.InlineIdsOrClauseBulkIdStrategy
.
This is because Hibernate’s default behavior (PersistentTableBulkIdStrategy
) attempts to create intermediate tables to handle delete and update operations on the multiple tables that represent a JOINED
inheritance hierarchy, but these table creations statements do not conform to Cloud Spanner DDL.
Using one of the Inline
bulk-ID strategy classes given above resolves this issue.