diff --git a/google-cloud-spanner-hibernate-dialect/src/main/java/com/google/cloud/spanner/hibernate/SpannerDialect.java b/google-cloud-spanner-hibernate-dialect/src/main/java/com/google/cloud/spanner/hibernate/SpannerDialect.java
index 53f1437e..520b3b6d 100644
--- a/google-cloud-spanner-hibernate-dialect/src/main/java/com/google/cloud/spanner/hibernate/SpannerDialect.java
+++ b/google-cloud-spanner-hibernate-dialect/src/main/java/com/google/cloud/spanner/hibernate/SpannerDialect.java
@@ -19,6 +19,7 @@
package com.google.cloud.spanner.hibernate;
import static org.hibernate.type.SqlTypes.DECIMAL;
+import static org.hibernate.type.SqlTypes.JSON;
import static org.hibernate.type.SqlTypes.NUMERIC;
import com.google.cloud.spanner.hibernate.hints.ReplaceQueryPartsHint;
@@ -65,6 +66,8 @@
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.JsonAsStringJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
+import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
+import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.jboss.logging.Logger;
/** Hibernate 6.x dialect for Cloud Spanner. */
@@ -86,7 +89,7 @@ public int executeInsert(
private static class SpannerJsonJdbcType extends JsonAsStringJdbcType {
private SpannerJsonJdbcType() {
- super(SqlTypes.LONG32VARCHAR, null);
+ super(JSON, null);
}
@Override
@@ -176,6 +179,9 @@ protected String columnType(int sqlTypeCode) {
if (sqlTypeCode == DECIMAL || sqlTypeCode == NUMERIC) {
return "numeric";
}
+ if (sqlTypeCode == JSON) {
+ return "json";
+ }
return super.columnType(sqlTypeCode);
}
@@ -186,6 +192,14 @@ protected void registerColumnTypes(
JdbcTypeRegistry jdbcTypeRegistry =
typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
jdbcTypeRegistry.addDescriptorIfAbsent(new SpannerJsonJdbcType());
+ final DdlTypeRegistry ddlTypeRegistry =
+ typeContributions.getTypeConfiguration().getDdlTypeRegistry();
+ ddlTypeRegistry.addDescriptor(
+ new DdlTypeImpl(
+ SqlTypes.JSON,
+ columnType(SqlTypes.JSON),
+ castType(SqlTypes.JSON),
+ this));
}
@Override
diff --git a/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/pom.xml b/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/pom.xml
index 47a6ea51..567a0685 100644
--- a/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/pom.xml
+++ b/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/pom.xml
@@ -75,6 +75,11 @@
junit
test
+
+ org.testcontainers
+ testcontainers
+ test
+
diff --git a/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/main/java/com/google/cloud/spanner/sample/entities/Singer.java b/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/main/java/com/google/cloud/spanner/sample/entities/Singer.java
index 25aaa0b2..cef1ffc3 100644
--- a/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/main/java/com/google/cloud/spanner/sample/entities/Singer.java
+++ b/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/main/java/com/google/cloud/spanner/sample/entities/Singer.java
@@ -21,12 +21,15 @@
import com.google.cloud.spanner.hibernate.types.SpannerStringArray;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
+import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
import java.util.List;
import org.hibernate.annotations.Type;
@Entity
+@Table(indexes = {@Index(name = "idx_singer_active", columnList = "active")})
public class Singer extends AbstractNonInterleavedEntity {
@Column(length = 100)
diff --git a/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/main/java/com/google/cloud/spanner/sample/service/VenueService.java b/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/main/java/com/google/cloud/spanner/sample/service/VenueService.java
index 479d8292..c1e3e957 100644
--- a/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/main/java/com/google/cloud/spanner/sample/service/VenueService.java
+++ b/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/main/java/com/google/cloud/spanner/sample/service/VenueService.java
@@ -66,11 +66,15 @@ public List generateRandomVenues(int count) {
for (int i = 0; i < count; i++) {
Venue venue = new Venue();
venue.setName(randomDataService.getRandomVenueName());
- VenueDescription description = new VenueDescription();
- description.setCapacity(random.nextInt(100_000));
- description.setType(randomDataService.getRandomVenueType());
- description.setLocation(randomDataService.getRandomVenueLocation());
- venue.setDescription(description);
+ if (random.nextBoolean()) {
+ VenueDescription description = new VenueDescription();
+ description.setCapacity(random.nextInt(100_000));
+ description.setType(randomDataService.getRandomVenueType());
+ description.setLocation(randomDataService.getRandomVenueLocation());
+ venue.setDescription(description);
+ } else {
+ venue.setDescription(null);
+ }
venues.add(venue);
}
return repository.saveAll(venues);
diff --git a/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/test/java/com/google/cloud/spanner/sample/SampleApplicationEmulatorTest.java b/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/test/java/com/google/cloud/spanner/sample/SampleApplicationEmulatorTest.java
new file mode 100644
index 00000000..261ce7cb
--- /dev/null
+++ b/google-cloud-spanner-hibernate-samples/spring-data-jpa-full-sample/src/test/java/com/google/cloud/spanner/sample/SampleApplicationEmulatorTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019-2024 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.google.cloud.spanner.sample;
+
+import com.google.cloud.spanner.connection.SpannerPool;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.springframework.boot.SpringApplication;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+/** Runs the sample application on the emulator. */
+@RunWith(JUnit4.class)
+public class SampleApplicationEmulatorTest {
+ private static GenericContainer> emulator;
+
+ /** Starts the emulator in a test container. */
+ @BeforeClass
+ public static void setup() {
+ emulator =
+ new GenericContainer<>(
+ DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:latest"))
+ .withExposedPorts(9010)
+ .waitingFor(Wait.forListeningPort());
+ emulator.start();
+ }
+
+ /** Stops the emulator. */
+ @AfterClass
+ public static void cleanup() {
+ SpannerPool.closeSpannerPool();
+ if (emulator != null) {
+ emulator.stop();
+ }
+ }
+
+ @Test
+ public void testRunApplication() {
+ System.setProperty("spanner.emulator", "true");
+ System.setProperty("spanner.host", "//localhost:" + emulator.getMappedPort(9010));
+ SpringApplication.run(SampleApplication.class).close();
+ }
+
+}