3434import com .google .cloud .spanner .Struct ;
3535import com .google .cloud .spanner .TimestampBound ;
3636import com .google .cloud .spanner .TransactionContext ;
37+ import com .google .cloud .spanner .Value ;
3738import com .google .spanner .admin .database .v1 .CreateDatabaseMetadata ;
39+ import com .google .spanner .admin .database .v1 .UpdateDatabaseDdlMetadata ;
3840import java .util .ArrayList ;
3941import java .util .Arrays ;
4042import java .util .List ;
4850 * <li> Writing, reading and executing SQL queries.
4951 * <li> Writing data using a read-write transaction.
5052 * <li> Using an index to read and execute SQL queries over data.
53+ * <li> Using commit timestamp for tracking when a record was last updated.
5154 * </ul>
5255 */
5356public class SpannerSample {
@@ -84,6 +87,24 @@ static class Album {
8487 }
8588 }
8689
90+ /**
91+ * Class to contain performance sample data.
92+ */
93+ static class Performance {
94+
95+ final long singerId ;
96+ final long venueId ;
97+ final String eventDate ;
98+ final long revenue ;
99+
100+ Performance (long singerId , long venueId , String eventDate , long revenue ) {
101+ this .singerId = singerId ;
102+ this .venueId = venueId ;
103+ this .eventDate = eventDate ;
104+ this .revenue = revenue ;
105+ }
106+ }
107+
87108 // [START spanner_insert_data]
88109 static final List <Singer > SINGERS =
89110 Arrays .asList (
@@ -102,6 +123,14 @@ static class Album {
102123 new Album (2 , 3 , "Terrified" ));
103124 // [END spanner_insert_data]
104125
126+ // [START spanner_insert_data_with_timestamp_column]
127+ static final List <Performance > PERFORMANCES =
128+ Arrays .asList (
129+ new Performance (1 , 4 , "2017-10-05" , 11000 ),
130+ new Performance (1 , 19 , "2017-11-02" , 15000 ),
131+ new Performance (2 , 42 , "2017-12-23" , 7000 ));
132+ // [END spanner_insert_data_with_timestamp_column]
133+
105134 // [START spanner_create_database]
106135 static void createDatabase (DatabaseAdminClient dbAdminClient , DatabaseId id ) {
107136 Operation <Database , CreateDatabaseMetadata > op = dbAdminClient
@@ -126,6 +155,48 @@ static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) {
126155 }
127156 // [END spanner_create_database]
128157
158+ // [START spanner_create_table_with_timestamp_column]
159+ static void createTableWithTimestamp (DatabaseAdminClient dbAdminClient , DatabaseId id ) {
160+ Operation <Void , UpdateDatabaseDdlMetadata > op = dbAdminClient
161+ .updateDatabaseDdl (
162+ id .getInstanceId ().getInstance (),
163+ id .getDatabase (),
164+ Arrays .asList (
165+ "CREATE TABLE Performances (\n "
166+ + " SingerId INT64 NOT NULL,\n "
167+ + " VenueId INT64 NOT NULL,\n "
168+ + " EventDate Date,\n "
169+ + " Revenue INT64, \n "
170+ + " LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)\n "
171+ + ") PRIMARY KEY (SingerId, VenueId, EventDate),\n "
172+ + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE" ), null );
173+ op .waitFor ().getResult ();
174+ System .out .println ("Created Performances table in database: [" + id + "]" );
175+ }
176+ // [END spanner_create_table_with_timestamp_column]
177+
178+ // [START spanner_insert_data_with_timestamp_column]
179+ static void writeExampleDataWithTimestamp (DatabaseClient dbClient ) {
180+ List <Mutation > mutations = new ArrayList <>();
181+ for (Performance performance : PERFORMANCES ) {
182+ mutations .add (
183+ Mutation .newInsertBuilder ("Performances" )
184+ .set ("SingerId" )
185+ .to (performance .singerId )
186+ .set ("VenueId" )
187+ .to (performance .venueId )
188+ .set ("EventDate" )
189+ .to (performance .eventDate )
190+ .set ("Revenue" )
191+ .to (performance .revenue )
192+ .set ("LastUpdateTime" )
193+ .to (Value .COMMIT_TIMESTAMP )
194+ .build ());
195+ }
196+ dbClient .write (mutations );
197+ }
198+ // [END spanner_insert_data_with_timestamp_column]
199+
129200 // [START spanner_insert_data]
130201 static void writeExampleData (DatabaseClient dbClient ) {
131202 List <Mutation > mutations = new ArrayList <>();
@@ -422,6 +493,98 @@ static void readStaleData(DatabaseClient dbClient) {
422493 }
423494 // [END spanner_read_stale_data]
424495
496+ // [START spanner_add_timestamp_column]
497+ static void addCommitTimestamp (DatabaseAdminClient adminClient , DatabaseId dbId ) {
498+ adminClient .updateDatabaseDdl (dbId .getInstanceId ().getInstance (),
499+ dbId .getDatabase (),
500+ Arrays .asList (
501+ "ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMP "
502+ + "OPTIONS (allow_commit_timestamp=true)" ),
503+ null ).waitFor ();
504+ System .out .println ("Added LastUpdateTime as a commit timestamp column in Albums table." );
505+ }
506+ // [END spanner_add_timestamp_column]
507+
508+ // Before executing this method, a new column MarketingBudget has to be added to the Albums
509+ // table by applying the DDL statement "ALTER TABLE Albums ADD COLUMN MarketingBudget INT64".
510+ // In addition this update expects the LastUpdateTime column added by applying the DDL statement
511+ // "ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMP OPTIONS (allow_commit_timestamp=true)"
512+ // [START spanner_update_data_with_timestamp_column]
513+ static void updateWithTimestamp (DatabaseClient dbClient ) {
514+ // Mutation can be used to update/insert/delete a single row in a table. Here we use
515+ // newUpdateBuilder to create update mutations.
516+ List <Mutation > mutations =
517+ Arrays .asList (
518+ Mutation .newUpdateBuilder ("Albums" )
519+ .set ("SingerId" )
520+ .to (1 )
521+ .set ("AlbumId" )
522+ .to (1 )
523+ .set ("MarketingBudget" )
524+ .to (1000000 )
525+ .set ("LastUpdateTime" )
526+ .to (Value .COMMIT_TIMESTAMP )
527+ .build (),
528+ Mutation .newUpdateBuilder ("Albums" )
529+ .set ("SingerId" )
530+ .to (2 )
531+ .set ("AlbumId" )
532+ .to (2 )
533+ .set ("MarketingBudget" )
534+ .to (750000 )
535+ .set ("LastUpdateTime" )
536+ .to (Value .COMMIT_TIMESTAMP )
537+ .build ());
538+ // This writes all the mutations to Cloud Spanner atomically.
539+ dbClient .write (mutations );
540+ }
541+ // [END spanner_update_data_with_timestamp_column]
542+
543+ // [START spanner_query_data_with_timestamp_column]
544+ static void queryMarketingBudgetWithTimestamp (DatabaseClient dbClient ) {
545+ // Rows without an explicit value for MarketingBudget will have a MarketingBudget equal to
546+ // null.
547+ ResultSet resultSet =
548+ dbClient
549+ .singleUse ()
550+ .executeQuery (Statement .of (
551+ "SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime FROM Albums"
552+ + " ORDER BY LastUpdateTime DESC" ));
553+ while (resultSet .next ()) {
554+ System .out .printf (
555+ "%d %d %s %s\n " ,
556+ resultSet .getLong ("SingerId" ),
557+ resultSet .getLong ("AlbumId" ),
558+ // We check that the value is non null. ResultSet getters can only be used to retrieve
559+ // non null values.
560+ resultSet .isNull ("MarketingBudget" ) ? "NULL" : resultSet .getLong ("MarketingBudget" ),
561+ resultSet .isNull ("LastUpdateTime" ) ? "NULL" : resultSet .getTimestamp ("LastUpdateTime" ));
562+ }
563+ }
564+ // [END spanner_query_data_with_timestamp_column]
565+
566+ static void queryPerformancesTable (DatabaseClient dbClient ) {
567+ // Rows without an explicit value for Revenue will have a Revenue equal to
568+ // null.
569+ ResultSet resultSet =
570+ dbClient
571+ .singleUse ()
572+ .executeQuery (Statement .of (
573+ "SELECT SingerId, VenueId, EventDate, Revenue, LastUpdateTime FROM Performances"
574+ + " ORDER BY LastUpdateTime DESC" ));
575+ while (resultSet .next ()) {
576+ System .out .printf (
577+ "%d %d %s %s %s\n " ,
578+ resultSet .getLong ("SingerId" ),
579+ resultSet .getLong ("VenueId" ),
580+ resultSet .getDate ("EventDate" ),
581+ // We check that the value is non null. ResultSet getters can only be used to retrieve
582+ // non null values.
583+ resultSet .isNull ("Revenue" ) ? "NULL" : resultSet .getLong ("Revenue" ),
584+ resultSet .getTimestamp ("LastUpdateTime" ));
585+ }
586+ }
587+
425588 static void run (DatabaseClient dbClient , DatabaseAdminClient dbAdminClient , String command ,
426589 DatabaseId database ) {
427590 switch (command ) {
@@ -470,6 +633,24 @@ static void run(DatabaseClient dbClient, DatabaseAdminClient dbAdminClient, Stri
470633 case "readstaledata" :
471634 readStaleData (dbClient );
472635 break ;
636+ case "addcommittimestamp" :
637+ addCommitTimestamp (dbAdminClient , database );
638+ break ;
639+ case "updatewithtimestamp" :
640+ updateWithTimestamp (dbClient );
641+ break ;
642+ case "querywithtimestamp" :
643+ queryMarketingBudgetWithTimestamp (dbClient );
644+ break ;
645+ case "createtablewithtimestamp" :
646+ createTableWithTimestamp (dbAdminClient , database );
647+ break ;
648+ case "writewithtimestamp" :
649+ writeExampleDataWithTimestamp (dbClient );
650+ break ;
651+ case "queryperformancestable" :
652+ queryPerformancesTable (dbClient );
653+ break ;
473654 default :
474655 printUsageAndExit ();
475656 }
@@ -484,6 +665,44 @@ static void printUsageAndExit() {
484665 " SpannerExample createdatabase my-instance example-db" );
485666 System .err .println (
486667 " SpannerExample write my-instance example-db" );
668+ System .err .println (
669+ " SpannerExample query my-instance example-db" );
670+ System .err .println (
671+ " SpannerExample read my-instance example-db" );
672+ System .err .println (
673+ " SpannerExample addmarketingbudget my-instance example-db" );
674+ System .err .println (
675+ " SpannerExample update my-instance example-db" );
676+ System .err .println (
677+ " SpannerExample writetransaction my-instance example-db" );
678+ System .err .println (
679+ " SpannerExample querymarketingbudget my-instance example-db" );
680+ System .err .println (
681+ " SpannerExample addindex my-instance example-db" );
682+ System .err .println (
683+ " SpannerExample readindex my-instance example-db" );
684+ System .err .println (
685+ " SpannerExample queryindex my-instance example-db" );
686+ System .err .println (
687+ " SpannerExample addstoringindex my-instance example-db" );
688+ System .err .println (
689+ " SpannerExample readstoringindex my-instance example-db" );
690+ System .err .println (
691+ " SpannerExample readonlytransaction my-instance example-db" );
692+ System .err .println (
693+ " SpannerExample readstaledata my-instance example-db" );
694+ System .err .println (
695+ " SpannerExample addcommittimestamp my-instance example-db" );
696+ System .err .println (
697+ " SpannerExample updatewithtimestamp my-instance example-db" );
698+ System .err .println (
699+ " SpannerExample querywithtimestamp my-instance example-db" );
700+ System .err .println (
701+ " SpannerExample createtablewithtimestamp my-instance example-db" );
702+ System .err .println (
703+ " SpannerExample writewithtimestamp my-instance example-db" );
704+ System .err .println (
705+ " SpannerExample queryperformancestable my-instance example-db" );
487706 System .exit (1 );
488707 }
489708
0 commit comments