Skip to content
This repository was archived by the owner on Jan 19, 2022. It is now read-only.

Commit 10c3278

Browse files
Spanner getkey moved, added key conveniences (#510)
* Moved getkey to its own schema operations class. added conveniences for users to make keys * renamed classes * fixing integration test creds * testing * Revert "testing" This reverts commit 8fe60ed. * env var testing * reverted travis yml * reverted key file * renamed class, added javadoc
1 parent 2579918 commit 10c3278

File tree

12 files changed

+234
-39
lines changed

12 files changed

+234
-39
lines changed

spring-cloud-gcp-autoconfigure/src/main/java/org/springframework/cloud/gcp/autoconfigure/spanner/GcpSpannerAutoConfiguration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.cloud.gcp.data.spanner.core.SpannerMutationFactoryImpl;
3939
import org.springframework.cloud.gcp.data.spanner.core.SpannerOperations;
4040
import org.springframework.cloud.gcp.data.spanner.core.SpannerTemplate;
41+
import org.springframework.cloud.gcp.data.spanner.core.admin.SpannerSchemaUtils;
4142
import org.springframework.cloud.gcp.data.spanner.core.convert.MappingSpannerConverter;
4243
import org.springframework.cloud.gcp.data.spanner.core.convert.SpannerConverter;
4344
import org.springframework.cloud.gcp.data.spanner.core.mapping.SpannerMappingContext;
@@ -133,4 +134,12 @@ public SpannerMutationFactory spannerMutationFactory(
133134
SpannerMappingContext spannerMappingContext) {
134135
return new SpannerMutationFactoryImpl(spannerConverter, spannerMappingContext);
135136
}
137+
138+
@Bean
139+
@ConditionalOnMissingBean
140+
public SpannerSchemaUtils spannerSchemaUtils(
141+
SpannerMappingContext spannerMappingContext,
142+
SpannerConverter spannerConverter) {
143+
return new SpannerSchemaUtils(spannerMappingContext, spannerConverter);
144+
}
136145
}

spring-cloud-gcp-data-spanner/src/main/java/org/springframework/cloud/gcp/data/spanner/core/SpannerOperations.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,6 @@
3737
*/
3838
public interface SpannerOperations {
3939

40-
/**
41-
* Gets the key for the given object.
42-
* @param object
43-
* @return
44-
*/
45-
Key getId(Object object);
46-
4740
/**
4841
* Finds a single stored object using a key.
4942
* @param entityClass the type of the object to retrieve.

spring-cloud-gcp-data-spanner/src/main/java/org/springframework/cloud/gcp/data/spanner/core/SpannerTemplate.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,6 @@ public SpannerMappingContext getMappingContext() {
9292
return this.mappingContext;
9393
}
9494

95-
@Override
96-
public Key getId(Object object) {
97-
SpannerPersistentEntity persistentEntity = this.mappingContext
98-
.getPersistentEntity(object.getClass());
99-
return (Key) persistentEntity.getPropertyAccessor(object)
100-
.getProperty(persistentEntity.getIdProperty());
101-
}
102-
10395
@Override
10496
public <T> T find(Class<T> entityClass, Key key) {
10597
return find(entityClass, key, null);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2018 original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.gcp.data.spanner.core.admin;
18+
19+
import com.google.cloud.spanner.Key;
20+
21+
import org.springframework.cloud.gcp.data.spanner.core.convert.SpannerConverter;
22+
import org.springframework.cloud.gcp.data.spanner.core.mapping.SpannerMappingContext;
23+
import org.springframework.cloud.gcp.data.spanner.core.mapping.SpannerPersistentEntity;
24+
import org.springframework.util.Assert;
25+
26+
/**
27+
* Contains functions related to the table schema of entities.
28+
*
29+
* @author Chengyuan Zhao
30+
*/
31+
public class SpannerSchemaUtils {
32+
33+
private final SpannerMappingContext mappingContext;
34+
35+
private final SpannerConverter spannerConverter;
36+
37+
public SpannerSchemaUtils(SpannerMappingContext mappingContext,
38+
SpannerConverter spannerConverter) {
39+
Assert.notNull(mappingContext,
40+
"A valid mapping context for Spanner is required.");
41+
Assert.notNull(spannerConverter,
42+
"A valid results mapper for Spanner is required.");
43+
this.mappingContext = mappingContext;
44+
this.spannerConverter = spannerConverter;
45+
}
46+
47+
/**
48+
* Gets the key for the given object.
49+
* @param object
50+
* @return
51+
*/
52+
public Key getId(Object object) {
53+
SpannerPersistentEntity persistentEntity = this.mappingContext
54+
.getPersistentEntity(object.getClass());
55+
return (Key) persistentEntity.getPropertyAccessor(object)
56+
.getProperty(persistentEntity.getIdProperty());
57+
}
58+
}

spring-cloud-gcp-data-spanner/src/main/java/org/springframework/cloud/gcp/data/spanner/repository/SpannerRepository.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@
1616

1717
package org.springframework.cloud.gcp.data.spanner.repository;
1818

19-
import com.google.cloud.spanner.Key;
20-
2119
import org.springframework.cloud.gcp.data.spanner.core.SpannerOperations;
2220
import org.springframework.data.repository.PagingAndSortingRepository;
2321

2422
/**
2523
* @author Ray Tsang
2624
* @author Chengyuan Zhao
2725
*/
28-
public interface SpannerRepository<T> extends PagingAndSortingRepository<T, Key> {
26+
public interface SpannerRepository<T, ID> extends PagingAndSortingRepository<T, ID> {
2927

3028
/**
3129
* Gets a {@link SpannerOperations}, which allows more-direct access to Google Spanner

spring-cloud-gcp-data-spanner/src/main/java/org/springframework/cloud/gcp/data/spanner/repository/support/SimpleSpannerRepository.java

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616

1717
package org.springframework.cloud.gcp.data.spanner.repository.support;
1818

19+
import java.util.Arrays;
1920
import java.util.Optional;
21+
import java.util.function.Function;
2022

2123
import com.google.cloud.spanner.Key;
2224
import com.google.cloud.spanner.KeySet;
2325

2426
import org.springframework.cloud.gcp.data.spanner.core.SpannerOperations;
27+
import org.springframework.cloud.gcp.data.spanner.core.mapping.SpannerDataException;
2528
import org.springframework.cloud.gcp.data.spanner.repository.SpannerRepository;
2629
import org.springframework.data.domain.Page;
2730
import org.springframework.data.domain.Pageable;
@@ -31,7 +34,7 @@
3134
/**
3235
* @author Chengyuan Zhao
3336
*/
34-
public class SimpleSpannerRepository<T> implements SpannerRepository<T> {
37+
public class SimpleSpannerRepository implements SpannerRepository {
3538

3639
private final SpannerOperations spannerOperations;
3740

@@ -67,13 +70,14 @@ public Iterable saveAll(Iterable entities) {
6770
}
6871

6972
@Override
70-
public Optional findById(Key key) {
73+
public Optional findById(Object key) {
7174
Assert.notNull(key, "A non-null ID is required.");
72-
return Optional.ofNullable(this.spannerOperations.find(this.entityType, key));
75+
return doIfKey(key, k -> Optional
76+
.ofNullable(this.spannerOperations.find(this.entityType, k)));
7377
}
7478

7579
@Override
76-
public boolean existsById(Key key) {
80+
public boolean existsById(Object key) {
7781
Assert.notNull(key, "A non-null ID is required.");
7882
return findById(key).isPresent();
7983
}
@@ -84,10 +88,10 @@ public Iterable findAll() {
8488
}
8589

8690
@Override
87-
public Iterable findAllById(Iterable<Key> iterable) {
91+
public Iterable findAllById(Iterable iterable) {
8892
KeySet.Builder builder = KeySet.newBuilder();
89-
for (Key id : iterable) {
90-
builder.addKey(id);
93+
for (Object id : iterable) {
94+
doIfKey(id, k -> builder.addKey(k));
9195
}
9296
return this.spannerOperations.find(this.entityType, builder.build());
9397
}
@@ -98,9 +102,12 @@ public long count() {
98102
}
99103

100104
@Override
101-
public void deleteById(Key key) {
105+
public void deleteById(Object key) {
102106
Assert.notNull(key, "A non-null ID is required.");
103-
this.spannerOperations.delete(this.entityType, key);
107+
doIfKey(key, k -> {
108+
this.spannerOperations.delete(this.entityType, k);
109+
return null;
110+
});
104111
}
105112

106113
@Override
@@ -129,4 +136,26 @@ public Iterable findAll(Sort sort) {
129136
public Page findAll(Pageable pageable) {
130137
return this.spannerOperations.findAll(this.entityType, pageable);
131138
}
139+
140+
private <T> T doIfKey(Object key, Function<Key, T> operation) {
141+
Key k;
142+
boolean isIterable = Iterable.class.isAssignableFrom(key.getClass());
143+
boolean isArray = Object[].class.isAssignableFrom(key.getClass());
144+
if (isIterable || isArray) {
145+
Key.Builder kb = Key.newBuilder();
146+
for (Object keyPart : (isArray ? (Arrays.asList((Object[]) key))
147+
: ((Iterable) key))) {
148+
kb.appendObject(keyPart);
149+
}
150+
k = kb.build();
151+
if (k.size() == 0) {
152+
throw new SpannerDataException(
153+
"A key must have at least one component, but 0 were given.");
154+
}
155+
}
156+
else {
157+
k = key instanceof Key ? (Key) key : Key.of(key);
158+
}
159+
return operation.apply(k);
160+
}
132161
}

spring-cloud-gcp-data-spanner/src/test/java/org/springframework/cloud/gcp/data/spanner/core/SpannerTemplateTests.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -455,15 +455,6 @@ public void findAllPageableTest() {
455455
assertEquals("c", ((TestEntity) page.getContent().get(2)).id);
456456
}
457457

458-
@Test
459-
public void getIdTest() {
460-
TestEntity t = new TestEntity();
461-
t.id = "aaa";
462-
t.id2 = 3L;
463-
assertEquals(Key.newBuilder().append(t.id).append(t.id2).build(),
464-
this.spannerTemplate.getId(t));
465-
}
466-
467458
@Table(name = "custom_test_table")
468459
private static class TestEntity {
469460
@PrimaryKeyColumn(keyOrder = 1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2018 original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.gcp.data.spanner.core.admin;
18+
19+
import java.util.List;
20+
21+
import com.google.cloud.ByteArray;
22+
import com.google.cloud.spanner.Key;
23+
import org.junit.Before;
24+
import org.junit.Test;
25+
26+
import org.springframework.cloud.gcp.data.spanner.core.convert.MappingSpannerConverter;
27+
import org.springframework.cloud.gcp.data.spanner.core.mapping.Column;
28+
import org.springframework.cloud.gcp.data.spanner.core.mapping.ColumnInnerType;
29+
import org.springframework.cloud.gcp.data.spanner.core.mapping.PrimaryKeyColumn;
30+
import org.springframework.cloud.gcp.data.spanner.core.mapping.SpannerMappingContext;
31+
import org.springframework.cloud.gcp.data.spanner.core.mapping.Table;
32+
33+
import static org.junit.Assert.assertEquals;
34+
35+
/**
36+
* @author Chengyuan Zhao
37+
*/
38+
public class SpannerSchemaUtilsTests {
39+
40+
private SpannerMappingContext spannerMappingContext;
41+
42+
private SpannerSchemaUtils spannerSchemaUtils;
43+
44+
@Before
45+
public void setUp() {
46+
this.spannerMappingContext = new SpannerMappingContext();
47+
this.spannerSchemaUtils = new SpannerSchemaUtils(
48+
this.spannerMappingContext,
49+
new MappingSpannerConverter(this.spannerMappingContext));
50+
}
51+
52+
@Test
53+
public void getIdTest() {
54+
TestEntity t = new TestEntity();
55+
t.id = "aaa";
56+
t.id2 = 3L;
57+
assertEquals(Key.newBuilder().append(t.id).append(t.id2).build(),
58+
this.spannerSchemaUtils.getId(t));
59+
}
60+
61+
@Table(name = "custom_test_table")
62+
private static class TestEntity {
63+
@PrimaryKeyColumn(keyOrder = 1)
64+
String id;
65+
66+
@PrimaryKeyColumn(keyOrder = 2)
67+
long id2;
68+
69+
@Column(name = "custom_col")
70+
String something;
71+
72+
@Column(name = "")
73+
String other;
74+
75+
ByteArray bytes;
76+
77+
@ColumnInnerType(innerType = ByteArray.class)
78+
List<ByteArray> bytesList;
79+
80+
@ColumnInnerType(innerType = Integer.class)
81+
List<Integer> integerList;
82+
83+
double[] doubles;
84+
}
85+
}

spring-cloud-gcp-data-spanner/src/test/java/org/springframework/cloud/gcp/data/spanner/repository/support/SpannerRepositoryImplTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818

1919
import java.util.Arrays;
2020

21+
import com.google.cloud.Timestamp;
2122
import com.google.cloud.spanner.Key;
2223
import com.google.cloud.spanner.KeySet;
2324
import org.junit.Test;
2425

2526
import org.springframework.cloud.gcp.data.spanner.core.SpannerOperations;
27+
import org.springframework.cloud.gcp.data.spanner.core.mapping.SpannerDataException;
2628
import org.springframework.data.domain.Pageable;
2729
import org.springframework.data.domain.Sort;
2830

@@ -135,6 +137,36 @@ public void findByIdTest() {
135137
verify(operations, times(1)).find(eq(Object.class), eq(key));
136138
}
137139

140+
@Test
141+
public void findByIdSingleItemTest() {
142+
SpannerOperations operations = mock(SpannerOperations.class);
143+
Key key = Key.of("key");
144+
Object ret = new Object();
145+
when(operations.find(eq(Object.class), eq(key))).thenReturn(ret);
146+
assertEquals(ret, new SimpleSpannerRepository(operations, Object.class)
147+
.findById("key").get());
148+
verify(operations, times(1)).find(eq(Object.class), eq(key));
149+
}
150+
151+
@Test
152+
public void findByIdListItemsTest() {
153+
SpannerOperations operations = mock(SpannerOperations.class);
154+
Timestamp timestamp = Timestamp.ofTimeMicroseconds(333);
155+
Key key = Key.of("key", timestamp);
156+
Object ret = new Object();
157+
when(operations.find(eq(Object.class), eq(key))).thenReturn(ret);
158+
assertEquals(ret, new SimpleSpannerRepository(operations, Object.class)
159+
.findById(new Object[] { "key", timestamp }).get());
160+
verify(operations, times(1)).find(eq(Object.class), eq(key));
161+
}
162+
163+
@Test(expected = SpannerDataException.class)
164+
public void findByIdEmptyKeyTest() {
165+
SpannerOperations operations = mock(SpannerOperations.class);
166+
new SimpleSpannerRepository(operations, Object.class).findById(new Object[] {})
167+
.get();
168+
}
169+
138170
@Test
139171
public void existsByIdTestFound() {
140172
SpannerOperations operations = mock(SpannerOperations.class);

spring-cloud-gcp-data-spanner/src/test/java/org/springframework/cloud/gcp/data/spanner/test/domain/TradeRepository.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818

1919
import java.util.List;
2020

21+
import com.google.cloud.spanner.Key;
22+
2123
import org.springframework.cloud.gcp.data.spanner.repository.SpannerRepository;
2224
import org.springframework.cloud.gcp.data.spanner.repository.query.Query;
2325

24-
public interface TradeRepository extends SpannerRepository<Trade> {
26+
public interface TradeRepository extends SpannerRepository<Trade, Key> {
2527

2628
List<Trade> findByTraderId(String traderId);
2729

0 commit comments

Comments
 (0)