目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Spring Data JPA

对象与关系的范式不匹配

对象与关系的范式不匹配

Hibernate

  • ⼀款开源的对象关系映射(Object / Relational Mapping)框架
  • 将开发者从 95% 的常 ⻅数据持久化 ⼯作中解放出来
  • 屏蔽了底层数据库的各种细节
  • 2006 年,Hibernate 3.2 成为 JPA 实现

Reference

Java Persistence API

 JPA1.0 是作为 JSR 220 的一部分正式发布。

  • 简化数据持久化代码的开发工作
  • 为 Java 社区屏蔽不同持久化 API 的差异(屏蔽了 Hibernate、JDO、EJB 之间的差异
  • 相当于在 O/R Mapping 框架之前做了一层抽象。

Spring Data

 在保留底层存储特性的同时,提供相对 ⼀致的、基于 Spring 的编程模型。Spring Family List :

  • Spring Data Commons
  • Spring Data JDBC
  • Spring Data JPA
  • Spring Data Redis
  • ……

如何引入 Spring Data JPA

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

常用 JPA 注解

实体

  • @Entity:声明此类为一个实体。
  • @MappedSuperclass:在多个实体类的父类上面标注。
  • @Table(name):实体与表关联。如果没有写@Table 注解,会自动将该类的名字作为表名。

主键
@Id

  • @GeneratedValue(strategy, generator):用于指定主键的生成策略、和对应的生成器。
  • @SequenceGenerator(name, sequenceName):指定序列。

映射

  • @Column(name, nullable, length, insertable, updatable)
  • @JoinTable(name):关联查询时,表与表是多对多的关系时,指定多对多关联表中间表的参数。
  • @JoinColumn(name):关联查询时,表与表是一对一、一对多、多对一以及多对多的关系时,声明表关联的外键字段作为连接表的条件。必须配合关联表的注解一起使用

关系

  • @OneToOne:关联表注解,表示对应的实体和本类是一对一的关系
  • @OneToMany:关联表注解,表示对应的实体和本类是一对多的关系
  • @ManyToOne:关联表注解,表示对应的实体和本类是多对一的关系
  • @ManyToMany:关联表注解,表示对应的实体和本类是多对多的关系
  • @OrderBy:

BucksProject

项目目标
bucks

项目中的对象实

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>geektime.spring.springbucks</groupId>
	<artifactId>jpa-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>jpa-demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>org.joda</groupId>
			<artifactId>joda-money</artifactId>
			<version>1.0.1</version>
		</dependency>
		<dependency>
			<groupId>org.jadira.usertype</groupId>
			<artifactId>usertype.core</artifactId>
			<version>6.0.1.GA</version>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

application.properties

# 定义DDL 每次运行都会创建表结构、每次结束都会删除
spring.jpa.hibernate.ddl-auto=create-drop
# 打印sql
spring.jpa.properties.hibernate.show_sql=true
# 对sql进行格式化
spring.jpa.properties.hibernate.format_sql=true

实体定义

BaseEntity

package geektime.spring.springbucks.jpademo.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.Id;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
import java.util.Date;

@MappedSuperclass
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseEntity implements Serializable {
    @Id
    @GeneratedValue
    private Long id;
    @Column(updatable = false)
    @CreationTimestamp
    private Date createTime;
    @UpdateTimestamp
    private Date updateTime;
}

Coffee

package geektime.spring.springbucks.jpademo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
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_MENU")
@Builder
@Data
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class Coffee extends BaseEntity implements Serializable {
    private String name;
    @Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyAmount",
            parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CNY")})
    private Money price;
}

CoffeeOrder

package geektime.spring.springbucks.jpademo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
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
@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<Coffee> items;
    @Enumerated
    @Column(nullable = false)
    private OrderState state;
}

OrderState 枚举类(描述订单的状态)

package geektime.spring.springbucks.jpademo.model;

public enum OrderState {
    INIT, PAID, BREWING, BREWED, TAKEN, CANCELLED
}

控制台输出

# 如果有,删除
Hibernate: 
    
    drop table t_menu if exists
Hibernate: 
    
    drop table t_order if exists
Hibernate: 
    
    drop table t_order_coffee if exists
Hibernate: 
    
    drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1

# 创建表结构
Hibernate: 
    
    create table t_menu (
       id bigint not null,
        create_time timestamp,
        name varchar(255),
        price decimal(19,2),
        update_time timestamp,
        primary key (id)
    )
Hibernate: 
    
    create table t_order (
       id bigint not null,
        create_time timestamp,
        customer varchar(255),
        state integer not null,
        update_time timestamp,
        primary key (id)
    )
Hibernate: 
    
    create table t_order_coffee (
       coffee_order_id bigint not null,
        items_id bigint not null
    )
Hibernate: 
    
    alter table t_order_coffee 
       add constraint FKj2swxd3y69u2tfvalju7sr07q 
       foreign key (items_id) 
       references t_menu
Hibernate: 
    
    alter table t_order_coffee 
       add constraint FK33ucji9dx64fyog6g17blpx9v 
       foreign key (coffee_order_id) 
       references t_order

# 删除
Hibernate: 
    
    drop table t_menu if exists
Hibernate: 
    
    drop table t_order if exists
Hibernate: 
    
    drop table t_order_coffee if exists
Hibernate: 
    
    drop sequence if exists hibernate_sequence

通过 Spring Data JPA 操作数据库

启动类添加 @EnableJpaRepositories

Repository<T, ID> 接口

  • CrudRepository<T, ID>
  • PagingAndSortingRepository<T, ID>
  • JpaRepository<T, ID>

定义查询
根据方法名定义查询

  • find…By… / read…By… / query…By… / get…By…
  • count…By…
  • …OrderBy…[Asc / Desc]
  • And / Or / IgnoreCase
  • Top / First / Distinct

分页查询

  • PagingAndSortingRepository<T, ID>
  • Pageable / Sort
  • Slice / Page

定义 Repository

BaseRepository

package geektime.spring.springbucks.jpademo.repository;

import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;

import java.util.List;

@NoRepositoryBean
public interface BaseRepository<T, Long> extends PagingAndSortingRepository<T, Long> {
    /**
     * 对更新时间进行升序排序,并且查询出前三名
     * @return
     */
    List<T> findTop3ByOrderByUpdateTimeDescIdAsc();
}

CoffeeRepository

package geektime.spring.springbucks.jpademo.repository;

import geektime.spring.springbucks.jpademo.model.Coffee;

public interface CoffeeRepository extends BaseRepository<Coffee, Long> {
}

CoffeeOrderRepository

package geektime.spring.springbucks.jpademo.repository;

import geektime.spring.springbucks.jpademo.model.CoffeeOrder;

import java.util.List;

public interface CoffeeOrderRepository extends BaseRepository<CoffeeOrder, Long> {
    List<CoffeeOrder> findByCustomerOrderById(String customer);
    List<CoffeeOrder> findByItems_Name(String name);
}

启动类

package geektime.spring.springbucks.jpademo;

import geektime.spring.springbucks.jpademo.model.Coffee;
import geektime.spring.springbucks.jpademo.model.CoffeeOrder;
import geektime.spring.springbucks.jpademo.model.OrderState;
import geektime.spring.springbucks.jpademo.repository.CoffeeOrderRepository;
import geektime.spring.springbucks.jpademo.repository.CoffeeRepository;
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.data.domain.Sort;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.transaction.Transactional;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@SpringBootApplication
@EnableJpaRepositories
@EnableTransactionManagement
@Slf4j
public class JpaDemoApplication implements ApplicationRunner {
	@Autowired
	private CoffeeRepository coffeeRepository;
	@Autowired
	private CoffeeOrderRepository orderRepository;

	public static void main(String[] args) {
		SpringApplication.run(JpaDemoApplication.class, args);
	}

	@Override
	@Transactional
	public void run(ApplicationArguments args) throws Exception {
		initOrders();
		findOrders();
	}

	/**
	 * 增
	 */
	private void initOrders() {
		//保存咖啡:Coffee表添加拿铁
		Coffee latte = Coffee.builder().name("latte")
				.price(Money.of(CurrencyUnit.of("CNY"), 30.0))
				.build();
		coffeeRepository.save(latte);
		log.info("Coffee: {}", latte);

		//保存咖啡:Coffee表添加特浓
		Coffee espresso = Coffee.builder().name("espresso")
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
				.build();
		coffeeRepository.save(espresso);
		log.info("Coffee: {}", espresso);

		//Li Lei 第一个订单(特浓)
		CoffeeOrder order = CoffeeOrder.builder()
				.customer("Li Lei")
				.items(Collections.singletonList(espresso))//不可变List
				.state(OrderState.INIT) //状态为INIT
				.build();
		orderRepository.save(order);
		log.info("Order: {}", order);

		//Li Lei 第二个订单(特浓、拿铁)
		order = CoffeeOrder.builder()
				.customer("Li Lei")
				.items(Arrays.asList(espresso, latte))
				.state(OrderState.INIT)
				.build();
		orderRepository.save(order);
		log.info("Order: {}", order);
	}

	/**
	 * 查询
	 */
	private void findOrders() {

		//查询咖啡:根据咖啡的ID降序
		coffeeRepository
				.findAll(Sort.by(Sort.Direction.DESC, "id"))
				.forEach(c -> log.info("Loading {}", c));

		//查询订单:对订单的更新时间进行升序排序,并且查询出前三名
		List<CoffeeOrder> list = orderRepository.findTop3ByOrderByUpdateTimeDescIdAsc();
		log.info("findTop3ByOrderByUpdateTimeDescIdAsc: {}", getJoinedOrderId(list));

		//查询订单: 查询顾客为Li Lei的所有订单:(输出其订单号每个订单的ID,及其包含的咖啡)
		list = orderRepository.findByCustomerOrderById("Li Lei");
		log.info("findByCustomerOrderById: {}", getJoinedOrderId(list));
		// 不开启事务会因为没Session而报LazyInitializationException
		list.forEach(o -> {
			log.info("Order {}", o.getId());
			o.getItems().forEach(i -> log.info("  Item {}", i));
		});

		//查询订单:查询订单中包含 拿铁的 所有订单(Items_Name  相当于查询items属性所包含集合中每个元素的name属性)
		list = orderRepository.findByItems_Name("latte");
		log.info("findByItems_Name: {}", getJoinedOrderId(list));
	}

	//过滤器list中的id,并且添加分隔符循环遍历
	private String getJoinedOrderId(List<CoffeeOrder> list) {
		return list.stream().map(o -> o.getId().toString())
				.collect(Collectors.joining(","));
	}
}

控制台输出

//第一订单:orderRepository.save(order);
 Order: CoffeeOrder(super=BaseEntity(id=3, createTime=null, updateTime=null), customer=Li Lei, items=[Coffee(super=BaseEntity(id=2, createTime=null, updateTime=null), name=espresso, price=CNY 20.00)], state=INIT)

//第一订单:orderRepository.save(order);
Order: CoffeeOrder(super=BaseEntity(id=4, createTime=null, updateTime=null), customer=Li Lei, items=[Coffee(super=BaseEntity(id=2, createTime=null, updateTime=null), name=espresso, price=CNY 20.00), Coffee(super=BaseEntity(id=1, createTime=null, updateTime=null), name=latte, price=CNY 30.00)], state=INIT)

//查询咖啡:根据咖啡的ID降序
Loading Coffee(super=BaseEntity(id=2, createTime=Thu Jan 09 17:38:01 CST 2020, updateTime=Thu Jan 09 17:38:01 CST 2020), name=espresso, price=CNY 20.00)
Loading Coffee(super=BaseEntity(id=1, createTime=Thu Jan 09 17:38:01 CST 2020, updateTime=Thu Jan 09 17:38:01 CST 2020), name=latte, price=CNY 30.00)

//查询订单:对订单的更新时间进行升序排序,并且查询出前三名
findTop3ByOrderByUpdateTimeDescIdAsc: 3,4

//查询订单: 查询顾客为Li Lei的所有订单:(输出其订单号每个订单的ID,及其包含的咖啡)
findByCustomerOrderById: 3,4
Order 3
   Item Coffee(super=BaseEntity(id=2, createTime=Thu Jan 09 17:38:01 CST 2020, updateTime=Thu Jan 09 17:38:01 CST 2020), name=espresso, price=CNY 20.00)
Order 4
   Item Coffee(super=BaseEntity(id=2, createTime=Thu Jan 09 17:38:01 CST 2020, updateTime=Thu Jan 09 17:38:01 CST 2020), name=espresso, price=CNY 20.00)
   Item Coffee(super=BaseEntity(id=1, createTime=Thu Jan 09 17:38:01 CST 2020, updateTime=Thu Jan 09 17:38:01 CST 2020), name=latte, price=CNY 30.00)

//查询订单:查询订单中包含 拿铁的 所有订单(Items_Name  相当于查询items属性所包含集合中每个元素的name属性)
findByItems_Name: 4

JpaRepository 接口的方法

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

	List<T> findAll();

	List<T> findAll(Sort sort);

	List<T> findAllById(Iterable<ID> ids);

	<S extends T> List<S> saveAll(Iterable<S> entities);

	void flush();

	<S extends T> S saveAndFlush(S entity);

	void deleteInBatch(Iterable<T> entities);

	void deleteAllInBatch();

	T getOne(ID id);
	@Override
	<S extends T> List<S> findAll(Example<S> example);

	@Override
	<S extends T> List<S> findAll(Example<S> example, Sort sort);
}

QueryByExampleExecutor

public interface QueryByExampleExecutor<T> {

	<S extends T> Optional<S> findOne(Example<S> example);

	<S extends T> Iterable<S> findAll(Example<S> example);

	<S extends T> Iterable<S> findAll(Example<S> example, Sort sort);

	<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);

	<S extends T> long count(Example<S> example);

	<S extends T> boolean exists(Example<S> example);
}

CoffeeRepository

package geektime.spring.springbucks.repository;

import geektime.spring.springbucks.model.Coffee;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CoffeeRepository extends JpaRepository<Coffee, Long> {
}

CoffeeOrderRepository

package geektime.spring.springbucks.repository;

import geektime.spring.springbucks.model.CoffeeOrder;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface CoffeeOrderRepository extends JpaRepository<CoffeeOrder, Long> {
    /**
     * 查询指定用户名的所有订单并根据 id 排序
     * @param customer
     * @return
     */
    List<CoffeeOrder> findByCustomerOrderById(String customer);
}

CoffeeService

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;
    }
}

CoffeeOrderService

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;
import java.util.List;

@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;
    }
    
    public List<CoffeeOrder> findByCustomerOrderById(String customer){
        return orderRepository.findByCustomerOrderById(customer);
    }
}

启动类

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.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.swing.text.html.Option;
import java.security.spec.ECParameterSpec;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@EnableTransactionManagement
@SpringBootApplication
@EnableJpaRepositories
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 {
		//直接使用coffeeRepository
		log.info("All Coffee: {}", coffeeRepository.findAll());
		//All Coffee: [Coffee(super=BaseEntity(id=1, createTime=2020-01-12 16:03:36.35, updateTime=2020-01-12 16:03:36.35), name=espresso, price=CNY 20.00), Coffee(super=BaseEntity(id=2, createTime=2020-01-12 16:03:36.352, updateTime=2020-01-12 16:03:36.352), name=latte, price=CNY 25.00), Coffee(super=BaseEntity(id=3, createTime=2020-01-12 16:03:36.352, updateTime=2020-01-12 16:03:36.352), name=capuccino, price=CNY 25.00), Coffee(super=BaseEntity(id=4, createTime=2020-01-12 16:03:36.352, updateTime=2020-01-12 16:03:36.352), name=mocha, price=CNY 30.00), Coffee(super=BaseEntity(id=5, createTime=2020-01-12 16:03:36.352, updateTime=2020-01-12 16:03:36.352), name=macchiato, price=CNY 30.00)]

		//findOneCoffee
		Optional<Coffee> latte = coffeeService.findOneCoffee("Latte");
		Optional<Coffee> espresso = coffeeService.findOneCoffee("espresso");
		if (latte.isPresent() && espresso.isPresent()) {
			CoffeeOrder order = orderService.createOrder("Li Lei", latte.get(), espresso.get());
			log.info("Update INIT to PAID: {}", orderService.updateState(order, OrderState.PAID));
			//Update INIT to PAID: true
			log.info("Update PAID to INIT: {}", orderService.updateState(order, OrderState.INIT));
			//Update PAID to INIT: false   无法从 PAID 转到 INIT
		}

		//orderService
		List<CoffeeOrder> list = orderService.findByCustomerOrderById("Li Lei");
		log.info("findByCustomerOrderById: {}", getJoinedOrderId(list));
		//findByCustomerOrderById: 1

		
	}
	//过滤器list中的id,并且添加分隔符循环遍历
	private String getJoinedOrderId(List<CoffeeOrder> list) {
		return list.stream().map(o -> o.getId().toString())
				.collect(Collectors.joining(","));
	}
}

作者:Soulboy