Added Starbucks sample for Spring Data REST.
This commit is contained in:
1
pom.xml
1
pom.xml
@@ -18,6 +18,7 @@
|
||||
<modules>
|
||||
<module>jpa</module>
|
||||
<module>mongodb</module>
|
||||
<module>rest</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
||||
21
rest/pom.xml
Normal file
21
rest/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-data-rest-examples</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-examples</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data REST - Examples</name>
|
||||
<description>Sample projects for Spring Data REST</description>
|
||||
|
||||
<modules>
|
||||
<module>starbucks</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
17
rest/starbucks/README.md
Normal file
17
rest/starbucks/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Spring Data REST - Starbucks example
|
||||
|
||||
This sample app exposes 10843 Starbucks coffee shops via a RESTful API that allows to access the stores in a hypermedia based way and exposes a resource to execute geo-location search for coffee shops.
|
||||
|
||||
## Quickstart
|
||||
|
||||
1. Install MongoDB (http://www.mongodb.org/downloads, unzip, run `bin/mongod --dbpath=data`)
|
||||
2. Build and run the app (`mvn spring-boot:run`)
|
||||
3. Access the root resource (`curl http://localhost:8080`) and traverse hyperlinks.
|
||||
4. Or access the location search directly (e.g. `localhost:8080/stores/search/findByAddressLocationNear?location=40.740337,-73.995146&distance=0.5miles`)
|
||||
|
||||
## Technologies used
|
||||
|
||||
- Spring Data REST & Spring Data MongoDB
|
||||
- MongoDB
|
||||
- Spring Batch (to read the CSV file containing the store data and pipe it into MongoDB)
|
||||
- Spring Boot
|
||||
41
rest/starbucks/pom.xml
Normal file
41
rest/starbucks/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-data-rest-starbucks</artifactId>
|
||||
|
||||
<name>Spring Data REST - Starbucks Example</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-rest-examples</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<mongodb.version>2.12.1</mongodb.version>
|
||||
<spring-data-mongodb.version>1.5.0.RC1</spring-data-mongodb.version>
|
||||
<spring-data-rest.version>2.1.0.RC1</spring-data-rest.version>
|
||||
<spring-hateoas.version>0.11.0.RELEASE</spring-hateoas.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.batch</groupId>
|
||||
<artifactId>spring-batch-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
33
rest/starbucks/src/main/java/example/stores/Address.java
Normal file
33
rest/starbucks/src/main/java/example/stores/Address.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.stores;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
|
||||
|
||||
/**
|
||||
* Value object to represent an {@link Address}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
public class Address {
|
||||
|
||||
private final String street, city, zip;
|
||||
private final @GeoSpatialIndexed Point location;
|
||||
}
|
||||
49
rest/starbucks/src/main/java/example/stores/Store.java
Normal file
49
rest/starbucks/src/main/java/example/stores/Store.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.stores;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
/**
|
||||
* Entity to represent a {@link Store}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Data
|
||||
@Document
|
||||
public class Store {
|
||||
|
||||
private final @Id String id;
|
||||
private final String name;
|
||||
private final Address address;
|
||||
|
||||
public Store(String name, Address address) {
|
||||
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
protected Store() {
|
||||
|
||||
this.id = null;
|
||||
this.name = null;
|
||||
this.address = null;
|
||||
}
|
||||
}
|
||||
38
rest/starbucks/src/main/java/example/stores/StoreApp.java
Normal file
38
rest/starbucks/src/main/java/example/stores/StoreApp.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.stores;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
|
||||
|
||||
/**
|
||||
* Spring configuration class main application bootstrap point.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
// Activates Spring Data REST
|
||||
@Import(RepositoryRestMvcConfiguration.class)
|
||||
public class StoreApp {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(StoreApp.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.stores;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.file.FlatFileItemReader;
|
||||
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
|
||||
import org.springframework.batch.item.file.mapping.FieldSetMapper;
|
||||
import org.springframework.batch.item.file.separator.DefaultRecordSeparatorPolicy;
|
||||
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.BindException;
|
||||
|
||||
/**
|
||||
* Component initializing a hand full of Starbucks stores and persisting them through a {@link StoreRepository}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class StoreInitializer {
|
||||
|
||||
@Autowired
|
||||
public StoreInitializer(StoreRepository repository, MongoOperations operations) throws Exception {
|
||||
|
||||
if (repository.count() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Store> stores = readStores();
|
||||
log.info("Importing {} stores into MongoDB…", stores.size());
|
||||
repository.save(stores);
|
||||
log.info("Successfully imported {} stores.", repository.count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a file {@code starbucks.csv} from the class path and parses it into {@link Store} instances about to
|
||||
* persisted.
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static List<Store> readStores() throws Exception {
|
||||
|
||||
ClassPathResource resource = new ClassPathResource("starbucks.csv");
|
||||
Scanner scanner = new Scanner(resource.getInputStream());
|
||||
String line = scanner.nextLine();
|
||||
scanner.close();
|
||||
|
||||
FlatFileItemReader<Store> itemReader = new FlatFileItemReader<Store>();
|
||||
itemReader.setResource(resource);
|
||||
|
||||
// DelimitedLineTokenizer defaults to comma as its delimiter
|
||||
DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
|
||||
tokenizer.setNames(line.split(","));
|
||||
tokenizer.setStrict(false);
|
||||
|
||||
DefaultLineMapper<Store> lineMapper = new DefaultLineMapper<Store>();
|
||||
lineMapper.setLineTokenizer(tokenizer);
|
||||
lineMapper.setFieldSetMapper(StoreFieldSetMapper.INSTANCE);
|
||||
itemReader.setLineMapper(lineMapper);
|
||||
itemReader.setRecordSeparatorPolicy(new DefaultRecordSeparatorPolicy());
|
||||
itemReader.setLinesToSkip(1);
|
||||
itemReader.open(new ExecutionContext());
|
||||
|
||||
List<Store> stores = new ArrayList<>();
|
||||
Store store = null;
|
||||
|
||||
do {
|
||||
|
||||
store = itemReader.read();
|
||||
|
||||
if (store != null) {
|
||||
stores.add(store);
|
||||
}
|
||||
|
||||
} while (store != null);
|
||||
|
||||
return stores;
|
||||
}
|
||||
|
||||
private static enum StoreFieldSetMapper implements FieldSetMapper<Store> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.file.mapping.FieldSetMapper#mapFieldSet(org.springframework.batch.item.file.transform.FieldSet)
|
||||
*/
|
||||
@Override
|
||||
public Store mapFieldSet(FieldSet fields) throws BindException {
|
||||
|
||||
Point location = new Point(fields.readDouble("Longitude"), fields.readDouble("Latitude"));
|
||||
Address address = new Address(fields.readString("Street Address"), fields.readString("City"),
|
||||
fields.readString("Zip"), location);
|
||||
|
||||
return new Store(fields.readString("Name"), address);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.stores;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
|
||||
/**
|
||||
* Repository interface for out-of-the-box paginating access to {@link Store}s and a query method to find stores by
|
||||
* location and distance.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface StoreRepository extends PagingAndSortingRepository<Store, String> {
|
||||
|
||||
@RestResource(rel = "by-location")
|
||||
Page<Store> findByAddressLocationNear(//
|
||||
@Param("location") Point location, @Param("distance") Distance distance, Pageable pageable);
|
||||
}
|
||||
32530
rest/starbucks/src/main/resources/starbucks.csv
Normal file
32530
rest/starbucks/src/main/resources/starbucks.csv
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.stores;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link StoreRepository}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = StoreApp.class)
|
||||
public class StoreRepositoryIntegrationTests {
|
||||
|
||||
@Autowired StoreRepository repository;
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void clearDb() {
|
||||
repository.deleteAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findsStoresByLocation() {
|
||||
|
||||
Point location = new Point(-73.995146, 40.740337);
|
||||
Store store = new Store("Foo", new Address("street", "city", "zip", location));
|
||||
|
||||
store = repository.save(store);
|
||||
|
||||
Page<Store> stores = repository.findByAddressLocationNear(location, new Distance(1.0, Metrics.KILOMETERS),
|
||||
new PageRequest(0, 10));
|
||||
|
||||
assertThat(stores.getContent(), hasSize(1));
|
||||
assertThat(stores.getContent(), hasItem(store));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user