对于现在的大多数场景下,面向数据库编程对于实现功能来说是十分方便的,门槛比较低。主要三把斧,建表,orm框架生成dao方法,然后service层对dao进行复杂操作。最后可以封装接口提供给下游服务使用。但是对于一个复杂的场景来说,对于每张表进行各自的增删改查,这样的模型是松散的。以下介绍一个订单的简单例子。对于订单,在订单上下文中属于聚合根,根据领域驱动设计的命令事件法,命令是下订单,返回的事件是订单已生成。因此,我们此文通过对订单这个聚合根的讲解,来突出聚合对于整个业务逻辑的好处。
一般我们在电商网站下单,需要付钱,需要填写地址等。因此,订单肯定有订单号、费用以及用户的收件地址,对于事件来说会存在一个状态。比如下单后会存在待付款、已付款、运送中等多个状态。对于一个订单我们可能会买多个物品,因此在订单中有多个条目。存在一对多的关系,因此item(条目)可以单独成表,与订单表是多对一的关系。至于订单聚合根中地址状态可以与订单同一个状态,也可以分开。与订单形成一对一的关系。本文中讲解的例子是分开。以下是本文讲解订单案例的数据库ER图。
上述数据库关联的sql语句如下:
use order_s;
create table order_table(
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
`order_name` varchar(64) NULL DEFAULT NULL COMMENT '订单名称' ,
primary key(`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table order_item (
`product_id` varchar(64) NULL DEFAULT NULL COMMENT '产品id' ,
`qty` int(11) NULL DEFAULT 0 COMMENT "金额" ,
`order_table` int(11) NULL DEFAULT 0 COMMENT '订单id' ,
`order_table_key` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
primary key(`order_table_key`),
CONSTRAINT `order_item_ibfk` FOREIGN KEY(`order_table`) REFERENCES `order_table` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table address (
`street` varchar(64) NULL DEFAULT NULL COMMENT '街道',
`zip` varchar(64) NULL DEFAULT NULL COMMENT '位置' ,
`order_table` int(11) NULL DEFAULT 0 COMMENT '订单id' ,
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
PRIMARY KEY (`id`),
CONSTRAINT `address_ibfk` FOREIGN KEY(`order_table`) REFERENCES `order_table` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table order_status (
`state` int(11) NULL DEFAULT 0 COMMENT '状态' ,
`order_table` int(11) NULL DEFAULT 0 COMMENT '订单id' ,
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
PRIMARY KEY (`id`),
CONSTRAINT `status_ibfk` FOREIGN KEY(`order_table`) REFERENCES `order_table` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)ENGINE=InnoDB DEFAULT CHARSET=utf8;
根据数据库模型给出demo。
order类代码:
根据Order类可以看出Order这个聚合根与OrderItem是一对多的关系,与Address和OrderStatus都是一对一的关系。除了通过@OneToOne和@OneToMany注释建立关联。同时需要在被关联对象中建立与维护端的关系,如下列代码中的代码片段在address赋值之后,同时需要将当前order对象赋值给Address对象中关联的order属性。这样两者便相互建立了关系。
private Order(Address address, Collection<OrderItem> items,String name) {
this.m_Address = address;
this.m_OrderStatus = new Placed();
this.name = name;
//需要与维持方建立关系,这里订单与地址一对一的关系,地址需要与订单建立关系
m_Address.setOrder(this);
this.items = items.stream().map(r->{
r.setOrder(this);
return r;}).collect(Collectors.toList());
m_OrderStatus.setOrder(this);
}
/**
* ClassName Order
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
@Table(name = "order_table")
@Entity
@Proxy(lazy = false)
public class Order {
@OneToOne(mappedBy = "order",fetch=FetchType.LAZY,cascade = CascadeType.ALL)
//@JsonBackReference
private Address m_Address;
@OneToOne(mappedBy = "order",fetch=FetchType.LAZY,cascade = CascadeType.ALL)
private OrderStatus m_OrderStatus;
@OneToMany(mappedBy = "order",cascade = CascadeType.ALL,fetch = FetchType.LAZY)
//@JsonBackReference
private Collection<OrderItem> items;
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "order_name")
private String name;
public Order() {
items = new ArrayList<>();
m_OrderStatus = new Placed();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void addItem(OrderItem item) {
items.add(item);
}
public Collection<OrderItem> getItems() {
return items;
}
public void setItems(Collection<OrderItem> items) {
this.items = items;
}
public Address getM_Address() {
return m_Address;
}
public void setM_Address(Address m_Address) {
this.m_Address = m_Address;
}
public OrderStatus getM_OrderStatus() {
return m_OrderStatus;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean setM_OrderStatus(OrderStatus m_OrderStatus) {
if(m_OrderStatus==null){
return false;
}
OrderStatus status = getM_OrderStatus();
if(status==null){
return false;
}
OrderStatus orderStatusN = status.next();
if (orderStatusN.getState() == m_OrderStatus.getState()) {
this.m_OrderStatus = orderStatusN;
return true;
} else
return false;
}
private Order(Address address, Collection<OrderItem> items,String name) {
this.m_Address = address;
this.m_OrderStatus = new Placed();
this.name = name;
//需要与维持方建立关系,这里订单与地址一对一的关系,地址需要与订单建立关系
m_Address.setOrder(this);
this.items = items.stream().map(r->{
r.setOrder(this);
return r;}).collect(Collectors.toList());
m_OrderStatus.setOrder(this);
}
public static OrderVOBuilder builder() {
return new OrderVOBuilder();
}
public static class OrderVOBuilder{
private Address address;
private Collection<OrderItem> items;
private String name;
public OrderVOBuilder withAddress(Address address) {
this.address = address;
return this;
}
public OrderVOBuilder withItems(Collection<OrderItem> items) {
this.items = items;
return this;
}
public OrderVOBuilder withName(String name){
this.name = name;
return this;
}
public Order build() {
return new Order(address, items,name);
}
}
}
OrderItem:
/**
* ClassName OrderItem
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
@Entity
@Table(name = "order_item")
@Proxy(lazy = false)
public class OrderItem {
@Column(name = "product_id")
private String productId;
private int qty;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "order_table_key")
private int order_table_key;
//JoinColumn中name是表中的外键名,referencedColumnName代表关联表的字段
@ManyToOne
@JoinColumn(name = "order_table",referencedColumnName = "id")
@JsonBackReference
private Order order;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public int getQty() {
return qty;
}
public void setQty(int qty) {
this.qty = qty;
}
public int getOrder_table_key() {
return order_table_key;
}
public void setOrder_table_key(int order_table_key) {
this.order_table_key = order_table_key;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
Address:
JPA自带的注释@OneToOne,同时@JoinColumn注释来将外键与关联表的主键关联起来。
/**
* ClassName Address
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
@Table(name = "address")
@Entity
@Proxy(lazy = false)
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "street")
private String street;
@Column(name = "zip")
private String zip;
@OneToOne(targetEntity = Order.class)
@JoinColumn(name = "order_table",referencedColumnName = "id")
@JsonBackReference
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Address() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}
OrderStatus:
/**
* ClassName OrderStatus
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
@Table(name = "order_status")
@Entity
@Proxy(lazy = false)
@ToString
public class OrderStatus {
protected int state = -1;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@OneToOne(targetEntity = Order.class)
@JoinColumn(name = "order_table",referencedColumnName = "id")
@JsonBackReference
private Order order;
public OrderStatus(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public OrderStatus(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
//切换的时候记得把其他的都要copy过来,仅有状态发生改变
public OrderStatus next() {
OrderStatus orderStatus = null;
if (state == 0) {
state = state+1;
}
else if (state == 1) {
state = state+1;
} else {
state = -1;
}
return this;
}
}
OrderStatus有三个子类
Payment:
/**
* ClassName Payment
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
public class Payment extends OrderStatus {
public Payment() {
super(1);//表示已支付
}
public OrderStatus next() {
return new Delivery();
}
}
Placed:
/**
* ClassName Placed
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
public class Placed extends OrderStatus {
public Placed() {
super(0);
}
@Override
public OrderStatus next() {
return new Payment();
}
}
Delivery:
/**
* ClassName Delivery
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
public class Delivery extends OrderStatus {
public Delivery() {
super(2);//已发货
}
@Override
public OrderStatus next() {
return null;
}
}
仓储层:
/**
* ClassName OrderRepository
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
public interface OrderRepository extends CrudRepository<Order,Integer> {
}
接口层:
/**
* ClassName OrderController
*
* @author carson
* @description
* @Version V1.0
* @createTime
*/
@RestController
@Transactional(rollbackFor = Exception.class)
public class OrderController {
@Resource
private OrderRepository orderRepo;
@PostMapping("/orders")
public Order placeOrder(@RequestBody OrderItem orderItem,
@RequestParam String name,
@RequestParam String street,
@RequestParam String zip) {
Collection<OrderItem> orderItems = new ArrayList<>();
((ArrayList<OrderItem>) orderItems).add(orderItem);
Order order = Order.builder().withName(name).withAddress(createAddress(street,zip)).withItems(orderItems).build();
System.out.println("order:"+order);
return orderRepo.save(order);
}
@PutMapping("/orders/updateAddress")
public Order updateAddress(@RequestParam String street,@RequestParam Integer orderId,@RequestParam String zip){
Order order = orderRepo.findById(orderId).orElse(new Order());
Address address = order.getM_Address();
if(Objects.isNull(address)){
address = createAddress(street, zip);
}else {
address.setZip(zip);
address.setStreet(street);
}
order.setM_Address(address);
return orderRepo.save(order);
}
@PostMapping("/orders/payment")
public Order payment(@RequestParam Integer orderId) {
Order orderSaved2 = orderRepo.findById(orderId).orElse(new Order());
if (orderSaved2.setM_OrderStatus(new Payment()))
orderRepo.save(orderSaved2);
return orderSaved2;
}
@PostMapping("/orders/delivery")
public Order delivery(@RequestParam Integer orderId) {
Order orderSaved2 = orderRepo.findById(orderId).orElse(new Order());
if (orderSaved2.setM_OrderStatus(new Delivery()))
orderRepo.save(orderSaved2);
return orderSaved2;
}
private Address createAddress(String street,String zip){
Address address = new Address();
address.setStreet(street);
address.setZip(zip);
return address;
}
}
注意点:
1.一对多和一对一都需要在被维护端的实例中建立与维护端的相互关系
2.注意@Id注释不要引错包,org.springframework.data.annotation.Id;会带来实体转换的错误,切记要引用javax.persistence.Id;
具体代码可以在github上下载,地址:https://github.com/carson0408/order