diff --git a/Chapter 5/mongodb-demo/.gitignore b/Chapter 5/mongodb-demo/.gitignore new file mode 100644 index 00000000..c456c4a3 --- /dev/null +++ b/Chapter 5/mongodb-demo/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/Chapter 5/mongodb-demo/pom.xml b/Chapter 5/mongodb-demo/pom.xml new file mode 100644 index 00000000..8474a258 --- /dev/null +++ b/Chapter 5/mongodb-demo/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.3.RELEASE + + + geektime.spring.data.reactive + mongodb-demo + 0.0.1-SNAPSHOT + mongodb-demo + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + + + + org.joda + joda-money + 1.0.1 + + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/MongodbDemoApplication.java b/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/MongodbDemoApplication.java new file mode 100644 index 00000000..11c08bfc --- /dev/null +++ b/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/MongodbDemoApplication.java @@ -0,0 +1,101 @@ +package geektime.spring.data.reactive.mongodbdemo; + +import geektime.spring.data.reactive.mongodbdemo.converter.MoneyReadConverter; +import geektime.spring.data.reactive.mongodbdemo.converter.MoneyWriteConverter; +import geektime.spring.data.reactive.mongodbdemo.model.Coffee; +import lombok.extern.slf4j.Slf4j; +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions; +import org.springframework.data.mongodb.core.query.Update; +import reactor.core.scheduler.Schedulers; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import static org.springframework.data.mongodb.core.query.Criteria.where; +import static org.springframework.data.mongodb.core.query.Query.query; + +@SpringBootApplication +@Slf4j +public class MongodbDemoApplication implements ApplicationRunner { + @Autowired + private ReactiveMongoTemplate mongoTemplate; + private CountDownLatch cdl = new CountDownLatch(2); + + public static void main(String[] args) { + SpringApplication.run(MongodbDemoApplication.class, args); + } + + @Bean + public MongoCustomConversions mongoCustomConversions() { + return new MongoCustomConversions( + Arrays.asList(new MoneyReadConverter(), + new MoneyWriteConverter())); + } + + @Override + public void run(ApplicationArguments args) throws Exception { +// startFromInsertion(() -> log.info("Runnable")); + startFromInsertion(() -> { + log.info("Runnable"); + decreaseHighPrice(); + }); + + log.info("after starting"); + +// decreaseHighPrice(); + + cdl.await(); + } + + private void startFromInsertion(Runnable runnable) { + mongoTemplate.insertAll(initCoffee()) + .publishOn(Schedulers.elastic()) + .doOnNext(c -> log.info("Next: {}", c)) + .doOnComplete(runnable) + .doFinally(s -> { + cdl.countDown(); + log.info("Finnally 1, {}", s); + }) + .count() + .subscribe(c -> log.info("Insert {} records", c)); + } + + private void decreaseHighPrice() { + mongoTemplate.updateMulti(query(where("price").gte(3000L)), + new Update().inc("price", -500L) + .currentDate("updateTime"), Coffee.class) + .doFinally(s -> { + cdl.countDown(); + log.info("Finnally 2, {}", s); + }) + .subscribe(r -> log.info("Result is {}", r)); + } + + private List initCoffee() { + Coffee espresso = Coffee.builder() + .name("espresso") + .price(Money.of(CurrencyUnit.of("CNY"), 20.0)) + .createTime(new Date()) + .updateTime(new Date()) + .build(); + Coffee latte = Coffee.builder() + .name("latte") + .price(Money.of(CurrencyUnit.of("CNY"), 30.0)) + .createTime(new Date()) + .updateTime(new Date()) + .build(); + + return Arrays.asList(espresso, latte); + } +} diff --git a/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/converter/MoneyReadConverter.java b/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/converter/MoneyReadConverter.java new file mode 100644 index 00000000..579e4ff0 --- /dev/null +++ b/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/converter/MoneyReadConverter.java @@ -0,0 +1,12 @@ +package geektime.spring.data.reactive.mongodbdemo.converter; + +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; +import org.springframework.core.convert.converter.Converter; + +public class MoneyReadConverter implements Converter { + @Override + public Money convert(Long aLong) { + return Money.ofMinor(CurrencyUnit.of("CNY"), aLong); + } +} diff --git a/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/converter/MoneyWriteConverter.java b/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/converter/MoneyWriteConverter.java new file mode 100644 index 00000000..a52b1376 --- /dev/null +++ b/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/converter/MoneyWriteConverter.java @@ -0,0 +1,11 @@ +package geektime.spring.data.reactive.mongodbdemo.converter; + +import org.joda.money.Money; +import org.springframework.core.convert.converter.Converter; + +public class MoneyWriteConverter implements Converter { + @Override + public Long convert(Money money) { + return money.getAmountMinorLong(); + } +} diff --git a/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/model/Coffee.java b/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/model/Coffee.java new file mode 100644 index 00000000..7eb18c3c --- /dev/null +++ b/Chapter 5/mongodb-demo/src/main/java/geektime/spring/data/reactive/mongodbdemo/model/Coffee.java @@ -0,0 +1,21 @@ +package geektime.spring.data.reactive.mongodbdemo.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.joda.money.Money; + +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Coffee { + private String id; + private String name; + private Money price; + private Date createTime; + private Date updateTime; +} diff --git a/Chapter 5/mongodb-demo/src/main/resources/application.properties b/Chapter 5/mongodb-demo/src/main/resources/application.properties new file mode 100644 index 00000000..38c73bce --- /dev/null +++ b/Chapter 5/mongodb-demo/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.data.mongodb.uri=mongodb://springbucks:springbucks@localhost:27017/springbucks \ No newline at end of file diff --git a/Chapter 5/mongodb-demo/src/test/java/geektime/spring/data/reactive/mongodbdemo/MongodbDemoApplicationTests.java b/Chapter 5/mongodb-demo/src/test/java/geektime/spring/data/reactive/mongodbdemo/MongodbDemoApplicationTests.java new file mode 100644 index 00000000..8b86be35 --- /dev/null +++ b/Chapter 5/mongodb-demo/src/test/java/geektime/spring/data/reactive/mongodbdemo/MongodbDemoApplicationTests.java @@ -0,0 +1,16 @@ +package geektime.spring.data.reactive.mongodbdemo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class MongodbDemoApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/Chapter 5/performance-aspect-demo/.gitignore b/Chapter 5/performance-aspect-demo/.gitignore new file mode 100644 index 00000000..c456c4a3 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/Chapter 5/performance-aspect-demo/pom.xml b/Chapter 5/performance-aspect-demo/pom.xml new file mode 100644 index 00000000..195251c0 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + geektime.spring + springbucks + 0.0.1-SNAPSHOT + springbucks + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.joda + joda-money + 1.0.1 + + + org.jadira.usertype + usertype.core + 6.0.1.GA + + + + p6spy + p6spy + 3.8.1 + + + + com.h2database + h2 + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/SpringBucksApplication.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/SpringBucksApplication.java new file mode 100644 index 00000000..cc0cda5d --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/SpringBucksApplication.java @@ -0,0 +1,50 @@ +package geektime.spring.springbucks; + +import geektime.spring.springbucks.model.Coffee; +import geektime.spring.springbucks.model.CoffeeOrder; +import geektime.spring.springbucks.model.OrderState; +import geektime.spring.springbucks.repository.CoffeeRepository; +import geektime.spring.springbucks.service.CoffeeOrderService; +import geektime.spring.springbucks.service.CoffeeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import java.util.Optional; + +@Slf4j +@EnableTransactionManagement +@SpringBootApplication +@EnableJpaRepositories +@EnableAspectJAutoProxy +public class SpringBucksApplication implements ApplicationRunner { + @Autowired + private CoffeeRepository coffeeRepository; + @Autowired + private CoffeeService coffeeService; + @Autowired + private CoffeeOrderService orderService; + + public static void main(String[] args) { + SpringApplication.run(SpringBucksApplication.class, args); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + log.info("All Coffee: {}", coffeeRepository.findAll()); + + Optional latte = coffeeService.findOneCoffee("Latte"); + if (latte.isPresent()) { + CoffeeOrder order = orderService.createOrder("Li Lei", latte.get()); + log.info("Update INIT to PAID: {}", orderService.updateState(order, OrderState.PAID)); + log.info("Update PAID to INIT: {}", orderService.updateState(order, OrderState.INIT)); + } + } +} + diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/aspect/PerformanceAspect.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/aspect/PerformanceAspect.java new file mode 100644 index 00000000..b091ba6d --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/aspect/PerformanceAspect.java @@ -0,0 +1,35 @@ +package geektime.spring.springbucks.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +@Aspect +@Component +@Slf4j +public class PerformanceAspect { +// @Around("execution(* geektime.spring.springbucks.repository..*(..))") + @Around("repositoryOps()") + public Object logPerformance(ProceedingJoinPoint pjp) throws Throwable { + long startTime = System.currentTimeMillis(); + String name = "-"; + String result = "Y"; + try { + name = pjp.getSignature().toShortString(); + return pjp.proceed(); + } catch (Throwable t) { + result = "N"; + throw t; + } finally { + long endTime = System.currentTimeMillis(); + log.info("{};{};{}ms", name, result, endTime - startTime); + } + } + + @Pointcut("execution(* geektime.spring.springbucks.repository..*(..))") + private void repositoryOps() { + } +} diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/BaseEntity.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/BaseEntity.java new file mode 100644 index 00000000..31233fb6 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/BaseEntity.java @@ -0,0 +1,30 @@ +package geektime.spring.springbucks.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import java.io.Serializable; +import java.util.Date; + +@MappedSuperclass +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BaseEntity implements Serializable { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(updatable = false) + @CreationTimestamp + private Date createTime; + @UpdateTimestamp + private Date updateTime; +} diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/Coffee.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/Coffee.java new file mode 100644 index 00000000..a4482e7b --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/Coffee.java @@ -0,0 +1,28 @@ +package geektime.spring.springbucks.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.hibernate.annotations.Type; +import org.joda.money.Money; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.io.Serializable; + +@Entity +@Table(name = "T_COFFEE") +@Builder +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class Coffee extends BaseEntity implements Serializable { + private String name; + @Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyMinorAmount", + parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CNY")}) + private Money price; +} diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/CoffeeOrder.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/CoffeeOrder.java new file mode 100644 index 00000000..0e45357d --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/CoffeeOrder.java @@ -0,0 +1,37 @@ +package geektime.spring.springbucks.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Enumerated; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.OrderBy; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.List; + +@Entity +@Table(name = "T_ORDER") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class CoffeeOrder extends BaseEntity implements Serializable { + private String customer; + @ManyToMany + @JoinTable(name = "T_ORDER_COFFEE") + @OrderBy("id") + private List items; + @Enumerated + @Column(nullable = false) + private OrderState state; +} diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/OrderState.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/OrderState.java new file mode 100644 index 00000000..212d6b0d --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/model/OrderState.java @@ -0,0 +1,5 @@ +package geektime.spring.springbucks.model; + +public enum OrderState { + INIT, PAID, BREWING, BREWED, TAKEN, CANCELLED +} diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/repository/CoffeeOrderRepository.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/repository/CoffeeOrderRepository.java new file mode 100644 index 00000000..333c5ceb --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/repository/CoffeeOrderRepository.java @@ -0,0 +1,7 @@ +package geektime.spring.springbucks.repository; + +import geektime.spring.springbucks.model.CoffeeOrder; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CoffeeOrderRepository extends JpaRepository { +} diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/repository/CoffeeRepository.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/repository/CoffeeRepository.java new file mode 100644 index 00000000..614bb341 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/repository/CoffeeRepository.java @@ -0,0 +1,7 @@ +package geektime.spring.springbucks.repository; + +import geektime.spring.springbucks.model.Coffee; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CoffeeRepository extends JpaRepository { +} diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/service/CoffeeOrderService.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/service/CoffeeOrderService.java new file mode 100644 index 00000000..19f015d4 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/service/CoffeeOrderService.java @@ -0,0 +1,43 @@ +package geektime.spring.springbucks.service; + +import geektime.spring.springbucks.model.Coffee; +import geektime.spring.springbucks.model.CoffeeOrder; +import geektime.spring.springbucks.model.OrderState; +import geektime.spring.springbucks.repository.CoffeeOrderRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.Arrays; + +@Slf4j +@Service +@Transactional +public class CoffeeOrderService { + @Autowired + private CoffeeOrderRepository orderRepository; + + public CoffeeOrder createOrder(String customer, Coffee...coffee) { + CoffeeOrder order = CoffeeOrder.builder() + .customer(customer) + .items(new ArrayList<>(Arrays.asList(coffee))) + .state(OrderState.INIT) + .build(); + CoffeeOrder saved = orderRepository.save(order); + log.info("New Order: {}", saved); + return saved; + } + + public boolean updateState(CoffeeOrder order, OrderState state) { + if (state.compareTo(order.getState()) <= 0) { + log.warn("Wrong State order: {}, {}", state, order.getState()); + return false; + } + order.setState(state); + orderRepository.save(order); + log.info("Updated Order: {}", order); + return true; + } +} diff --git a/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/service/CoffeeService.java b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/service/CoffeeService.java new file mode 100644 index 00000000..81cc3e86 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/java/geektime/spring/springbucks/service/CoffeeService.java @@ -0,0 +1,29 @@ +package geektime.spring.springbucks.service; + +import geektime.spring.springbucks.model.Coffee; +import geektime.spring.springbucks.repository.CoffeeRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.exact; + +@Slf4j +@Service +public class CoffeeService { + @Autowired + private CoffeeRepository coffeeRepository; + + public Optional findOneCoffee(String name) { + ExampleMatcher matcher = ExampleMatcher.matching() + .withMatcher("name", exact().ignoreCase()); + Optional coffee = coffeeRepository.findOne( + Example.of(Coffee.builder().name(name).build(), matcher)); + log.info("Coffee Found: {}", coffee); + return coffee; + } +} diff --git a/Chapter 5/performance-aspect-demo/src/main/resources/application.properties b/Chapter 5/performance-aspect-demo/src/main/resources/application.properties new file mode 100644 index 00000000..a925e566 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver +spring.datasource.url=jdbc:p6spy:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password= + +spring.jpa.hibernate.ddl-auto=none +#spring.jpa.properties.hibernate.show_sql=true +#spring.jpa.properties.hibernate.format_sql=true \ No newline at end of file diff --git a/Chapter 5/performance-aspect-demo/src/main/resources/data.sql b/Chapter 5/performance-aspect-demo/src/main/resources/data.sql new file mode 100644 index 00000000..929682f1 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/resources/data.sql @@ -0,0 +1,5 @@ +insert into t_coffee (name, price, create_time, update_time) values ('espresso', 2000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('latte', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('capuccino', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('mocha', 3000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('macchiato', 3000, now(), now()); diff --git a/Chapter 5/performance-aspect-demo/src/main/resources/schema.sql b/Chapter 5/performance-aspect-demo/src/main/resources/schema.sql new file mode 100644 index 00000000..6d2aab60 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/resources/schema.sql @@ -0,0 +1,26 @@ +drop table t_coffee if exists; +drop table t_order if exists; +drop table t_order_coffee if exists; + +create table t_coffee ( + id bigint auto_increment, + create_time timestamp, + update_time timestamp, + name varchar(255), + price bigint, + primary key (id) +); + +create table t_order ( + id bigint auto_increment, + create_time timestamp, + update_time timestamp, + customer varchar(255), + state integer not null, + primary key (id) +); + +create table t_order_coffee ( + coffee_order_id bigint not null, + items_id bigint not null +); diff --git a/Chapter 5/performance-aspect-demo/src/main/resources/spy.properties b/Chapter 5/performance-aspect-demo/src/main/resources/spy.properties new file mode 100644 index 00000000..49e45f51 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/main/resources/spy.properties @@ -0,0 +1,8 @@ +# 单行日志 +logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat +# 使用Slf4J记录sql +appender=com.p6spy.engine.spy.appender.Slf4JLogger +# 是否开启慢SQL记录 +outagedetection=true +# 慢SQL记录标准,单位秒 +outagedetectioninterval=2 \ No newline at end of file diff --git a/Chapter 5/performance-aspect-demo/src/test/java/geektime/spring/springbucks/SpringBucksApplicationTests.java b/Chapter 5/performance-aspect-demo/src/test/java/geektime/spring/springbucks/SpringBucksApplicationTests.java new file mode 100644 index 00000000..db4843b4 --- /dev/null +++ b/Chapter 5/performance-aspect-demo/src/test/java/geektime/spring/springbucks/SpringBucksApplicationTests.java @@ -0,0 +1,17 @@ +package geektime.spring.springbucks; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBucksApplicationTests { + + @Test + public void contextLoads() { + } + +} + diff --git a/Chapter 5/r2dbc-repository-demo/.gitignore b/Chapter 5/r2dbc-repository-demo/.gitignore new file mode 100644 index 00000000..c456c4a3 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/Chapter 5/r2dbc-repository-demo/pom.xml b/Chapter 5/r2dbc-repository-demo/pom.xml new file mode 100644 index 00000000..836ec549 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.3.RELEASE + + + geektime.spring.data.reactive + r2dbc-repository-demo + 0.0.1-SNAPSHOT + r2dbc-repository-demo + Demo project for Spring Boot + + + 1.8 + + + + + + org.springframework.data + spring-data-r2dbc + 1.0.0.M1 + + + + io.r2dbc + r2dbc-h2 + 1.0.0.M6 + + + + + org.joda + joda-money + 1.0.1 + + + + org.springframework.boot + spring-boot-starter + + + + com.h2database + h2 + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + spring-milestone + Spring Maven MILESTONE Repository + http://repo.spring.io/libs-milestone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/R2dbcRepositoryDemoApplication.java b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/R2dbcRepositoryDemoApplication.java new file mode 100644 index 00000000..1127aef7 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/R2dbcRepositoryDemoApplication.java @@ -0,0 +1,71 @@ +package geektime.spring.data.reactive.r2dbc; + +import geektime.spring.data.reactive.r2dbc.converter.MoneyReadConverter; +import geektime.spring.data.reactive.r2dbc.converter.MoneyWriteConverter; +import geektime.spring.data.reactive.r2dbc.repository.CoffeeRepository; +import io.r2dbc.h2.H2ConnectionConfiguration; +import io.r2dbc.h2.H2ConnectionFactory; +import io.r2dbc.spi.ConnectionFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.data.convert.CustomConversions; +import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; +import org.springframework.data.r2dbc.dialect.Dialect; +import org.springframework.data.r2dbc.function.convert.R2dbcCustomConversions; +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; + +@SpringBootApplication +@Slf4j +@EnableR2dbcRepositories +public class R2dbcRepositoryDemoApplication extends AbstractR2dbcConfiguration + implements ApplicationRunner { + @Autowired + private CoffeeRepository repository; + + public static void main(String[] args) { + SpringApplication.run(R2dbcRepositoryDemoApplication.class, args); + } + + @Bean + public ConnectionFactory connectionFactory() { + return new H2ConnectionFactory( + H2ConnectionConfiguration.builder() + .inMemory("testdb") + .username("sa") + .build()); + } + + @Bean + public R2dbcCustomConversions r2dbcCustomConversions() { + Dialect dialect = getDialect(connectionFactory()); + CustomConversions.StoreConversions storeConversions = + CustomConversions.StoreConversions.of(dialect.getSimpleTypeHolder()); + return new R2dbcCustomConversions(storeConversions, + Arrays.asList(new MoneyReadConverter(), new MoneyWriteConverter())); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + CountDownLatch cdl = new CountDownLatch(2); + + repository.findAllById(Flux.just(1L, 2L)) + .map(c -> c.getName() + "-" + c.getPrice().toString()) + .doFinally(s -> cdl.countDown()) + .subscribe(c -> log.info("Find {}", c)); + + repository.findByName("mocha") + .doFinally(s -> cdl.countDown()) + .subscribe(c -> log.info("Find {}", c)); + + cdl.await(); + } +} diff --git a/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyReadConverter.java b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyReadConverter.java new file mode 100644 index 00000000..66d6e37f --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyReadConverter.java @@ -0,0 +1,12 @@ +package geektime.spring.data.reactive.r2dbc.converter; + +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; +import org.springframework.core.convert.converter.Converter; + +public class MoneyReadConverter implements Converter { + @Override + public Money convert(Long aLong) { + return Money.ofMinor(CurrencyUnit.of("CNY"), aLong); + } +} diff --git a/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyWriteConverter.java b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyWriteConverter.java new file mode 100644 index 00000000..2c4dcf08 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyWriteConverter.java @@ -0,0 +1,11 @@ +package geektime.spring.data.reactive.r2dbc.converter; + +import org.joda.money.Money; +import org.springframework.core.convert.converter.Converter; + +public class MoneyWriteConverter implements Converter { + @Override + public Long convert(Money money) { + return money.getAmountMinorLong(); + } +} diff --git a/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/model/Coffee.java b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/model/Coffee.java new file mode 100644 index 00000000..ee105380 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/model/Coffee.java @@ -0,0 +1,25 @@ +package geektime.spring.data.reactive.r2dbc.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.joda.money.Money; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; + +import java.util.Date; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table("t_coffee") +public class Coffee { + @Id + private Long id; + private String name; + private Money price; + private Date createTime; + private Date updateTime; +} diff --git a/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/repository/CoffeeRepository.java b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/repository/CoffeeRepository.java new file mode 100644 index 00000000..945730f4 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/main/java/geektime/spring/data/reactive/r2dbc/repository/CoffeeRepository.java @@ -0,0 +1,11 @@ +package geektime.spring.data.reactive.r2dbc.repository; + +import geektime.spring.data.reactive.r2dbc.model.Coffee; +import org.springframework.data.r2dbc.repository.query.Query; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import reactor.core.publisher.Flux; + +public interface CoffeeRepository extends ReactiveCrudRepository { + @Query("select * from t_coffee where name = $1") + Flux findByName(String name); +} diff --git a/Chapter 5/r2dbc-repository-demo/src/main/resources/application.properties b/Chapter 5/r2dbc-repository-demo/src/main/resources/application.properties new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/Chapter 5/r2dbc-repository-demo/src/main/resources/data.sql b/Chapter 5/r2dbc-repository-demo/src/main/resources/data.sql new file mode 100644 index 00000000..929682f1 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/main/resources/data.sql @@ -0,0 +1,5 @@ +insert into t_coffee (name, price, create_time, update_time) values ('espresso', 2000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('latte', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('capuccino', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('mocha', 3000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('macchiato', 3000, now(), now()); diff --git a/Chapter 5/r2dbc-repository-demo/src/main/resources/schema.sql b/Chapter 5/r2dbc-repository-demo/src/main/resources/schema.sql new file mode 100644 index 00000000..8a98c383 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/main/resources/schema.sql @@ -0,0 +1,10 @@ +drop table t_coffee if exists; + +create table t_coffee ( + id bigint auto_increment, + create_time timestamp, + update_time timestamp, + name varchar(255), + price bigint, + primary key (id) +); diff --git a/Chapter 5/r2dbc-repository-demo/src/test/java/geektime/spring/data/reactive/r2dbc/R2dbcRepositoryApplicationTests.java b/Chapter 5/r2dbc-repository-demo/src/test/java/geektime/spring/data/reactive/r2dbc/R2dbcRepositoryApplicationTests.java new file mode 100644 index 00000000..61606d11 --- /dev/null +++ b/Chapter 5/r2dbc-repository-demo/src/test/java/geektime/spring/data/reactive/r2dbc/R2dbcRepositoryApplicationTests.java @@ -0,0 +1,16 @@ +package geektime.spring.data.reactive.r2dbc; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class R2dbcRepositoryApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/Chapter 5/reactive-springbucks/.gitignore b/Chapter 5/reactive-springbucks/.gitignore new file mode 100644 index 00000000..c456c4a3 --- /dev/null +++ b/Chapter 5/reactive-springbucks/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/Chapter 5/reactive-springbucks/pom.xml b/Chapter 5/reactive-springbucks/pom.xml new file mode 100644 index 00000000..0fe0b991 --- /dev/null +++ b/Chapter 5/reactive-springbucks/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.3.RELEASE + + + geektime.spring + reactive-springbucks + 0.0.1-SNAPSHOT + reactive-springbucks + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-redis-reactive + + + + org.apache.commons + commons-pool2 + + + + org.springframework.data + spring-data-r2dbc + 1.0.0.M1 + + + + io.r2dbc + r2dbc-h2 + 1.0.0.M6 + + + + org.joda + joda-money + 1.0.1 + + + + com.fasterxml.jackson.core + jackson-databind + 2.9.5 + + + + com.h2database + h2 + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + spring-milestone + Spring Maven MILESTONE Repository + http://repo.spring.io/libs-milestone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/ReactiveSpringbucksApplication.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/ReactiveSpringbucksApplication.java new file mode 100644 index 00000000..876babe8 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/ReactiveSpringbucksApplication.java @@ -0,0 +1,65 @@ +package geektime.spring.springbucks; + +import geektime.spring.springbucks.converter.MoneyReadConverter; +import geektime.spring.springbucks.converter.MoneyWriteConverter; +import geektime.spring.springbucks.model.Coffee; +import io.r2dbc.h2.H2ConnectionConfiguration; +import io.r2dbc.h2.H2ConnectionFactory; +import io.r2dbc.spi.ConnectionFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.data.convert.CustomConversions; +import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; +import org.springframework.data.r2dbc.dialect.Dialect; +import org.springframework.data.r2dbc.function.convert.R2dbcCustomConversions; +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; +import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; +import org.springframework.data.redis.core.ReactiveRedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.util.Arrays; + +@SpringBootApplication +@EnableR2dbcRepositories +@Slf4j +public class ReactiveSpringbucksApplication extends AbstractR2dbcConfiguration { + + public static void main(String[] args) { + SpringApplication.run(ReactiveSpringbucksApplication.class, args); + } + + @Bean + public ConnectionFactory connectionFactory() { + return new H2ConnectionFactory( + H2ConnectionConfiguration.builder() + .inMemory("testdb") + .username("sa") + .build()); + } + + @Bean + public R2dbcCustomConversions r2dbcCustomConversions() { + Dialect dialect = getDialect(connectionFactory()); + CustomConversions.StoreConversions storeConversions = + CustomConversions.StoreConversions.of(dialect.getSimpleTypeHolder()); + return new R2dbcCustomConversions(storeConversions, + Arrays.asList(new MoneyReadConverter(), new MoneyWriteConverter())); + } + + @Bean + public ReactiveRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory factory) { + StringRedisSerializer keySerializer = new StringRedisSerializer(); + Jackson2JsonRedisSerializer valueSerializer = new Jackson2JsonRedisSerializer<>(Coffee.class); + + RedisSerializationContext.RedisSerializationContextBuilder builder + = RedisSerializationContext.newSerializationContext(keySerializer); + + RedisSerializationContext context = builder.value(valueSerializer).build(); + + return new ReactiveRedisTemplate<>(factory, context); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/SpringbucksRunner.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/SpringbucksRunner.java new file mode 100644 index 00000000..28a12014 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/SpringbucksRunner.java @@ -0,0 +1,49 @@ +package geektime.spring.springbucks; + +import geektime.spring.springbucks.model.Coffee; +import geektime.spring.springbucks.model.CoffeeOrder; +import geektime.spring.springbucks.model.OrderState; +import geektime.spring.springbucks.service.CoffeeService; +import geektime.spring.springbucks.service.OrderService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.Date; + +@Component +@Slf4j +public class SpringbucksRunner implements ApplicationRunner { + @Autowired + private CoffeeService coffeeService; + @Autowired + private OrderService orderService; + + @Override + public void run(ApplicationArguments args) throws Exception { + coffeeService.initCache() + .then( + coffeeService.findOneCoffee("mocha") + .flatMap(c -> { + CoffeeOrder order = createOrder("Li Lei", c); + return orderService.create(order); + }) + .doOnError(t -> log.error("error", t))) + .subscribe(o -> log.info("Create Order: {}", o)); + log.info("After Subscribe"); + Thread.sleep(5000); + } + + private CoffeeOrder createOrder(String customer, Coffee... coffee) { + return CoffeeOrder.builder() + .customer(customer) + .items(Arrays.asList(coffee)) + .state(OrderState.INIT) + .createTime(new Date()) + .updateTime(new Date()) + .build(); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/converter/MoneyReadConverter.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/converter/MoneyReadConverter.java new file mode 100644 index 00000000..e1c21414 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/converter/MoneyReadConverter.java @@ -0,0 +1,14 @@ +package geektime.spring.springbucks.converter; + +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.ReadingConverter; + +@ReadingConverter +public class MoneyReadConverter implements Converter { + @Override + public Money convert(Long aLong) { + return Money.ofMinor(CurrencyUnit.of("CNY"), aLong); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/converter/MoneyWriteConverter.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/converter/MoneyWriteConverter.java new file mode 100644 index 00000000..0912246d --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/converter/MoneyWriteConverter.java @@ -0,0 +1,13 @@ +package geektime.spring.springbucks.converter; + +import org.joda.money.Money; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.WritingConverter; + +@WritingConverter +public class MoneyWriteConverter implements Converter { + @Override + public Long convert(Money money) { + return money.getAmountMinorLong(); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/Coffee.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/Coffee.java new file mode 100644 index 00000000..46b38310 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/Coffee.java @@ -0,0 +1,32 @@ +package geektime.spring.springbucks.model; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import geektime.spring.springbucks.serializer.MoneyDeserializer; +import geektime.spring.springbucks.serializer.MoneySerializer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.joda.money.Money; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; + +import java.io.Serializable; +import java.util.Date; + +@Table("t_coffee") +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Coffee implements Serializable { + @Id + private Long id; + private String name; + @JsonSerialize(using = MoneySerializer.class) + @JsonDeserialize(using = MoneyDeserializer.class) + private Money price; + private Date createTime; + private Date updateTime; +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/CoffeeOrder.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/CoffeeOrder.java new file mode 100644 index 00000000..668987dc --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/CoffeeOrder.java @@ -0,0 +1,23 @@ +package geektime.spring.springbucks.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class CoffeeOrder implements Serializable { + private Long id; + private String customer; + private OrderState state; + private List items; + private Date createTime; + private Date updateTime; +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/OrderState.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/OrderState.java new file mode 100644 index 00000000..212d6b0d --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/model/OrderState.java @@ -0,0 +1,5 @@ +package geektime.spring.springbucks.model; + +public enum OrderState { + INIT, PAID, BREWING, BREWED, TAKEN, CANCELLED +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/repository/CoffeeOrderRepository.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/repository/CoffeeOrderRepository.java new file mode 100644 index 00000000..22899fc2 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/repository/CoffeeOrderRepository.java @@ -0,0 +1,32 @@ +package geektime.spring.springbucks.repository; + +import geektime.spring.springbucks.model.CoffeeOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.r2dbc.function.DatabaseClient; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.sql.Timestamp; + +@Repository +public class CoffeeOrderRepository { + @Autowired + private DatabaseClient databaseClient; + + public Mono save(CoffeeOrder order) { + return databaseClient.insert().into("t_order") + .value("customer", order.getCustomer()) + .value("state", order.getState().ordinal()) + .value("create_time", new Timestamp(order.getCreateTime().getTime())) + .value("update_time", new Timestamp(order.getUpdateTime().getTime())) + .fetch() + .first() + .flatMap(m -> Mono.just((Long) m.get("ID"))) + .flatMap(id -> Flux.fromIterable(order.getItems()) + .flatMap(c -> databaseClient.insert().into("t_order_coffee") + .value("coffee_order_id", id) + .value("items_id", c.getId()) + .then()).then(Mono.just(id))); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/repository/CoffeeRepository.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/repository/CoffeeRepository.java new file mode 100644 index 00000000..e81a9be4 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/repository/CoffeeRepository.java @@ -0,0 +1,11 @@ +package geektime.spring.springbucks.repository; + +import geektime.spring.springbucks.model.Coffee; +import org.springframework.data.r2dbc.repository.R2dbcRepository; +import org.springframework.data.r2dbc.repository.query.Query; +import reactor.core.publisher.Mono; + +public interface CoffeeRepository extends R2dbcRepository { + @Query("select * from t_coffee where name=$1") + Mono findByName(String name); +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/serializer/MoneyDeserializer.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/serializer/MoneyDeserializer.java new file mode 100644 index 00000000..f514f710 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/serializer/MoneyDeserializer.java @@ -0,0 +1,21 @@ +package geektime.spring.springbucks.serializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; + +import java.io.IOException; + +public class MoneyDeserializer extends StdDeserializer { + protected MoneyDeserializer() { + super(Money.class); + } + + @Override + public Money deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + return Money.ofMinor(CurrencyUnit.of("CNY"), p.getLongValue()); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/serializer/MoneySerializer.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/serializer/MoneySerializer.java new file mode 100644 index 00000000..a72996e4 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/serializer/MoneySerializer.java @@ -0,0 +1,19 @@ +package geektime.spring.springbucks.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.joda.money.Money; + +import java.io.IOException; + +public class MoneySerializer extends StdSerializer { + protected MoneySerializer() { + super(Money.class); + } + + @Override + public void serialize(Money money, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeNumber(money.getAmountMinorLong()); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/service/CoffeeService.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/service/CoffeeService.java new file mode 100644 index 00000000..6247a191 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/service/CoffeeService.java @@ -0,0 +1,36 @@ +package geektime.spring.springbucks.service; + +import geektime.spring.springbucks.model.Coffee; +import geektime.spring.springbucks.repository.CoffeeRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.ReactiveRedisTemplate; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.Duration; + +@Service +@Slf4j +public class CoffeeService { + private static final String PREFIX = "springbucks-"; + @Autowired + private CoffeeRepository coffeeRepository; + @Autowired + private ReactiveRedisTemplate redisTemplate; + + public Flux initCache() { + return coffeeRepository.findAll() + .flatMap(c -> redisTemplate.opsForValue() + .set(PREFIX + c.getName(), c) + .flatMap(b -> redisTemplate.expire(PREFIX + c.getName(), Duration.ofMinutes(1))) + .doOnSuccess(v -> log.info("Loading and caching {}", c))); + } + + public Mono findOneCoffee(String name) { + return redisTemplate.opsForValue().get(PREFIX + name) + .switchIfEmpty(coffeeRepository.findByName(name) + .doOnSuccess(s -> log.info("Loading {} from DB.", name))); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/service/OrderService.java b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/service/OrderService.java new file mode 100644 index 00000000..48052519 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/java/geektime/spring/springbucks/service/OrderService.java @@ -0,0 +1,20 @@ +package geektime.spring.springbucks.service; + +import geektime.spring.springbucks.model.CoffeeOrder; +import geektime.spring.springbucks.repository.CoffeeOrderRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.r2dbc.function.DatabaseClient; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Service +public class OrderService { + @Autowired + private CoffeeOrderRepository repository; + @Autowired + private DatabaseClient client; + + public Mono create(CoffeeOrder order) { + return repository.save(order); + } +} diff --git a/Chapter 5/reactive-springbucks/src/main/resources/application.properties b/Chapter 5/reactive-springbucks/src/main/resources/application.properties new file mode 100644 index 00000000..607e8234 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/resources/application.properties @@ -0,0 +1,6 @@ +spring.redis.host=localhost +spring.redis.lettuce.pool.maxActive=5 +spring.redis.lettuce.pool.maxIdle=5 + +#spring.datasource.initialization-mode=always +logging.level.org.springframework.data.r2dbc=DEBUG \ No newline at end of file diff --git a/Chapter 5/reactive-springbucks/src/main/resources/data.sql b/Chapter 5/reactive-springbucks/src/main/resources/data.sql new file mode 100644 index 00000000..929682f1 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/resources/data.sql @@ -0,0 +1,5 @@ +insert into t_coffee (name, price, create_time, update_time) values ('espresso', 2000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('latte', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('capuccino', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('mocha', 3000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('macchiato', 3000, now(), now()); diff --git a/Chapter 5/reactive-springbucks/src/main/resources/schema.sql b/Chapter 5/reactive-springbucks/src/main/resources/schema.sql new file mode 100644 index 00000000..6d2aab60 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/main/resources/schema.sql @@ -0,0 +1,26 @@ +drop table t_coffee if exists; +drop table t_order if exists; +drop table t_order_coffee if exists; + +create table t_coffee ( + id bigint auto_increment, + create_time timestamp, + update_time timestamp, + name varchar(255), + price bigint, + primary key (id) +); + +create table t_order ( + id bigint auto_increment, + create_time timestamp, + update_time timestamp, + customer varchar(255), + state integer not null, + primary key (id) +); + +create table t_order_coffee ( + coffee_order_id bigint not null, + items_id bigint not null +); diff --git a/Chapter 5/reactive-springbucks/src/test/java/geektime/spring/springbucks/ReactiveSpringbucksApplicationTests.java b/Chapter 5/reactive-springbucks/src/test/java/geektime/spring/springbucks/ReactiveSpringbucksApplicationTests.java new file mode 100644 index 00000000..677bd205 --- /dev/null +++ b/Chapter 5/reactive-springbucks/src/test/java/geektime/spring/springbucks/ReactiveSpringbucksApplicationTests.java @@ -0,0 +1,16 @@ +package geektime.spring.springbucks; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class ReactiveSpringbucksApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/Chapter 5/redis-demo/.gitignore b/Chapter 5/redis-demo/.gitignore new file mode 100644 index 00000000..c456c4a3 --- /dev/null +++ b/Chapter 5/redis-demo/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/Chapter 5/redis-demo/pom.xml b/Chapter 5/redis-demo/pom.xml new file mode 100644 index 00000000..0ee8d32c --- /dev/null +++ b/Chapter 5/redis-demo/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.3.RELEASE + + + geektime.spring.data.reactive + redis-demo + 0.0.1-SNAPSHOT + redis-demo + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-redis-reactive + + + org.springframework.boot + spring-boot-starter-jdbc + + + + com.h2database + h2 + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/Chapter 5/redis-demo/src/main/java/geektime/spring/data/reactive/redisdemo/Coffee.java b/Chapter 5/redis-demo/src/main/java/geektime/spring/data/reactive/redisdemo/Coffee.java new file mode 100644 index 00000000..d9a29ed3 --- /dev/null +++ b/Chapter 5/redis-demo/src/main/java/geektime/spring/data/reactive/redisdemo/Coffee.java @@ -0,0 +1,16 @@ +package geektime.spring.data.reactive.redisdemo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Coffee { + private Long id; + private String name; + private Long price; +} diff --git a/Chapter 5/redis-demo/src/main/java/geektime/spring/data/reactive/redisdemo/RedisDemoApplication.java b/Chapter 5/redis-demo/src/main/java/geektime/spring/data/reactive/redisdemo/RedisDemoApplication.java new file mode 100644 index 00000000..58b3a0d3 --- /dev/null +++ b/Chapter 5/redis-demo/src/main/java/geektime/spring/data/reactive/redisdemo/RedisDemoApplication.java @@ -0,0 +1,75 @@ +package geektime.spring.data.reactive.redisdemo; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; +import org.springframework.data.redis.core.ReactiveHashOperations; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; +import org.springframework.jdbc.core.JdbcTemplate; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +@SpringBootApplication +@Slf4j +public class RedisDemoApplication implements ApplicationRunner { + private static final String KEY = "COFFEE_MENU"; + + @Autowired + private JdbcTemplate jdbcTemplate; + @Autowired + private ReactiveStringRedisTemplate redisTemplate; + + public static void main(String[] args) { + SpringApplication.run(RedisDemoApplication.class, args); + } + + @Bean + ReactiveStringRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory factory) { + return new ReactiveStringRedisTemplate(factory); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + ReactiveHashOperations hashOps = redisTemplate.opsForHash(); + CountDownLatch cdl = new CountDownLatch(1); + + List list = jdbcTemplate.query( + "select * from t_coffee", (rs, i) -> + Coffee.builder() + .id(rs.getLong("id")) + .name(rs.getString("name")) + .price(rs.getLong("price")) + .build() + ); + + Flux.fromIterable(list) + .publishOn(Schedulers.single()) + .doOnComplete(() -> log.info("list ok")) + .flatMap(c -> { + log.info("try to put {},{}", c.getName(), c.getPrice()); + return hashOps.put(KEY, c.getName(), c.getPrice().toString()); + }) + .doOnComplete(() -> log.info("set ok")) + .concatWith(redisTemplate.expire(KEY, Duration.ofMinutes(1))) + .doOnComplete(() -> log.info("expire ok")) + .onErrorResume(e -> { + log.error("exception {}", e.getMessage()); + return Mono.just(false); + }) + .subscribe(b -> log.info("Boolean: {}", b), + e -> log.error("Exception {}", e.getMessage()), + () -> cdl.countDown()); + log.info("Waiting"); + cdl.await(); + } +} diff --git a/Chapter 5/redis-demo/src/main/resources/application.properties b/Chapter 5/redis-demo/src/main/resources/application.properties new file mode 100644 index 00000000..89f6450a --- /dev/null +++ b/Chapter 5/redis-demo/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.redis.host=localhost diff --git a/Chapter 5/redis-demo/src/main/resources/data.sql b/Chapter 5/redis-demo/src/main/resources/data.sql new file mode 100644 index 00000000..929682f1 --- /dev/null +++ b/Chapter 5/redis-demo/src/main/resources/data.sql @@ -0,0 +1,5 @@ +insert into t_coffee (name, price, create_time, update_time) values ('espresso', 2000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('latte', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('capuccino', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('mocha', 3000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('macchiato', 3000, now(), now()); diff --git a/Chapter 5/redis-demo/src/main/resources/schema.sql b/Chapter 5/redis-demo/src/main/resources/schema.sql new file mode 100644 index 00000000..8a98c383 --- /dev/null +++ b/Chapter 5/redis-demo/src/main/resources/schema.sql @@ -0,0 +1,10 @@ +drop table t_coffee if exists; + +create table t_coffee ( + id bigint auto_increment, + create_time timestamp, + update_time timestamp, + name varchar(255), + price bigint, + primary key (id) +); diff --git a/Chapter 5/redis-demo/src/test/java/geektime/spring/data/reactive/redisdemo/RedisDemoApplicationTests.java b/Chapter 5/redis-demo/src/test/java/geektime/spring/data/reactive/redisdemo/RedisDemoApplicationTests.java new file mode 100644 index 00000000..ea7ece4d --- /dev/null +++ b/Chapter 5/redis-demo/src/test/java/geektime/spring/data/reactive/redisdemo/RedisDemoApplicationTests.java @@ -0,0 +1,16 @@ +package geektime.spring.data.reactive.redisdemo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class RedisDemoApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/Chapter 5/simple-r2dbc-demo/.gitignore b/Chapter 5/simple-r2dbc-demo/.gitignore new file mode 100644 index 00000000..c456c4a3 --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/Chapter 5/simple-r2dbc-demo/pom.xml b/Chapter 5/simple-r2dbc-demo/pom.xml new file mode 100644 index 00000000..062aa94c --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.3.RELEASE + + + geektime.spring.data.reactive + simple-r2dbc-demo + 0.0.1-SNAPSHOT + simple-r2dbc-demo + Demo project for Spring Boot + + + 1.8 + + + + + + org.springframework.data + spring-data-r2dbc + 1.0.0.M1 + + + + io.r2dbc + r2dbc-h2 + 1.0.0.M6 + + + + + org.joda + joda-money + 1.0.1 + + + + org.springframework.boot + spring-boot-starter + + + + com.h2database + h2 + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + spring-milestone + Spring Maven MILESTONE Repository + http://repo.spring.io/libs-milestone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/SimpleR2dbcDemoApplication.java b/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/SimpleR2dbcDemoApplication.java new file mode 100644 index 00000000..11ff8e84 --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/SimpleR2dbcDemoApplication.java @@ -0,0 +1,84 @@ +package geektime.spring.data.reactive.r2dbc; + +import geektime.spring.data.reactive.r2dbc.converter.MoneyReadConverter; +import geektime.spring.data.reactive.r2dbc.converter.MoneyWriteConverter; +import geektime.spring.data.reactive.r2dbc.model.Coffee; +import io.r2dbc.h2.H2ConnectionConfiguration; +import io.r2dbc.h2.H2ConnectionFactory; +import io.r2dbc.spi.ConnectionFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.data.convert.CustomConversions; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; +import org.springframework.data.r2dbc.dialect.Dialect; +import org.springframework.data.r2dbc.function.DatabaseClient; +import org.springframework.data.r2dbc.function.convert.R2dbcCustomConversions; +import reactor.core.scheduler.Schedulers; + +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; + +@SpringBootApplication +@Slf4j +public class SimpleR2dbcDemoApplication extends AbstractR2dbcConfiguration + implements ApplicationRunner { + @Autowired + private DatabaseClient client; + + public static void main(String[] args) { + SpringApplication.run(SimpleR2dbcDemoApplication.class, args); + } + + @Bean + public ConnectionFactory connectionFactory() { + return new H2ConnectionFactory( + H2ConnectionConfiguration.builder() + .inMemory("testdb") + .username("sa") + .build()); + } + + @Bean + public R2dbcCustomConversions r2dbcCustomConversions() { + Dialect dialect = getDialect(connectionFactory()); + CustomConversions.StoreConversions storeConversions = + CustomConversions.StoreConversions.of(dialect.getSimpleTypeHolder()); + return new R2dbcCustomConversions(storeConversions, + Arrays.asList(new MoneyReadConverter(), new MoneyWriteConverter())); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + CountDownLatch cdl = new CountDownLatch(2); + + client.execute() + .sql("select * from t_coffee") + .as(Coffee.class) + .fetch() + .first() + .doFinally(s -> cdl.countDown()) +// .subscribeOn(Schedulers.elastic()) + .subscribe(c -> log.info("Fetch execute() {}", c)); + + client.select() + .from("t_coffee") + .orderBy(Sort.by(Sort.Direction.DESC, "id")) + .page(PageRequest.of(0, 3)) + .as(Coffee.class) + .fetch() + .all() + .doFinally(s -> cdl.countDown()) +// .subscribeOn(Schedulers.elastic()) + .subscribe(c -> log.info("Fetch select() {}", c)); + + log.info("After Starting."); + cdl.await(); + } +} diff --git a/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyReadConverter.java b/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyReadConverter.java new file mode 100644 index 00000000..66d6e37f --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyReadConverter.java @@ -0,0 +1,12 @@ +package geektime.spring.data.reactive.r2dbc.converter; + +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; +import org.springframework.core.convert.converter.Converter; + +public class MoneyReadConverter implements Converter { + @Override + public Money convert(Long aLong) { + return Money.ofMinor(CurrencyUnit.of("CNY"), aLong); + } +} diff --git a/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyWriteConverter.java b/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyWriteConverter.java new file mode 100644 index 00000000..2c4dcf08 --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/converter/MoneyWriteConverter.java @@ -0,0 +1,11 @@ +package geektime.spring.data.reactive.r2dbc.converter; + +import org.joda.money.Money; +import org.springframework.core.convert.converter.Converter; + +public class MoneyWriteConverter implements Converter { + @Override + public Long convert(Money money) { + return money.getAmountMinorLong(); + } +} diff --git a/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/model/Coffee.java b/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/model/Coffee.java new file mode 100644 index 00000000..a4569753 --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/src/main/java/geektime/spring/data/reactive/r2dbc/model/Coffee.java @@ -0,0 +1,21 @@ +package geektime.spring.data.reactive.r2dbc.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.joda.money.Money; + +import java.util.Date; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Coffee { + private Long id; + private String name; + private Money price; + private Date createTime; + private Date updateTime; +} diff --git a/Chapter 5/simple-r2dbc-demo/src/main/resources/application.properties b/Chapter 5/simple-r2dbc-demo/src/main/resources/application.properties new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/Chapter 5/simple-r2dbc-demo/src/main/resources/data.sql b/Chapter 5/simple-r2dbc-demo/src/main/resources/data.sql new file mode 100644 index 00000000..1a0f4890 --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/src/main/resources/data.sql @@ -0,0 +1,5 @@ +insert into t_coffee (name, price, create_time, update_time) values ('espresso', 2000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('latte', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('capuccino', 2500, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('mocha', 3000, now(), now()); +insert into t_coffee (name, price, create_time, update_time) values ('macchiato', 3000, now(), now()); \ No newline at end of file diff --git a/Chapter 5/simple-r2dbc-demo/src/main/resources/schema.sql b/Chapter 5/simple-r2dbc-demo/src/main/resources/schema.sql new file mode 100644 index 00000000..8a98c383 --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/src/main/resources/schema.sql @@ -0,0 +1,10 @@ +drop table t_coffee if exists; + +create table t_coffee ( + id bigint auto_increment, + create_time timestamp, + update_time timestamp, + name varchar(255), + price bigint, + primary key (id) +); diff --git a/Chapter 5/simple-r2dbc-demo/src/test/java/geektime/spring/data/reactive/r2dbc/SimpleR2dbcDemoApplicationTests.java b/Chapter 5/simple-r2dbc-demo/src/test/java/geektime/spring/data/reactive/r2dbc/SimpleR2dbcDemoApplicationTests.java new file mode 100644 index 00000000..4d20021b --- /dev/null +++ b/Chapter 5/simple-r2dbc-demo/src/test/java/geektime/spring/data/reactive/r2dbc/SimpleR2dbcDemoApplicationTests.java @@ -0,0 +1,16 @@ +package geektime.spring.data.reactive.r2dbc; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SimpleR2dbcDemoApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/Chapter 5/simple-reactor-demo/.gitignore b/Chapter 5/simple-reactor-demo/.gitignore new file mode 100644 index 00000000..c456c4a3 --- /dev/null +++ b/Chapter 5/simple-reactor-demo/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/Chapter 5/simple-reactor-demo/pom.xml b/Chapter 5/simple-reactor-demo/pom.xml new file mode 100644 index 00000000..28224698 --- /dev/null +++ b/Chapter 5/simple-reactor-demo/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + geektime.spring.reactor + simple-reactor-demo + 0.0.1-SNAPSHOT + simple-reactor-demo + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + org.projectlombok + lombok + + + io.projectreactor + reactor-core + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/Chapter 5/simple-reactor-demo/src/main/java/geektime/spring/reactor/simple/SimpleReactorDemoApplication.java b/Chapter 5/simple-reactor-demo/src/main/java/geektime/spring/reactor/simple/SimpleReactorDemoApplication.java new file mode 100644 index 00000000..30e07264 --- /dev/null +++ b/Chapter 5/simple-reactor-demo/src/main/java/geektime/spring/reactor/simple/SimpleReactorDemoApplication.java @@ -0,0 +1,46 @@ +package geektime.spring.reactor.simple; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +@SpringBootApplication +@Slf4j +public class SimpleReactorDemoApplication implements ApplicationRunner { + + public static void main(String[] args) { + SpringApplication.run(SimpleReactorDemoApplication.class, args); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + Flux.range(1, 6) + .doOnRequest(n -> log.info("Request {} number", n)) // 注意顺序造成的区别 +// .publishOn(Schedulers.elastic()) + .doOnComplete(() -> log.info("Publisher COMPLETE 1")) + .map(i -> { + log.info("Publish {}, {}", Thread.currentThread(), i); + return 10 / (i - 3); +// return i; + }) + .doOnComplete(() -> log.info("Publisher COMPLETE 2")) +// .subscribeOn(Schedulers.single()) +// .onErrorResume(e -> { +// log.error("Exception {}", e.toString()); +// return Mono.just(-1); +// }) +// .onErrorReturn(-1) + .subscribe(i -> log.info("Subscribe {}: {}", Thread.currentThread(), i), + e -> log.error("error {}", e.toString()), + () -> log.info("Subscriber COMPLETE")//, +// s -> s.request(4) + ); + Thread.sleep(2000); + } +} + diff --git a/Chapter 5/simple-reactor-demo/src/main/resources/application.properties b/Chapter 5/simple-reactor-demo/src/main/resources/application.properties new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Chapter 5/simple-reactor-demo/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/Chapter 5/simple-reactor-demo/src/test/java/geektime/spring/reactor/simple/SimpleReactorDemoApplicationTests.java b/Chapter 5/simple-reactor-demo/src/test/java/geektime/spring/reactor/simple/SimpleReactorDemoApplicationTests.java new file mode 100644 index 00000000..d01de30f --- /dev/null +++ b/Chapter 5/simple-reactor-demo/src/test/java/geektime/spring/reactor/simple/SimpleReactorDemoApplicationTests.java @@ -0,0 +1,17 @@ +package geektime.spring.reactor.simple; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SimpleReactorDemoApplicationTests { + + @Test + public void contextLoads() { + } + +} + diff --git "a/PDF/\347\254\2545\347\253\240.pdf" "b/PDF/\347\254\2545\347\253\240.pdf" new file mode 100644 index 00000000..fa7ef917 Binary files /dev/null and "b/PDF/\347\254\2545\347\253\240.pdf" differ