diff --git a/README.md b/README.md index e56fd25a4..0e445bf00 100644 --- a/README.md +++ b/README.md @@ -155,8 +155,19 @@ activate the `shade` profile like this: mvn package -Pshade ``` - - +## Samples + +See the [samples](/samples) directory for various examples for using the Spanner JDBC driver. + +- [snippets](/samples/snippets): Contains small code snippets for commonly used JDBC and Spanner + features. Refer to these snippets for examples on how to execute DDL and DML batches, use various + data types with the JDBC driver, execute various types of transactions (read/write, read-only, + Partitioned DML), use request and transaction tags, etc. +- [spring-data-jdbc](/samples/spring-data-jdbc): Contains a sample application that uses Spring Data + JDBC in combination with a Spanner PostgreSQL database. +- [spring-data-mybatis](/samples/spring-data-mybatis): Contains a sample application that uses + Spring Data MyBatis in combination with a Spanner PostgreSQL database. +- [quickperf](/samples/quickperf): Contains a simple benchmarking application. ## Troubleshooting diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index bc7048666..ccd66c4ea 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -15,6 +15,7 @@ com.google.cloud.samples shared-configuration 1.2.2 + diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 697da8e6c..56b88c472 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -122,6 +122,13 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + + **/SingerProto.java + + diff --git a/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java b/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java index a597d7430..2b340feb1 100644 --- a/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java @@ -16,6 +16,8 @@ package com.example.spanner.jdbc; +import com.example.spanner.jdbc.SingerProto.Genre; +import com.example.spanner.jdbc.SingerProto.SingerInfo; import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.cloud.spanner.DatabaseId; @@ -29,6 +31,8 @@ import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient; import com.google.cloud.spanner.admin.database.v1.DatabaseAdminSettings; import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; +import com.google.cloud.spanner.jdbc.ProtoEnumType; +import com.google.cloud.spanner.jdbc.ProtoMessageType; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.spanner.admin.database.v1.CreateDatabaseRequest; @@ -36,6 +40,8 @@ import com.google.spanner.admin.instance.v1.InstanceName; import com.google.spanner.v1.DatabaseName; import io.grpc.ManagedChannelBuilder; +import java.io.IOException; +import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -1576,6 +1582,98 @@ static void arrayOfStructAsQueryParameter( } } + static void protoColumns( + final String project, + final String instance, + final String database, + final Properties properties) throws SQLException, IOException { + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + project, instance, database), + properties)) { + // Create a PROTO BUNDLE and a table. + try (Statement statement = connection.createStatement(); + InputStream protoDescriptors = JdbcSample.class.getClassLoader() + .getResourceAsStream("com/example/spanner/jdbc/descriptors.pb")) { + if (protoDescriptors == null) { + throw new IllegalArgumentException("proto descriptors not found"); + } + + // Unwrap the CloudSpannerJdbcConnection interface to set the proto + // descriptors that should be used for the next DDL statements. + connection + .unwrap(CloudSpannerJdbcConnection.class) + .setProtoDescriptors(protoDescriptors); + // Execute the DDL statements as one batch. + // This will reduce execution time compared to executing each statement + // sequentially. + statement.addBatch("CREATE PROTO BUNDLE (\n" + + "examples.spanner.music.SingerInfo,\n" + + "examples.spanner.music.Genre,\n" + + ")"); + statement.addBatch("CREATE TABLE SingersWithProto (\n" + + " SingerId INT64 NOT NULL,\n" + + " SingerInfo examples.spanner.music.SingerInfo,\n" + + " SingerGenre examples.spanner.music.Genre,\n" + + ") PRIMARY KEY (SingerId)"); + statement.executeBatch(); + } + + // Insert a couple of rows using a prepared statement. + try (PreparedStatement statement = connection.prepareStatement( + "INSERT INTO SingersWithProto " + + "(SingerId, SingerInfo, SingerGenre) " + + "VALUES (?, ?, ?)")) { + int param = 0; + statement.setLong(++param, 1L); + statement.setObject(++param, + SingerInfo.newBuilder() + .setGenre(Genre.ROCK) + .setBirthDate("1998-07-04") + .setSingerId(1L) + .setNationality("ES") + .build(), ProtoMessageType.VENDOR_TYPE_NUMBER); + statement.setObject(++param, Genre.ROCK, + ProtoEnumType.VENDOR_TYPE_NUMBER); + statement.addBatch(); + + param = 0; + statement.setLong(++param, 2L); + statement.setObject(++param, + SingerInfo.newBuilder() + .setGenre(Genre.POP) + .setBirthDate("2001-12-03") + .setSingerId(2L) + .setNationality("FO") + .build(), ProtoMessageType.VENDOR_TYPE_NUMBER); + statement.setObject(++param, Genre.POP, + ProtoEnumType.VENDOR_TYPE_NUMBER); + statement.addBatch(); + + int[] updateCounts = statement.executeBatch(); + System.out.printf("Inserted %d singers\n", + Arrays.stream(updateCounts).sum()); + } + + // Read the inserted rows. + try (ResultSet resultSet = connection.createStatement() + .executeQuery("SELECT * FROM SingersWithProto")) { + while (resultSet.next()) { + long singerId = resultSet.getLong("SingerId"); + // Proto messages and proto enums can be retrieved with the + // ResultSet#getObject(int, Class) method. + // The Spanner JDBC driver automatically deserializes + // and converts the column to the Java class representation. + SingerInfo info = resultSet.getObject("SingerInfo", SingerInfo.class); + Genre genre = resultSet.getObject("SingerGenre", Genre.class); + System.out.printf("%d:\n%s\n%s\n", singerId, info, genre); + } + } + } + } + /** The expected number of command line arguments. */ private static final int NUM_EXPECTED_ARGS = 3; @@ -1761,6 +1859,13 @@ static boolean runGoogleSQLSample( database.getDatabase(), createProperties()); return true; + case "protocolumns": + protoColumns( + database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), + database.getDatabase(), + createProperties()); + return true; default: return false; } diff --git a/samples/snippets/src/main/java/com/example/spanner/jdbc/SingerProto.java b/samples/snippets/src/main/java/com/example/spanner/jdbc/SingerProto.java new file mode 100644 index 000000000..382456a2e --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/jdbc/SingerProto.java @@ -0,0 +1,1168 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: singer.proto + +package com.example.spanner.jdbc; + +public final class SingerProto { + private SingerProto() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + /** + * Protobuf enum {@code examples.spanner.music.Genre} + */ + public enum Genre + implements com.google.protobuf.ProtocolMessageEnum { + /** + * POP = 0; + */ + POP(0), + /** + * JAZZ = 1; + */ + JAZZ(1), + /** + * FOLK = 2; + */ + FOLK(2), + /** + * ROCK = 3; + */ + ROCK(3), + UNRECOGNIZED(-1), + ; + + /** + * POP = 0; + */ + public static final int POP_VALUE = 0; + /** + * JAZZ = 1; + */ + public static final int JAZZ_VALUE = 1; + /** + * FOLK = 2; + */ + public static final int FOLK_VALUE = 2; + /** + * ROCK = 3; + */ + public static final int ROCK_VALUE = 3; + + + public final int getNumber() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalArgumentException( + "Can't get the number of an unknown enum value."); + } + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static Genre valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static Genre forNumber(int value) { + switch (value) { + case 0: return POP; + case 1: return JAZZ; + case 2: return FOLK; + case 3: return ROCK; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + Genre> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Genre findValueByNumber(int number) { + return Genre.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalStateException( + "Can't get the descriptor of an unrecognized enum value."); + } + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return com.example.spanner.jdbc.SingerProto.getDescriptor().getEnumTypes().get(0); + } + + private static final Genre[] VALUES = values(); + + public static Genre valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + if (desc.getIndex() == -1) { + return UNRECOGNIZED; + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private Genre(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:examples.spanner.music.Genre) + } + + public interface SingerInfoOrBuilder extends + // @@protoc_insertion_point(interface_extends:examples.spanner.music.SingerInfo) + com.google.protobuf.MessageOrBuilder { + + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + boolean hasSingerId(); + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + long getSingerId(); + + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + boolean hasBirthDate(); + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + java.lang.String getBirthDate(); + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + com.google.protobuf.ByteString + getBirthDateBytes(); + + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + boolean hasNationality(); + /** + * optional string nationality = 3; + * @return The nationality. + */ + java.lang.String getNationality(); + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + com.google.protobuf.ByteString + getNationalityBytes(); + + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return Whether the genre field is set. + */ + boolean hasGenre(); + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return The enum numeric value on the wire for genre. + */ + int getGenreValue(); + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return The genre. + */ + com.example.spanner.jdbc.SingerProto.Genre getGenre(); + } + /** + * Protobuf type {@code examples.spanner.music.SingerInfo} + */ + public static final class SingerInfo extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:examples.spanner.music.SingerInfo) + SingerInfoOrBuilder { + private static final long serialVersionUID = 0L; + // Use SingerInfo.newBuilder() to construct. + private SingerInfo(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private SingerInfo() { + birthDate_ = ""; + nationality_ = ""; + genre_ = 0; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new SingerInfo(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.example.spanner.jdbc.SingerProto.internal_static_examples_spanner_music_SingerInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.example.spanner.jdbc.SingerProto.internal_static_examples_spanner_music_SingerInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.example.spanner.jdbc.SingerProto.SingerInfo.class, com.example.spanner.jdbc.SingerProto.SingerInfo.Builder.class); + } + + private int bitField0_; + public static final int SINGER_ID_FIELD_NUMBER = 1; + private long singerId_ = 0L; + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + @java.lang.Override + public boolean hasSingerId() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + @java.lang.Override + public long getSingerId() { + return singerId_; + } + + public static final int BIRTH_DATE_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object birthDate_ = ""; + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + @java.lang.Override + public boolean hasBirthDate() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + @java.lang.Override + public java.lang.String getBirthDate() { + java.lang.Object ref = birthDate_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + birthDate_ = s; + return s; + } + } + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getBirthDateBytes() { + java.lang.Object ref = birthDate_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + birthDate_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int NATIONALITY_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object nationality_ = ""; + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + @java.lang.Override + public boolean hasNationality() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional string nationality = 3; + * @return The nationality. + */ + @java.lang.Override + public java.lang.String getNationality() { + java.lang.Object ref = nationality_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + nationality_ = s; + return s; + } + } + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNationalityBytes() { + java.lang.Object ref = nationality_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nationality_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int GENRE_FIELD_NUMBER = 4; + private int genre_ = 0; + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return Whether the genre field is set. + */ + @java.lang.Override public boolean hasGenre() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return The enum numeric value on the wire for genre. + */ + @java.lang.Override public int getGenreValue() { + return genre_; + } + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return The genre. + */ + @java.lang.Override public com.example.spanner.jdbc.SingerProto.Genre getGenre() { + com.example.spanner.jdbc.SingerProto.Genre result = com.example.spanner.jdbc.SingerProto.Genre.forNumber(genre_); + return result == null ? com.example.spanner.jdbc.SingerProto.Genre.UNRECOGNIZED : result; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeInt64(1, singerId_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, birthDate_); + } + if (((bitField0_ & 0x00000004) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, nationality_); + } + if (((bitField0_ & 0x00000008) != 0)) { + output.writeEnum(4, genre_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(1, singerId_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, birthDate_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, nationality_); + } + if (((bitField0_ & 0x00000008) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(4, genre_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.example.spanner.jdbc.SingerProto.SingerInfo)) { + return super.equals(obj); + } + com.example.spanner.jdbc.SingerProto.SingerInfo other = (com.example.spanner.jdbc.SingerProto.SingerInfo) obj; + + if (hasSingerId() != other.hasSingerId()) return false; + if (hasSingerId()) { + if (getSingerId() + != other.getSingerId()) return false; + } + if (hasBirthDate() != other.hasBirthDate()) return false; + if (hasBirthDate()) { + if (!getBirthDate() + .equals(other.getBirthDate())) return false; + } + if (hasNationality() != other.hasNationality()) return false; + if (hasNationality()) { + if (!getNationality() + .equals(other.getNationality())) return false; + } + if (hasGenre() != other.hasGenre()) return false; + if (hasGenre()) { + if (genre_ != other.genre_) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasSingerId()) { + hash = (37 * hash) + SINGER_ID_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getSingerId()); + } + if (hasBirthDate()) { + hash = (37 * hash) + BIRTH_DATE_FIELD_NUMBER; + hash = (53 * hash) + getBirthDate().hashCode(); + } + if (hasNationality()) { + hash = (37 * hash) + NATIONALITY_FIELD_NUMBER; + hash = (53 * hash) + getNationality().hashCode(); + } + if (hasGenre()) { + hash = (37 * hash) + GENRE_FIELD_NUMBER; + hash = (53 * hash) + genre_; + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static com.example.spanner.jdbc.SingerProto.SingerInfo parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(com.example.spanner.jdbc.SingerProto.SingerInfo prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code examples.spanner.music.SingerInfo} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:examples.spanner.music.SingerInfo) + com.example.spanner.jdbc.SingerProto.SingerInfoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.example.spanner.jdbc.SingerProto.internal_static_examples_spanner_music_SingerInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.example.spanner.jdbc.SingerProto.internal_static_examples_spanner_music_SingerInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.example.spanner.jdbc.SingerProto.SingerInfo.class, com.example.spanner.jdbc.SingerProto.SingerInfo.Builder.class); + } + + // Construct using com.example.spanner.jdbc.SingerProto.SingerInfo.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + singerId_ = 0L; + birthDate_ = ""; + nationality_ = ""; + genre_ = 0; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.example.spanner.jdbc.SingerProto.internal_static_examples_spanner_music_SingerInfo_descriptor; + } + + @java.lang.Override + public com.example.spanner.jdbc.SingerProto.SingerInfo getDefaultInstanceForType() { + return com.example.spanner.jdbc.SingerProto.SingerInfo.getDefaultInstance(); + } + + @java.lang.Override + public com.example.spanner.jdbc.SingerProto.SingerInfo build() { + com.example.spanner.jdbc.SingerProto.SingerInfo result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.example.spanner.jdbc.SingerProto.SingerInfo buildPartial() { + com.example.spanner.jdbc.SingerProto.SingerInfo result = new com.example.spanner.jdbc.SingerProto.SingerInfo(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(com.example.spanner.jdbc.SingerProto.SingerInfo result) { + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.singerId_ = singerId_; + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.birthDate_ = birthDate_; + to_bitField0_ |= 0x00000002; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.nationality_ = nationality_; + to_bitField0_ |= 0x00000004; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.genre_ = genre_; + to_bitField0_ |= 0x00000008; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.example.spanner.jdbc.SingerProto.SingerInfo) { + return mergeFrom((com.example.spanner.jdbc.SingerProto.SingerInfo)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.example.spanner.jdbc.SingerProto.SingerInfo other) { + if (other == com.example.spanner.jdbc.SingerProto.SingerInfo.getDefaultInstance()) return this; + if (other.hasSingerId()) { + setSingerId(other.getSingerId()); + } + if (other.hasBirthDate()) { + birthDate_ = other.birthDate_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.hasNationality()) { + nationality_ = other.nationality_; + bitField0_ |= 0x00000004; + onChanged(); + } + if (other.hasGenre()) { + setGenre(other.getGenre()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + singerId_ = input.readInt64(); + bitField0_ |= 0x00000001; + break; + } // case 8 + case 18: { + birthDate_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + nationality_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 32: { + genre_ = input.readEnum(); + bitField0_ |= 0x00000008; + break; + } // case 32 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private long singerId_ ; + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + @java.lang.Override + public boolean hasSingerId() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + @java.lang.Override + public long getSingerId() { + return singerId_; + } + /** + * optional int64 singer_id = 1; + * @param value The singerId to set. + * @return This builder for chaining. + */ + public Builder setSingerId(long value) { + + singerId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * optional int64 singer_id = 1; + * @return This builder for chaining. + */ + public Builder clearSingerId() { + bitField0_ = (bitField0_ & ~0x00000001); + singerId_ = 0L; + onChanged(); + return this; + } + + private java.lang.Object birthDate_ = ""; + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + public boolean hasBirthDate() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + public java.lang.String getBirthDate() { + java.lang.Object ref = birthDate_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + birthDate_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + public com.google.protobuf.ByteString + getBirthDateBytes() { + java.lang.Object ref = birthDate_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + birthDate_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string birth_date = 2; + * @param value The birthDate to set. + * @return This builder for chaining. + */ + public Builder setBirthDate( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + birthDate_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * optional string birth_date = 2; + * @return This builder for chaining. + */ + public Builder clearBirthDate() { + birthDate_ = getDefaultInstance().getBirthDate(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + * optional string birth_date = 2; + * @param value The bytes for birthDate to set. + * @return This builder for chaining. + */ + public Builder setBirthDateBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + birthDate_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object nationality_ = ""; + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + public boolean hasNationality() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional string nationality = 3; + * @return The nationality. + */ + public java.lang.String getNationality() { + java.lang.Object ref = nationality_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + nationality_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + public com.google.protobuf.ByteString + getNationalityBytes() { + java.lang.Object ref = nationality_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nationality_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string nationality = 3; + * @param value The nationality to set. + * @return This builder for chaining. + */ + public Builder setNationality( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + nationality_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * optional string nationality = 3; + * @return This builder for chaining. + */ + public Builder clearNationality() { + nationality_ = getDefaultInstance().getNationality(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + * optional string nationality = 3; + * @param value The bytes for nationality to set. + * @return This builder for chaining. + */ + public Builder setNationalityBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + nationality_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + private int genre_ = 0; + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return Whether the genre field is set. + */ + @java.lang.Override public boolean hasGenre() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return The enum numeric value on the wire for genre. + */ + @java.lang.Override public int getGenreValue() { + return genre_; + } + /** + * optional .examples.spanner.music.Genre genre = 4; + * @param value The enum numeric value on the wire for genre to set. + * @return This builder for chaining. + */ + public Builder setGenreValue(int value) { + genre_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return The genre. + */ + @java.lang.Override + public com.example.spanner.jdbc.SingerProto.Genre getGenre() { + com.example.spanner.jdbc.SingerProto.Genre result = com.example.spanner.jdbc.SingerProto.Genre.forNumber(genre_); + return result == null ? com.example.spanner.jdbc.SingerProto.Genre.UNRECOGNIZED : result; + } + /** + * optional .examples.spanner.music.Genre genre = 4; + * @param value The genre to set. + * @return This builder for chaining. + */ + public Builder setGenre(com.example.spanner.jdbc.SingerProto.Genre value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + genre_ = value.getNumber(); + onChanged(); + return this; + } + /** + * optional .examples.spanner.music.Genre genre = 4; + * @return This builder for chaining. + */ + public Builder clearGenre() { + bitField0_ = (bitField0_ & ~0x00000008); + genre_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:examples.spanner.music.SingerInfo) + } + + // @@protoc_insertion_point(class_scope:examples.spanner.music.SingerInfo) + private static final com.example.spanner.jdbc.SingerProto.SingerInfo DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new com.example.spanner.jdbc.SingerProto.SingerInfo(); + } + + public static com.example.spanner.jdbc.SingerProto.SingerInfo getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SingerInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.example.spanner.jdbc.SingerProto.SingerInfo getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_examples_spanner_music_SingerInfo_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_examples_spanner_music_SingerInfo_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\014singer.proto\022\026examples.spanner.music\"\301" + + "\001\n\nSingerInfo\022\026\n\tsinger_id\030\001 \001(\003H\000\210\001\001\022\027\n" + + "\nbirth_date\030\002 \001(\tH\001\210\001\001\022\030\n\013nationality\030\003 " + + "\001(\tH\002\210\001\001\0221\n\005genre\030\004 \001(\0162\035.examples.spann" + + "er.music.GenreH\003\210\001\001B\014\n\n_singer_idB\r\n\013_bi" + + "rth_dateB\016\n\014_nationalityB\010\n\006_genre*.\n\005Ge" + + "nre\022\007\n\003POP\020\000\022\010\n\004JAZZ\020\001\022\010\n\004FOLK\020\002\022\010\n\004ROCK" + + "\020\003B)\n\030com.example.spanner.jdbcB\013SingerPr" + + "otoP\000b\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_examples_spanner_music_SingerInfo_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_examples_spanner_music_SingerInfo_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_examples_spanner_music_SingerInfo_descriptor, + new java.lang.String[] { "SingerId", "BirthDate", "Nationality", "Genre", "SingerId", "BirthDate", "Nationality", "Genre", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/samples/snippets/src/main/resources/com/example/spanner/jdbc/README.md b/samples/snippets/src/main/resources/com/example/spanner/jdbc/README.md new file mode 100644 index 000000000..4b689229b --- /dev/null +++ b/samples/snippets/src/main/resources/com/example/spanner/jdbc/README.md @@ -0,0 +1,7 @@ +#### To generate SingerProto.java and descriptors.pb file from singer.proto using `protoc` +```shell +cd samples/snippets/src/main/resources/ +protoc --proto_path=com/example/spanner/jdbc/ \ + --include_imports --descriptor_set_out=com/example/spanner/jdbc/descriptors.pb \ + --java_out=../java com/example/spanner/jdbc/singer.proto +``` diff --git a/samples/snippets/src/main/resources/com/example/spanner/jdbc/descriptors.pb b/samples/snippets/src/main/resources/com/example/spanner/jdbc/descriptors.pb new file mode 100644 index 000000000..2dfd90bae Binary files /dev/null and b/samples/snippets/src/main/resources/com/example/spanner/jdbc/descriptors.pb differ diff --git a/samples/snippets/src/main/resources/com/example/spanner/jdbc/singer.proto b/samples/snippets/src/main/resources/com/example/spanner/jdbc/singer.proto new file mode 100644 index 000000000..c6662e180 --- /dev/null +++ b/samples/snippets/src/main/resources/com/example/spanner/jdbc/singer.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package examples.spanner.music; + +option java_package = "com.example.spanner.jdbc"; +option java_outer_classname = "SingerProto"; +option java_multiple_files = false; + +message SingerInfo { + optional int64 singer_id = 1; + optional string birth_date = 2; + optional string nationality = 3; + optional Genre genre = 4; +} + +enum Genre { + POP = 0; + JAZZ = 1; + FOLK = 2; + ROCK = 3; +} diff --git a/samples/snippets/src/test/java/com/example/spanner/jdbc/JdbcSampleTest.java b/samples/snippets/src/test/java/com/example/spanner/jdbc/JdbcSampleTest.java index c1275f457..3f46e4d63 100644 --- a/samples/snippets/src/test/java/com/example/spanner/jdbc/JdbcSampleTest.java +++ b/samples/snippets/src/test/java/com/example/spanner/jdbc/JdbcSampleTest.java @@ -29,6 +29,7 @@ import static com.example.spanner.jdbc.JdbcSample.ddlBatchPostgreSQL; import static com.example.spanner.jdbc.JdbcSample.partitionedDml; import static com.example.spanner.jdbc.JdbcSample.partitionedDmlPostgreSQL; +import static com.example.spanner.jdbc.JdbcSample.protoColumns; import static com.example.spanner.jdbc.JdbcSample.queryData; import static com.example.spanner.jdbc.JdbcSample.queryDataPostgreSQL; import static com.example.spanner.jdbc.JdbcSample.queryDataWithNewColumn; @@ -248,6 +249,24 @@ public void testGoogleSQLSamples() throws Exception { result = runSample( () -> arrayOfStructAsQueryParameter(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties)); assertEquals("value1;1;\nvalue2;2;\n", result); + + result = runSample( + () -> protoColumns(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties)); + assertEquals("Inserted 2 singers\n" + + "2:\n" + + "singer_id: 2\n" + + "birth_date: \"2001-12-03\"\n" + + "nationality: \"FO\"\n" + + "genre: POP\n" + + "\n" + + "POP\n" + + "1:\n" + + "singer_id: 1\n" + + "birth_date: \"1998-07-04\"\n" + + "nationality: \"ES\"\n" + + "genre: ROCK\n" + + "\n" + + "ROCK\n", result); } @Test