Reflect grouping in directory structure

This commit is contained in:
Andy Wilkinson
2022-10-28 14:36:50 +01:00
parent 393e1cb6bf
commit 7112a4b6a4
788 changed files with 9 additions and 100 deletions

View File

@@ -0,0 +1,33 @@
package com.example.data.mongodb;
import java.time.Duration;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
import org.springframework.aot.smoketest.support.assertj.AssertableOutput;
import org.springframework.aot.smoketest.support.junit.ApplicationTest;
import static org.assertj.core.api.Assertions.assertThat;
@ApplicationTest
class DataMongoDbApplicationAotTests {
@Test
void findAll(AssertableOutput output) {
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
assertThat(output).hasSingleLineContaining("findAll(): Person{firstname='first-1', lastname='last-1'}")
.hasSingleLineContaining("findAll(): Person{firstname='first-2', lastname='last-2'}")
.hasSingleLineContaining("findAll(): Person{firstname='first-3', lastname='last-3'}");
});
}
@Test
void findByLastName(AssertableOutput output) {
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
assertThat(output)
.hasSingleLineContaining("findByLastname(): Person{firstname='first-3', lastname='last-3'}");
});
}
}

View File

@@ -0,0 +1,328 @@
package com.example.data.mongodb;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
@Component
class CLR implements CommandLineRunner {
@Autowired
private MongoTemplate template;
@Autowired
private PersonRepository personRepository;
@Autowired
private OrderRepository orderRepository;
@Autowired
private CustomerRepository customerRepository;
@Override
public void run(String... args) {
initializeDatabase(args);
LineItem product1 = new LineItem("p1", 1.23);
LineItem product2 = new LineItem("p2", 0.87, 2);
LineItem product3 = new LineItem("p3", 5.33);
log("\n\n\n---- INT REPO ----");
log("-----------------\n\n\n");
runSaveThenFindAll(product1, product2, product3);
runPagingAndSorting(product1, product2, product3);
runPartTreeQuery(product1, product2, product3);
runAnnotatedQuery(product1, product2, product3);
runAnnotatedAggregations(product1, product2, product3);
runCustomImplementation(product1, product2, product3);
runResultProjection(product1, product2, product3);
runCustomConversions();
runWithDbRefs(product1, product2, product3);
runWithDocumentReferences(product1, product2, product3);
runQueryByExample(product1);
personRepository.save(new Person("first-1", "last-1"));
personRepository.save(new Person("first-2", "last-2"));
personRepository.save(new Person("first-3", "last-3"));
for (Person person : this.personRepository.findAll()) {
System.out.printf("findAll(): %s%n", person);
}
for (Person person : this.personRepository.findByLastname("last-3")) {
System.out.printf("findByLastname(): %s%n", person);
}
}
// Prepare Collections to avoid timeouts on slow ci/docker/...
private void initializeDatabase(String... args) {
template.execute(db -> {
Set<String> collectionNames = db.listCollectionNames().into(new HashSet<>());
if (!collectionNames.contains("order")) {
db.createCollection("order");
}
if (collectionNames.contains("coupon")) {
template.execute(Coupon.class, mongoCollection -> mongoCollection.deleteMany(new Document()));
}
else {
db.createCollection("coupon");
}
return "ok";
});
}
// Basic save and findAll with Repository
private void runSaveThenFindAll(LineItem product1, LineItem product2, LineItem product3) {
log("---- FIND ALL ----");
orderRepository.deleteAll();
Order order = newOrder("c42", product1, product2, product3);
orderRepository.save(order);
Iterable<Order> all = orderRepository.findAll();
all.forEach(this::log);
log("-----------------\n\n\n");
}
// Paging and Sorting with Repository
private void runPagingAndSorting(LineItem product1, LineItem product2, LineItem product3) {
log("---- Paging / Sorting ----");
orderRepository.deleteAll();
orderRepository.save(newOrder("c42", product1));
orderRepository.save(newOrder("c42", product2));
orderRepository.save(newOrder("c42", product3));
orderRepository.save(newOrder("b12", product1));
orderRepository.save(newOrder("b12", product1));
// sort
List<Order> sortedByCustomer = orderRepository.findBy(Sort.by("customerId"));
log("sortedByCustomer: %s", sortedByCustomer);
// page
Page<Order> c42_page0 = orderRepository.findByCustomerId("c42", PageRequest.of(0, 2));
log("c42_page0: %s", c42_page0);
Page<Order> c42_page1 = orderRepository.findByCustomerId("c42", c42_page0.nextPageable());
log("c42_page1: %s", c42_page1);
// slice
Slice<Order> c42_slice0 = orderRepository.findSliceByCustomerId("c42", PageRequest.of(0, 2));
log("c42_slice0: %s", c42_slice0);
log("-----------------\n\n\n");
}
// Part Tree Query
private void runPartTreeQuery(LineItem product1, LineItem product2, LineItem product3) {
log("---- PART TREE QUERY ----");
orderRepository.deleteAll();
Order order = newOrder("c42", product1, product2, product3);
orderRepository.save(order);
List<Order> byCustomerId = orderRepository.findByCustomerId(order.getCustomerId());
byCustomerId.forEach(this::log);
log("-----------------\n\n\n");
}
// Query using Annotation (@Query(..))
private void runAnnotatedQuery(LineItem product1, LineItem product2, LineItem product3) {
log("---- ANNOTATED QUERY ----");
orderRepository.deleteAll();
Order order = newOrder("c42", product1, product2, product3);
orderRepository.save(order);
List<Order> byCustomerId = orderRepository.findByCustomerViaAnnotation(order.getCustomerId());
byCustomerId.forEach(this::log);
log("-----------------\n\n\n");
}
// Query with Aggregations (e.g. sum, total)
private void runAnnotatedAggregations(LineItem product1, LineItem product2, LineItem product3) {
log("---- ANNOTATED AGGREGATIONS ----");
orderRepository.deleteAll();
orderRepository.save(newOrder("c42", product1));
orderRepository.save(newOrder("c42", product2));
orderRepository.save(newOrder("c42", product3));
orderRepository.save(newOrder("b12", product1));
orderRepository.save(newOrder("b12", product1));
List<OrdersPerCustomer> result = orderRepository.totalOrdersPerCustomer(Sort.by(Sort.Order.desc("total")));
log("result: %s", result);
// assertThat(result).containsExactly(new OrdersPerCustomer("c42", 3L), new
// OrdersPerCustomer("b12", 2L));
log("-----------------\n\n\n");
}
// Query as a Custom Implementation
private void runCustomImplementation(LineItem product1, LineItem product2, LineItem product3) {
log("---- CUSTOM IMPLEMENTATION ----");
orderRepository.deleteAll();
Order order = newOrder("c42", product1, product2, product3);
order = orderRepository.save(order);
Invoice invoice = orderRepository.getInvoiceFor(order);
log("invoice: %s", invoice);
log("-----------------\n\n\n");
}
// Query with Result Projection
private void runResultProjection(LineItem product1, LineItem product2, LineItem product3) {
log("---- RESULT PROJECTION ----");
orderRepository.deleteAll();
Order order = newOrder("c42", product1, product2, product3);
orderRepository.save(order);
List<OrderProjection> result = orderRepository.findOrderProjectionByCustomerId(order.getCustomerId());
result.forEach(it -> log("OrderProjection(%s){id=%s, customerId=%s}", it.getClass().getSimpleName(), it.getId(),
it.getCustomerId()));
log("-----------------\n\n\n");
}
// Query using Custom Conversions
private void runCustomConversions() {
log("---- CUSTOM CONVERSION ----");
customerRepository.deleteAll();
customerRepository.save(new Customer("c-1", "c", "42"));
Document saved = template.execute(Customer.class, collection -> {
return collection.find(new Document("_id", "c-1")).first();
});
log("Raw Document: %s", saved);
if (!saved.get("name").equals("c; 42")) {
throw new RuntimeException("Custom Conversion is broken");
}
Optional<Customer> byId = customerRepository.findById("c-1");
log("Domain Object: %s", byId.get());
log("-----------------\n\n\n");
}
// Query for entity with DBRefs
private void runWithDbRefs(LineItem product1, LineItem product2, LineItem product3) {
log("---- DBREFs ----");
orderRepository.deleteAll();
Coupon coupon = new Coupon("X3R");
template.insert(coupon);
Order order = newOrder("c42", product1, product2, product3);
order.setCoupon(coupon);
order.setReduction(coupon);
order.setSimpleRef(coupon);
orderRepository.save(order);
Optional<Order> loaded = orderRepository.findById(order.getId());
log("simple ref (no proxy): %s", loaded.get().getSimpleRef().getCode());
log("lazyLoading (aot): %s", loaded.get().getCoupon().getCode());
log("lazyLoading (jdk): %s", loaded.get().getReduction().getId());
log("-----------------\n\n\n");
}
// Query for entity with Document References
private void runWithDocumentReferences(LineItem product1, LineItem product2, LineItem product3) {
log("---- Document References ----");
orderRepository.deleteAll();
Discount discount = new Discount(30F);
template.insert(discount);
Order order = newOrder("c42", product1, product2, product3);
order.setDocumentRef(discount);
order.setLazyDocumentRef(discount);
orderRepository.save(order);
Optional<Order> loaded = orderRepository.findById(order.getId());
log("document ref (no proxy): %s", loaded.get().getDocumentRef().getPercentage());
log("lazy document ref (aot): %s", loaded.get().lazyDocumentRef);
log("-----------------\n\n\n");
}
private void runQueryByExample(LineItem product1) {
log("---- QUERY BY EXAMPLE ----");
orderRepository.deleteAll();
Order order1 = orderRepository.save(newOrder("c42", product1));
Order order2 = orderRepository.save(newOrder("b12", product1));
Example<Order> example = Example.of(newOrder(order1.getCustomerId(), order1.getOrderDate()),
ExampleMatcher.matching().withIgnorePaths("items"));
Iterable<Order> result = orderRepository.findAll(example);
log("result: %s", result);
log("-----------------\n\n\n");
}
private Order newOrder(String customerId, LineItem... items) {
return newOrder(customerId, new Date(), items);
}
private Order newOrder(String customerId, Date date, LineItem... items) {
Order order = new Order(customerId, date);
Arrays.stream(items).forEach(order::addItem);
return order;
}
private void log(Object value) {
log(String.valueOf(value), new Object[0]);
}
private void log(String message, Object... arguments) {
String messageNewline = String.valueOf(message).endsWith("\n") ? message : message + "\n";
System.out.printf(messageNewline, arguments);
System.out.flush();
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Coupon implements PriceReduction {
private @Id String id;
private String code;
public Coupon(String code) {
this.code = code;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Customer {
private String id;
private Name name;
public Customer() {
}
public Customer(String id, String firstname, String lastname) {
this.id = id;
this.name = new Name(firstname, lastname);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import org.springframework.data.repository.CrudRepository;
interface CustomerRepository extends CrudRepository<Customer, String> {
}

View File

@@ -0,0 +1,64 @@
package com.example.data.mongodb;
import java.util.Arrays;
import java.util.Optional;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.mongodb.MongoManagedTypes;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
@SpringBootApplication
@EnableMongoAuditing(auditorAwareRef = "fixedAuditor")
public class DataMongoDbApplication {
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(DataMongoDbApplication.class, args);
Thread.currentThread().join(); // To be able to measure memory consumption
}
@Bean
AuditorAware<String> fixedAuditor() {
return () -> Optional.of("Douglas Adams");
}
@Bean
public MongoCustomConversions mongoCustomConversions() {
return new MongoCustomConversions(Arrays.asList(new StringToNameConverter(), new NameToStringConverter()));
}
@Bean
public MongoManagedTypes managedTypes() {
return MongoManagedTypes.from(Customer.class, Order.class);
}
@ReadingConverter
public static class StringToNameConverter implements Converter<String, Name> {
@Override
public Name convert(String source) {
String[] args = source.split(";");
return new Name(args[0].trim(), args[1].trim());
}
}
@WritingConverter
public static class NameToStringConverter implements Converter<Name, String> {
@Override
public String convert(Name source) {
return source.getFirstname() + "; " + source.getLastname();
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Discount implements PriceReduction {
private @Id String id;
private Float percentage;
public Discount(Float percentage) {
this.percentage = percentage;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Float getPercentage() {
return percentage;
}
public void setPercentage(Float percentage) {
this.percentage = percentage;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import java.util.List;
public class Invoice {
private final String orderId;
private final double taxAmount;
private final double netAmount;
private final double totalAmount;
private final List<LineItem> items;
public Invoice(String orderId, double taxAmount, double netAmount, double totalAmount, List<LineItem> items) {
this.orderId = orderId;
this.taxAmount = taxAmount;
this.netAmount = netAmount;
this.totalAmount = totalAmount;
this.items = items;
}
public String getOrderId() {
return orderId;
}
public double getTaxAmount() {
return taxAmount;
}
public double getNetAmount() {
return netAmount;
}
public double getTotalAmount() {
return totalAmount;
}
public List<LineItem> getItems() {
return items;
}
@Override
public String toString() {
return "Invoice{" + "orderId='" + orderId + '\'' + ", taxAmount=" + taxAmount + ", netAmount=" + netAmount
+ ", totalAmount=" + totalAmount + ", items=" + items + '}';
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import org.springframework.data.annotation.PersistenceConstructor;
public class LineItem {
private final String caption;
private final double price;
int quantity = 1;
public LineItem(String caption, double price) {
this.caption = caption;
this.price = price;
}
@PersistenceConstructor
public LineItem(String caption, double price, int quantity) {
this(caption, price);
this.quantity = quantity;
}
public String getCaption() {
return caption;
}
public double getPrice() {
return price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
@Override
public String toString() {
return "LineItem{" + "caption='" + caption + '\'' + ", price=" + price + ", quantity=" + quantity + '}';
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
public class Name {
private String firstname;
private String lastname;
public Name(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexResolver;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.index.PartialIndexFilter;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.query.Criteria;
@Document
public class Order {
private String id;
@Indexed
private String customerId;
@Field("order-date")
private Date orderDate;
private List<LineItem> items;
@DBRef
private Coupon simpleRef;
@DBRef(lazy = true) // TODO: ClassProxies
private Coupon coupon;
@DBRef(lazy = true) // JdkProxy
private PriceReduction reduction;
@DocumentReference
private Discount documentRef;
// @DocumentReference(lazy = true) // TODO: ClassProxies
Discount lazyDocumentRef;
@CreatedDate
Instant createdAt;
@CreatedBy
String createdBy;
@LastModifiedDate
Instant modifiedAt;
@LastModifiedBy
String modifiedBy;
protected Order() {
}
protected Order(String customerId) {
this(customerId, null);
}
@PersistenceConstructor
public Order(String id, String customerId, Date orderDate, List<LineItem> items) {
this.id = id;
this.customerId = customerId;
this.orderDate = orderDate;
this.items = items;
}
/**
* Creates a new {@link Order} for the given customer id and order date.
* @param customerId {@link String ID} of the {@literal customer}.
* @param orderDate {@link Date} of this {@link Order}.
*/
public Order(String customerId, Date orderDate) {
this(null, customerId, orderDate, new ArrayList<>());
}
/**
* Adds a {@link LineItem} to the {@link Order}.
* @param item {@link LineItem Item} ordered.
* @return this {@link Order}.
*/
public Order addItem(LineItem item) {
this.items.add(item);
return this;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
public List<LineItem> getItems() {
return items;
}
public void setItems(List<LineItem> items) {
this.items = items;
}
public Coupon getCoupon() {
return coupon;
}
public void setCoupon(Coupon coupon) {
this.coupon = coupon;
}
public void setReduction(PriceReduction reduction) {
this.reduction = reduction;
}
public PriceReduction getReduction() {
return reduction;
}
public Coupon getSimpleRef() {
return simpleRef;
}
public void setSimpleRef(Coupon simpleRef) {
this.simpleRef = simpleRef;
}
public Discount getDocumentRef() {
return documentRef;
}
public void setDocumentRef(Discount documentRef) {
this.documentRef = documentRef;
}
public Discount getLazyDocumentRef() {
return lazyDocumentRef;
}
public void setLazyDocumentRef(Discount lazyDocumentRef) {
this.lazyDocumentRef = lazyDocumentRef;
}
@Override
public String toString() {
return "Order{" + "id='" + id + '\'' + ", customerId='" + customerId + '\'' + ", orderDate=" + orderDate
+ ", items=" + items + ", coupon=" + coupon + ", reduction=" + reduction + ", createdAt=" + createdAt
+ ", createdBy=" + createdBy + ", modifiedAt=" + modifiedAt + ", modifiedBy=" + modifiedBy + '}';
}
// Reproducer for
// https://github.com/spring-projects-experimental/spring-native/issues/1376
public static List<IndexDefinition> getIndexes(IndexResolver indexResolver) {
List<IndexDefinition> ret = new ArrayList<>();
indexResolver.resolveIndexFor(Order.class).forEach(ret::add);
ret.add(new Index().unique().on("createdBy", Sort.Direction.ASC)
.partial(PartialIndexFilter.of(Criteria.where("createdBy").exists(true))));
return ret;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
public interface OrderProjection {
String getId();
String getCustomerId();
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.repository.Aggregation;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
public interface OrderRepository
extends CrudRepository<Order, String>, OrderRepositoryCustom, QueryByExampleExecutor<Order> {
List<Order> findByCustomerId(String customerId);
List<Order> findBy(Sort sort);
Page<Order> findByCustomerId(String customerId, Pageable pageable);
Slice<Order> findSliceByCustomerId(String customerId, Pageable pageable);
@Query("{ 'customerId' : '?0'}")
List<Order> findByCustomerViaAnnotation(String customer);
List<OrderProjection> findOrderProjectionByCustomerId(String customerId);
@Aggregation("{ $group : { _id : $customerId, total : { $sum : 1 } } }")
List<OrdersPerCustomer> totalOrdersPerCustomer(Sort sort);
@Aggregation(pipeline = { "{ $match : { customerId : ?0 } }", "{ $count : total }" })
Long totalOrdersForCustomer(String customerId);
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
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,57 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Indexed;
@Component
class OrderRepositoryImpl implements OrderRepositoryCustom {
private final MongoOperations operations;
private double taxRate = 0.19;
public OrderRepositoryImpl(MongoOperations operations) {
this.operations = operations;
}
@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,49 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
import org.springframework.data.annotation.Id;
/**
* @author Christoph Strobl
*/
public class OrdersPerCustomer {
@Id //
private String customerId;
private Long total;
public OrdersPerCustomer(String customerId, Long total) {
this.customerId = customerId;
this.total = total;
}
public String getCustomerId() {
return customerId;
}
public Long getTotal() {
return total;
}
@Override
public String toString() {
return "OrdersPerCustomer{" + "customerId='" + customerId + '\'' + ", total=" + total + '}';
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2020 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
*
* https://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 com.example.data.mongodb;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Person {
@Id
private String id;
private String firstname;
@Indexed
private String lastname;
public Person() {
}
public Person(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
@Override
public String toString() {
return "Person{" + "firstname='" + firstname + '\'' + ", lastname='" + lastname + '\'' + '}';
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2020 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
*
* https://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 com.example.data.mongodb;
import java.util.List;
import org.springframework.data.repository.ListCrudRepository;
public interface PersonRepository extends ListCrudRepository<Person, String> {
List<Person> findByLastname(String lastname);
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2022 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
*
* https://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 com.example.data.mongodb;
public interface PriceReduction {
String getId();
}

View File

@@ -0,0 +1,2 @@
spring.data.mongodb.host=${MONGO_HOST:localhost}
spring.data.mongodb.port=${MONGO_PORT_27017:27017}