Преобразование объекта в модель предметной области
Приведённый ниже код используется для запроса заказа и возвращает Aggregate<Order>
. После запроса к базе данных и создания Order-агрегата вызывается AggregateFactory.createAggregate
для создания объекта Aggregate<T>
. Внутри Aggregate<T>
, он автоматически сохраняет снимок Order для последующего сравнения.
public Aggregate<Order> findById(String id) {
OrderDO orderDO = orderMapper.selectByPrimaryKey(id);
if (orderDO == null) {
throw new EntityNotFoundException("Order(" + id + ") not found"););
}
Order order = orderDO.toOrder();
order.setCustomer(customerRepository.findById(orderDO.getCustomerId()));
order.setItems(getOrderItems(id));
return AggregateFactory.createAggregate(order);
}
Интерфейс save
используется для выполнения операций добавления, изменения и удаления строк заказа и заказа. Пример кода:
void save(Aggregate<Order> orderAggregate) {
if (orderAggregate.isNew()) {
//insert order
Order order = orderAggregate.getRoot();
orderMapper.insert(new OrderDO(order));
//insert order items
List<OrderItemDO> itemDOs = order.getItems().stream()
.map(item -> new OrderItemDO(order.getId(), item))
.collect(Collectors.toList());
orderItemMapper.insertAll(itemDOs);
} else if (orderAggregate.isChanged()) {
//update order
updateAggregateRoot(orderAggregate);
//delete the removed order items from DB
removeOrderItems(orderAggregate);
//update the changed order items
updateOrderItems(orderAggregate);
//insert the new order items into DB
insertOrderItems(orderAggregate);
}
}
В этом примере кода, когда orderAggregate.isNew()
истинно, данные вставляются с помощью MyBatis Mapper. В противном случае, если агрегат был изменён, необходимо обновить данные.
Сначала обновляется корень агрегата. Объект домена (Order) сначала преобразуется в объект данных (OrderDO), затем DataObjectUtils сравнивает историческую версию OrderDO, чтобы получить значение Delta, и, наконец, обновляет базу данных с помощью метода обновления MyBatis select. Код выглядит следующим образом:
private void updateAggregateRoot(Aggregate<Order> orderAggregate) {
//only update changed fields, avoid update all fields
OrderDO newOrderDO = new OrderDO(orderAggregate.getRoot());
Set<String> changedFields = DataObjectUtils.getChangedFields(orderAggregate.getRootSnapshot(), orderAggregate.getRoot());
if (orderMapper.updateByPrimaryKeySelective(newOrderDO, changedFields) != 1) {
throw new OptimisticLockException(String.format("Update order (%s) error, it's not found or changed by another user",
orderAggregate.getRoot().getId()));
}
}
Для добавления, удаления и изменения строк заказа используются методы Aggregate
, а затем выполняются операции с базой данных. Пример кода выглядит следующим образом:
private void removeOrderItems(Aggregate<Order> orderAggregate) {
Collection<OrderItem> removedEntities = orderAggregate.findRemovedEntities(Order::getItems, OrderItem::getId);
removedEntities.stream().forEach((item) -> {
if (orderItemMapper.deleteByPrimaryKey(item.getId()) != 1) {
throw new OptimisticLockException(String.format("Delete order item (%d) error, it's not found", item.getId()));
}
});
}
private void updateOrderItems(Aggregate<Order> orderAggregate) {
Collection<ChangedEntity<OrderItem>> entityPairs = orderAggregate.findChangedEntitiesWithOldValues(Order::getItems, OrderItem::getId);
for (ChangedEntity<OrderItem> pair : entityPairs) {
Set<String> changedFields = DataObjectUtils.getChangedFields(pair.getOldEntity(), pair.getNewEntity());
OrderItemDO orderItemDO = new OrderItemDO(orderAggregate.getRoot().getId(), pair.getNewEntity());
if (orderItemMapper.updateByPrimaryKeySelective(orderItemDO, changedFields) != 1) {
throw new OptimisticLockException(String.format("Update order item (%d) error, it's not found", orderItemDO.getId()));
}
}
}
private void insertOrderItems(Aggregate<Order> orderAggregate) {
Collection<OrderItem> newEntities = orderAggregate.findNewEntities(Order::getItems, (item) -> item.getId() == null);
if (newEntities.size() > 0) {
List<OrderItemDO> itemDOs = newEntities.stream().map(item -> new OrderItemDO(orderAggregate.getRoot().getId(), item)).collect(Collectors.toList());
orderItemMapper.insertAll(itemDOs);
}
}
``` Агрегатные методы `findXXXEntities`, предоставляемые `Aggregate<T>`, предназначены для работы с наборами сущностей, такими как строки деталей заказа. Например, в детали заказа может быть добавлен товар A, изменено количество товара B или удалён товар C. Методы `findXXXEntities` используются для обнаружения этих изменений.
Первый параметр — это функциональный интерфейс, который используется для получения набора сущностей и идентификации новых, изменённых или удалённых сущностей в этом наборе. Второй параметр также является функциональным интерфейсом, который получает значение первичного ключа сущности.
Следует отметить, что при изменении агрегата необходимо обновить номер версии корня агрегата независимо от того, изменился ли корень агрегата. Это гарантирует, что агрегат будет обновлён как единое целое и предотвратит возникновение несогласованности данных при параллельных изменениях.
### 3.3 Удаление заказа
При удалении заказа необходимо также удалить все строки деталей этого заказа.
```java
public void remove(Aggregate<Order> aggregate) {
Order order = aggregate.getRoot();
if (orderMapper.delete(new OrderDO(order)) != 1) {
throw new OptimisticLockException(
String.format("Delete order (%s) error, it's not found or changed by another user", order.getId())
);
}
orderItemMapper.deleteByOrderId(order.getId());
}
Полный пример кода можно найти в проекте «Агрегатная персистентность» на GitHub (https://github.com/meixuesong/aggregate-persistence-sample). Этот проект демонстрирует, как использовать Mybatis для реализации агрегатной персистентности и сохранять только изменённые данные. Например, если в таблице есть 20 полей и изменяется только одно поле, то при использовании этого подхода будет изменено только одно поле в базе данных, а не все поля.
В целом, этот проект предлагает облегчённое решение для агрегатной персистентности, которое помогает разработчикам создавать чистые доменные модели и хорошо поддерживает работу репозиториев. Используя моментальный снимок корня агрегата, Aggregate<T>
может определить, какие изменения произошли в агрегате, после чего репозиторий использует оптимистичную блокировку на основе версии и функцию сравнения свойств DataObjectUtils для выборочного обновления базы данных.
Версия 1.2: Изменён способ определения изменений. Ранее использовался метод сравнения значений полей, и если значение было равно null, изменение не определялось. В новой версии используется метод DataObjectUtils.getChangedFields
для получения имён изменённых полей.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )