diff --git a/rest/multi-store/README.adoc b/rest/multi-store/README.adoc new file mode 100644 index 00000000..816fcb3b --- /dev/null +++ b/rest/multi-store/README.adoc @@ -0,0 +1,199 @@ += Mixing Spring Data JPA and Spring Data MongoDB (with Spring Data REST for extra) + +This example app shows how to mix together several Spring Data projects. + +* Each data store's domain objects must be split up into distinct packages. That way, the Spring Data store can be configured. +* It's most important to make sure the underlying Spring Data layers work before adding Spring Data REST. Spring Data REST simply delegates and doesn't write extra functionality. +* With both set up properly, things should flow nicely. + +== Top level app + +[source,java] +---- +package example; + +// …import statements … + +@Configuration +@EnableAutoConfiguration +@EnableJpaRepositories +@EnableMongoRepositories +public class Application { + + private static final Logger log = LoggerFactory.getLogger(Application.class); + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Autowired PersonRepository personRepository; + @Autowired TreasureRepository treasureRepository; + + @PostConstruct + void checkitOut() { + + personRepository.save(new Person("Frodo", "Baggins")); + personRepository.save(new Person("Bilbo", "Baggins")); + + for (Person person : personRepository.findAll()) { + log.info("Hello " + person.toString()); + } + + treasureRepository.deleteAll(); + + Treasure sting = new Treasure(); + sting.setName("Sting"); + sting.setDescription("Made by the Elves"); + treasureRepository.save(sting); + + Treasure ring = new Treasure(); + ring.setName("Sauron's ring"); + ring.setDescription("One ring to rule them all"); + treasureRepository.save(ring); + + for (Treasure treasure : treasureRepository.findAll()) { + log.info("Found treasure " + treasure.toString()); + } + } +} +---- + +* Notice how `@EnableJpaRepositories(basePackageClasses = Person.class)` uses `Person` as a tip off of what package to start scanning for JPA entities +* Notice how `@EnableMongoRepositories(basePackageClasses = Treasure.class)` uses `Treasure` to tip off where to look for MongoDB documents +* The repository interfaces need to be in the separate packages as well. Otherwise, it doesn't work for the same reason. + +To prove it works, two people are created, stored, then retrieved. Then, two treasures are created, stored, and retrieved. + +NOTE: The treasures are first cleared out. Otherwise, every time you run this app, it will keep adding. + +After launching the app by running `mvn spring-boot:run`, from another shell, you can explore the RESTful interface. + +[source, bash] +---- +$ curl http://localhost:8080 +---- +[source, javascript] +---- +{ "_links" : { + "persons" : { + "href" : "http://localhost:8080/persons" + }, + "treasures" : { + "href" : "http://localhost:8080/treasures" + } + } +} +---- + +Here you can see the two resources backed by the repositories of the application, served up seamlessly. Following the `persons` link, you can see the two people created at startup. + +[source, bash] +---- +$ curl http://localhost:8080/persons +---- +[source, javascript] +---- +{ "_embedded" : { + "persons" : [ { + "firstName" : "Frodo", + "lastName" : "Baggins", + "_links" : { + "self" : { + "href" : "http://localhost:8080/persons/1" + } + } + }, { + "firstName" : "Bilbo", + "lastName" : "Baggins", + "_links" : { + "self" : { + "href" : "http://localhost:8080/persons/2" + } + } + } ] + } +} +---- + +You can easily create a new entry: + +[source, bash] +---- +$ curl -X POST -i -H "Content-Type:application/json" -d '{"firstName":"Greg", "lastName":"Turnquist"}' http://localhost:8080/persons +HTTP/1.1 201 Created +Server: Apache-Coyote/1.1 +Location: http://localhost:8080/persons/3 +Content-Length: 0 +Date: Tue, 22 Jul 2014 22:26:17 GMT + +$ curl http://localhost:8080/persons/3 +---- +[source, javascript] +---- +{ "firstName" : "Greg", + "lastName" : "Turnquist", + "_links" : { + "self" : { + "href" : "http://localhost:8080/persons/3" + } + } +} +---- + +Excellent! + +[source, bash] +---- +$ curl http://localhost:8080/treasures +---- +[source, javascript] +---- +{ "_embedded" : { + "treasures" : [ { + "name" : "Sting", + "description" : "Made by the Elves", + "_links" : { + "self" : { + "href" : "http://localhost:8080/treasures/53cedae13004309b49465fbc" + } + } + }, { + "name" : "Sauron's ring", + "description" : "One ring to rule them all", + "_links" : { + "self" : { + "href" : "http://localhost:8080/treasures/53cedae13004309b49465fbd" + } + } + } ] + } +} +---- + +If you venture into `treasures`, you can see the two treasures found in Middle Earth. Let's add another: + +[source, bash] +---- +$ curl -X POST -i -H "content-type:application/json" -d '{"name":"MacBook Pro", "description":"Tool of black magic"}' localhost:8080/treasures + +HTTP/1.1 201 Created +Server: Apache-Coyote/1.1 +Location: http://localhost:8080/treasures/53cee60a3004309b49465fbe +Content-Length: 0 +Date: Tue, 22 Jul 2014 22:30:34 GMT +---- +[source, bash] +---- +$ curl http://localhost:8080/treasures/53cee60a3004309b49465fbe +---- +[source, javascript] +---- +{ "name" : "MacBook Pro", + "description" : "Tool of black magic", + "_links" : { + "self" : { + "href" : "http://localhost:8080/treasures/53cee60a3004309b49465fbe" + } + } +} +---- \ No newline at end of file diff --git a/rest/multi-store/pom.xml b/rest/multi-store/pom.xml new file mode 100644 index 00000000..ab840241 --- /dev/null +++ b/rest/multi-store/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + spring-data-rest-multi-store + + Spring Data REST - Multi-store example + Spring Data REST example mixing Spring Data JPA and Spring Data MongoDB + + + org.springframework.data.examples + spring-data-rest-examples + 1.0.0.BUILD-SNAPSHOT + + + + Evans-BUILD-SNAPSHOT + 0.16.0.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + + diff --git a/rest/multi-store/src/main/java/example/Application.java b/rest/multi-store/src/main/java/example/Application.java new file mode 100644 index 00000000..eb578f13 --- /dev/null +++ b/rest/multi-store/src/main/java/example/Application.java @@ -0,0 +1,73 @@ +/* + * 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; + +import javax.annotation.PostConstruct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; + +import example.person.Person; +import example.person.PersonRepository; +import example.treasure.Treasure; +import example.treasure.TreasureRepository; + +/** + * Application configuration file. Used for bootstrap and data setup. + * + * @author Greg Turnquist + * @author Oliver Gierke + */ +@Configuration +@EnableAutoConfiguration +@EnableJpaRepositories +@EnableMongoRepositories +public class Application { + + private static final Logger log = LoggerFactory.getLogger(Application.class); + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Autowired PersonRepository personRepository; + @Autowired TreasureRepository treasureRepository; + + @PostConstruct + void checkitOut() { + + personRepository.save(new Person("Frodo", "Baggins")); + personRepository.save(new Person("Bilbo", "Baggins")); + + for (Person person : personRepository.findAll()) { + log.info("Hello " + person.toString()); + } + + treasureRepository.deleteAll(); + treasureRepository.save(new Treasure("Sting", "Made by the Elves")); + treasureRepository.save(new Treasure("Sauron's ring", "One ring to rule them all")); + + for (Treasure treasure : treasureRepository.findAll()) { + log.info("Found treasure " + treasure.toString()); + } + } +} diff --git a/rest/multi-store/src/main/java/example/person/Person.java b/rest/multi-store/src/main/java/example/person/Person.java new file mode 100644 index 00000000..cd7536cf --- /dev/null +++ b/rest/multi-store/src/main/java/example/person/Person.java @@ -0,0 +1,43 @@ +/* + * 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.person; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +/** + * A person. + * + * @author Greg Turnquist + * @author Oliver Gierke + */ +@Entity +@Data +@RequiredArgsConstructor +public class Person { + + private @Id @GeneratedValue Long id; + private final String firstName; + private final String lastName; + + protected Person() { + this(null, null); + } +} diff --git a/rest/multi-store/src/main/java/example/person/PersonRepository.java b/rest/multi-store/src/main/java/example/person/PersonRepository.java new file mode 100644 index 00000000..7a691808 --- /dev/null +++ b/rest/multi-store/src/main/java/example/person/PersonRepository.java @@ -0,0 +1,26 @@ +/* + * 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.person; + +import org.springframework.data.repository.CrudRepository; + +/** + * A repository to manage {@link Person} instances. + * + * @author Greg Turnquist + * @author Oliver Gierke + */ +public interface PersonRepository extends CrudRepository {} diff --git a/rest/multi-store/src/main/java/example/treasure/Treasure.java b/rest/multi-store/src/main/java/example/treasure/Treasure.java new file mode 100644 index 00000000..693fb52e --- /dev/null +++ b/rest/multi-store/src/main/java/example/treasure/Treasure.java @@ -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.treasure; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * A treasure. + * + * @author Greg Turnquist + * @author Oliver Gierke + */ +@Data +@Document +@RequiredArgsConstructor +public class Treasure { + + private @Id String id; + private final String name; + private final String description; +} diff --git a/rest/multi-store/src/main/java/example/treasure/TreasureRepository.java b/rest/multi-store/src/main/java/example/treasure/TreasureRepository.java new file mode 100644 index 00000000..f8be6e18 --- /dev/null +++ b/rest/multi-store/src/main/java/example/treasure/TreasureRepository.java @@ -0,0 +1,26 @@ +/* + * 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.treasure; + +import org.springframework.data.repository.CrudRepository; + +/** + * A repository to manage {@link Treasure}s. + * + * @author Greg Turnquist + * @author Oliver Gierke + */ +public interface TreasureRepository extends CrudRepository {} diff --git a/rest/pom.xml b/rest/pom.xml index 222f09b1..4b28962e 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -16,6 +16,7 @@ starbucks + multi-store