Initial commit.

Moved Spring Data JPA examples from spring-data-jpa-examples repo to this one. Added samples for MongoDB.
This commit is contained in:
Oliver Gierke
2014-01-31 14:19:08 +01:00
parent 4c7d78a410
commit 4b11a9dc8f
89 changed files with 3905 additions and 3 deletions

View File

@@ -0,0 +1,35 @@
/*
* 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 org.springframework.data.examples.mongodb.customer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.data.mongodb.core.geo.Point;
/**
* A domain object to capture addresses.
*
* @author Oliver Gierke
*/
@Getter
@RequiredArgsConstructor
public class Address {
private final Point location;
private String street;
private String zipCode;
}

View File

@@ -0,0 +1,54 @@
/*
* 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 org.springframework.data.examples.mongodb.customer;
import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.util.Assert;
/**
* An entity to represent a customer.
*
* @author Oliver Gierke
*/
@Data
@Document
public class Customer {
private ObjectId id;
private String firstname, lastname;
@GeoSpatialIndexed(name = "address.location")//
private Address address;
/**
* Creates a new {@link Customer} with the given firstname and lastname.
*
* @param firstname must not be {@literal null} or empty.
* @param lastname must not be {@literal null} or empty.
*/
public Customer(String firstname, String lastname) {
Assert.hasText(firstname, "Firstname must not be null or empty!");
Assert.hasText(lastname, "Lastname must not be null or empty!");
this.firstname = firstname;
this.lastname = lastname;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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 org.springframework.data.examples.mongodb.customer;
import java.util.List;
import org.bson.types.ObjectId;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.repository.CrudRepository;
/**
* Repository interface to manage {@link Customer} instances.
*
* @author Oliver Gierke
*/
public interface CustomerRepository extends CrudRepository<Customer, ObjectId> {
/**
* Derived query using dynamic sort information.
*
* @param lastname
* @param sort
* @return
*/
List<Customer> findByLastname(String lastname, Sort sort);
/**
* Showcase for a repository query using geo-spatial functionality.
*
* @param point
* @param distance
* @return
*/
GeoResults<Customer> findByAddressLocationNear(Point point, Distance distance);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2013-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 org.springframework.data.examples.mongodb.shop;
import java.util.List;
import lombok.Value;
/**
* A DTO to represent invoices.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
@Value
public class Invoice {
private final String orderId;
private final double taxAmount;
private final double netAmount;
private final double totalAmount;
private final List<LineItem> items;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2013-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 org.springframework.data.examples.mongodb.shop;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.data.annotation.PersistenceConstructor;
/**
* A line item.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
@Data
@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor))
public class LineItem {
private final String caption;
private final double price;
int quantity = 1;
public LineItem(String caption, double price, int quantity) {
this(caption, price);
this.quantity = quantity;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2013-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 org.springframework.data.examples.mongodb.shop;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* An entity representing an {@link Order}. Note how we don't need any MongoDB mapping annotations as {@code id} is
* recognized as the id property by default.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
@Data
@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor))
@Document
public class Order {
private final String id;
private final String customerId;
private final Date orderDate;
private final List<LineItem> items;
/**
* Creates a new {@link Order} for the given customer id and order date.
*
* @param customerId
* @param orderDate
*/
public Order(String customerId, Date orderDate) {
this(null, customerId, orderDate, new ArrayList<LineItem>());
}
/**
* Adds a {@link LineItem} to the {@link Order}.
*
* @param item
* @return
*/
public Order addItem(LineItem item) {
this.items.add(item);
return this;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2013-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 org.springframework.data.examples.mongodb.shop;
import org.springframework.data.repository.CrudRepository;
/**
* A repository interface assembling CRUD functionality as well as the API to invoke the methods implemented manually.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
public interface OrderRepository extends CrudRepository<Order, String>, OrderRepositoryCustom {
}

View File

@@ -0,0 +1,32 @@
/*
* 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 org.springframework.data.examples.mongodb.shop;
/**
* The interface for repository functionality that will be implemented manually.
*
* @author Oliver Gierke
*/
interface OrderRepositoryCustom {
/**
* Creates an {@link Invoice} for the given {@link Order}.
*
* @param order must not be {@literal null}.
* @return
*/
Invoice getInvoiceFor(Order order);
}

View File

@@ -0,0 +1,67 @@
/*
* 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 org.springframework.data.examples.mongodb.shop;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
/**
* The manual implementation parts for {@link OrderRepository}. This will automatically be picked up by the Spring Data
* infrastructure as we follow the naming convention of extending the core repository interface's name with {@code Impl}
* .
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class OrderRepositoryImpl implements OrderRepositoryCustom {
private MongoOperations operations;
private double taxRate = 0.19;
/**
* The implementation uses the MongoDB aggregation framework support Spring Data provides as well as SpEL expressions
* to define arithmetical expressions. Note how we work with property names only and don't have to mitigate the nested
* {@code $_id} fields MongoDB usually requires.
*
* @see org.springframework.data.examples.mongodb.shop.OrderRepositoryCustom#getInvoiceFor(org.springframework.data.examples.mongodb.shop.Order)
*/
@Override
public Invoice getInvoiceFor(Order order) {
AggregationResults<Invoice> results = operations.aggregate(newAggregation(Order.class, //
match(where("id").is(order.getId())), //
unwind("items"), //
project("id", "customerId", "items") //
.andExpression("'$items.price' * '$items.quantity'").as("lineTotal"), //
group("id") //
.sum("lineTotal").as("netAmount") //
.addToSet("items").as("items"), //
project("id", "items", "netAmount") //
.and("orderId").previousOperation() //
.andExpression("netAmount * [0]", taxRate).as("taxAmount") //
.andExpression("netAmount * (1 + [0])", taxRate).as("totalAmount") //
), Invoice.class);
return results.getUniqueMappedResult();
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 org.springframework.data.examples.mongodb;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
/**
* Test configuration to connect to a MongoDB named "test" and using a {@link MongoClient}. Also enables Spring Data
* repositories for MongoDB.
*
* @author Oliver Gierke
*/
@Configuration
@EnableMongoRepositories
public class TestConfiguration extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return "test";
}
@Override
public Mongo mongo() throws Exception {
return new MongoClient();
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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 org.springframework.data.examples.mongodb.customer;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.examples.mongodb.TestConfiguration;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.geo.Metrics;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.querydsl.QSort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Integration test for {@link CustomerRepository}.
*
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
public class CustomerRepositoryIntegrationTest {
@Autowired CustomerRepository repository;
Customer dave, oliver, carter;
@Before
public void setUp() {
repository.deleteAll();
dave = repository.save(new Customer("Dave", "Matthews"));
oliver = repository.save(new Customer("Oliver August", "Matthews"));
carter = repository.save(new Customer("Carter", "Beauford"));
}
/**
* Test case to show that automatically generated ids are assigned to the domain objects.
*/
@Test
public void setsIdOnSave() {
Customer dave = repository.save(new Customer("Dave", "Matthews"));
assertThat(dave.getId(), is(notNullValue()));
}
/**
* Test case to show the usage of the Querydsl-specific {@link QSort} to define the sort order in a type-safe way.
*/
@Test
public void findCustomersUsingQuerydslSort() {
QCustomer customer = QCustomer.customer;
List<Customer> result = repository.findByLastname("Matthews", new QSort(customer.firstname.asc()));
assertThat(result, hasSize(2));
assertThat(result.get(0), is(dave));
assertThat(result.get(1), is(oliver));
}
/**
* Test case to show the usage of the geo-spatial APIs to lookup people within a given distance of a reference point.
*/
@Test
public void exposesGeoSpatialFunctionality() {
Customer ollie = new Customer("Oliver", "Gierke");
ollie.setAddress(new Address(new Point(52.52548, 13.41477)));
ollie = repository.save(ollie);
Point referenceLocation = new Point(52.51790, 13.41239);
Distance oneKilometer = new Distance(1, Metrics.KILOMETERS);
GeoResults<Customer> result = repository.findByAddressLocationNear(referenceLocation, oneKilometer);
assertThat(result.getContent(), hasSize(1));
assertThat(result.getContent().get(0).getDistance(), is(new Distance(0.8624842788060683, Metrics.KILOMETERS)));
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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 org.springframework.data.examples.mongodb.shop;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.number.IsCloseTo.*;
import static org.junit.Assert.*;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.examples.mongodb.TestConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Integration tests for {@link OrderRepository}.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
public class OrderRepositoryIntegrationTests {
@Autowired OrderRepository repository;
private final static LineItem product1 = new LineItem("p1", 1.23);
private final static LineItem product2 = new LineItem("p2", 0.87, 2);
private final static LineItem product3 = new LineItem("p3", 5.33);
@Before
public void setup() {
repository.deleteAll();
}
@Test
public void createsInvoiceViaAggregation() {
Order order = new Order("c42", new Date()).//
addItem(product1).addItem(product2).addItem(product3);
order = repository.save(order);
Invoice invoice = repository.getInvoiceFor(order);
assertThat(invoice, is(notNullValue()));
assertThat(invoice.getOrderId(), is(order.getId()));
assertThat(invoice.getNetAmount(), is(closeTo(8.3, 000001)));
assertThat(invoice.getTaxAmount(), is(closeTo(1.577, 000001)));
assertThat(invoice.getTotalAmount(), is(closeTo(9.877, 000001)));
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.data.mongodb" level="debug" />
<logger name="org.springframework.data.mongodb.core.aggregation" level="debug" />
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>