iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0
自我挑戰組

Spring In Action系列 第 24

Reactive persistence

  • 分享至 

  • xImage
  •  

這章節在介紹如何讓reactive Spring與資料庫連結。

若是使用關聯式資料庫,我們會引用R2DBC來做這件事,這個模組相當於Spring Data JDBC的功效:

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

大致的寫法其實就跟Spring Data一樣,只不過在不同物件的關聯上不像Spring Data JPA那麼聰明,我們會需要自訂一些做法在Service來達到物件彼此aggregate的儲存,所以在domain class的定義上,有一對多的關係時,我們得先設定成儲存該Id欄位,而不是直接儲存該class物件類別:

package demo;
import java.util.HashSet;
import java.util.Set;
import org.springframework.data.annotation.Id;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@Data
@NoArgsConstructor
@RequiredArgsConstructor
public class Sword {
  @Id
  private Long id;
  private @NonNull String name;
  private Set<Long> ingredientIds = new HashSet<>();
  public void addIngredient(Ingredient ingredient) {
    ingredientIds.add(ingredient.getId());
  }
}

不過這樣就得注意我們使用的資料庫有沒有支援array的欄位格式,目前已知PostgreSQL和H2是有支援的。

接下來我們來看看repository的設置:

package demo.data;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import demo.SwordOrder;

public interface OrderRepository 
    extends ReactiveCrudRepository<SwordOrder, Long> {
}

可以發現跟JPA的設置幾乎一樣,只差別在extends的介面變成ReactiveCrudRepository。

下面來看看這個repository會怎麼用:

@Autowired
OrderRepository orderRepo;
...
orderRepository.findAll()
    .doOnNext(order -> {
      System.out.println(
          "Deliver to: " + order.getDeliveryName());
    })
    .subscribe();

剛剛也有提到由於R2DBC不像JPA那麼聰明的去把aggregate的物件彼此聯繫起來,所以會需要如下額外的code來達到目的:

@Data
public class SwordOrder {
  ...
  @Transient
  private transient List<Sword> swords = new ArrayList<>();
  public void addSword(Sword sword) {
    this.swords.add(sword);
    if (sword.getId() != null) {
      this.swordIds.add(sword.getId());
    }
  }
}
@Service
@RequiredArgsConstructor
public class SwordOrderAggregateService {
  private final SwordRepository swordRepo;
  private final OrderRepository orderRepo;

  public Mono<SwordOrder> save(SwordOrder swordOrder) {
    return Mono.just(swordOrder)
      .flatMap(order -> {
        List<Sword> swords = order.getSwords();
        order.setSwords(new ArrayList<>());
        return swordRepo.saveAll(swords)
            .map(sword -> {
              order.addSword(sword);
              return order;
            }).last();
      })
      .flatMap(orderRepo::save);
  }
}

public Mono<SwordOrder> findById(Long id) {
    return orderRepo
      .findById(id)
      .flatMap(order -> {
        return swordRepo.findAllById(order.getSwordIds())
           .map(taco -> {
            order.addSword(taco);
            return order;
          }).last();
      });
}

上一篇
WebFlux security
下一篇
Send request as a client in reactive programming
系列文
Spring In Action30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言