在CQRS(Command Query Responsibility Segregation)架构中,读操作(Query)和写操作(Command)被分离到不同的模型中。这样可以优化读取和写入的性能,并且允许使用不同的存储技术来实现读取和写入模型。Hibernate通常用于处理写操作,因为它擅长处理复杂的交易和关系数据。
下面我们将展示如何在Spring Boot项目中结合Hibernate实现CQRS架构。
1. 项目依赖
在pom.xml中添加必要的依赖:
xml
<dependencies>
<!-- Spring Boot Starter Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- HikariCP for Connection Pooling -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Data MongoDB (for Query Model) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
</dependencies>
2. 配置数据源和Hibernate属性
在application.properties中配置MySQL和MongoDB数据源:
properties
# MySQL Database Configuration (Command Model)
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=rootpassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
# MongoDB Configuration (Query Model)
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=myquerydatabase
3. 定义写模型(Command Model)
Order.java
java
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Data
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String product;
private int quantity;
private double price;
}
OrderRepository.java
java
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderRepository extends JpaRepository<Order, Long> {
}
4. 定义读模型(Query Model)
OrderView.java
java
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.annotation.Id;
@Data
@Document(collection = "orders")
public class OrderView {
@Id
private String id;
private String product;
private int quantity;
private double price;
}
OrderViewRepository.java
java
import org.springframework.data.mongodb.repository.MongoRepository;
public interface OrderViewRepository extends MongoRepository<OrderView, String> {
}
5. 创建服务层
写操作服务(Command Service)
OrderCommandService.java
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderCommandService {
@Autowired
private OrderRepository orderRepository;
@Transactional
public Order createOrder(Order order) {
return orderRepository.save(order);
}
@Transactional
public Order updateOrder(Order order) {
return orderRepository.save(order);
}
@Transactional
public void deleteOrder(Long id) {
orderRepository.deleteById(id);
}
}
读操作服务(Query Service)
OrderQueryService.java
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class OrderQueryService {
@Autowired
private OrderViewRepository orderViewRepository;
public List<OrderView> getAllOrders() {
return orderViewRepository.findAll();
}
public Optional<OrderView> getOrderById(String id) {
return orderViewRepository.findById(id);
}
}
6. 创建事件和事件处理器
OrderCreatedEvent.java
java
import lombok.Data;
import java.io.Serializable;
@Data
public class OrderCreatedEvent implements Serializable {
private Long orderId;
private String product;
private int quantity;
private double price;
}
OrderUpdatedEvent.java
java
import lombok.Data;
import java.io.Serializable;
@Data
public class OrderUpdatedEvent implements Serializable {
private Long orderId;
private String product;
private int quantity;
private double price;
}
OrderDeletedEvent.java
java
import lombok.Data;
import java.io.Serializable;
@Data
public class OrderDeletedEvent implements Serializable {
private Long orderId;
}
7. 事件发布器和处理器
事件发布器
EventPublisher.java
java
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class EventPublisher {
private final ApplicationEventPublisher applicationEventPublisher;
public EventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void publish(Object event) {
applicationEventPublisher.publishEvent(event);
}
}
事件处理器
OrderEventListener.java
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class OrderEventListener {
@Autowired
private OrderViewRepository orderViewRepository;
@EventListener
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
OrderView orderView = new OrderView();
orderView.setId(event.getOrderId().toString());
orderView.setProduct(event.getProduct());
orderView.setQuantity(event.getQuantity());
orderView.setPrice(event.getPrice());
orderViewRepository.save(orderView);
}
@EventListener
public void handleOrderUpdatedEvent(OrderUpdatedEvent event) {
OrderView orderView = orderViewRepository.findById(event.getOrderId().toString()).orElse(new OrderView());
orderView.setProduct(event.getProduct());
orderView.setQuantity(event.getQuantity());
orderView.setPrice(event.getPrice());
orderViewRepository.save(orderView);
}
@EventListener
public void handleOrderDeletedEvent(OrderDeletedEvent event) {
orderViewRepository.deleteById(event.getOrderId().toString());
}
}
8. 修改服务层发布事件
修改写操作服务(Command Service)
OrderCommandService.java
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderCommandService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private EventPublisher eventPublisher;
@Transactional
public Order createOrder(Order order) {
Order savedOrder = orderRepository.save(order);
OrderCreatedEvent event = new OrderCreatedEvent();
event.setOrderId(savedOrder.getId());
event.setProduct(savedOrder.getProduct());
event.setQuantity(savedOrder.getQuantity());
event.setPrice(savedOrder.getPrice());
eventPublisher.publish(event);
return savedOrder;
}
@Transactional
public Order updateOrder(Order order) {
Order updatedOrder = orderRepository.save(order);
OrderUpdatedEvent event = new OrderUpdatedEvent();
event.setOrderId(updatedOrder.getId());
event.setProduct(updatedOrder.getProduct());
event.setQuantity(updatedOrder.getQuantity());
event.setPrice(updatedOrder.getPrice());
eventPublisher.publish(event);
return updatedOrder;
}
@Transactional
public void deleteOrder(Long id) {
orderRepository.deleteById(id);
OrderDeletedEvent event = new OrderDeletedEvent();
event.setOrderId(id);
eventPublisher.publish(event);
}
}
9. 创建控制器
写操作控制器(Command Controller)
OrderCommandController.java
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/orders")
public class OrderCommandController {
@Autowired
private OrderCommandService orderCommandService;
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
Order createdOrder = orderCommandService.createOrder(order);
return ResponseEntity.ok(createdOrder);
}
@PutMapping("/{id}")
public ResponseEntity<Order> updateOrder(@PathVariable Long id, @RequestBody Order order) {
order.setId(id); // Ensure the order ID is set correctly