天天看點

如何用函數式優雅的寫一個增删改查@落雨

如何用函數式優雅的寫一個增删改查@落雨

作為一名專業的

CRUD-Boy

,本篇介紹如何使用

io.vavr

工具包來寫一個比較舒服的

有則更新,無則新增

CRUD

小需求

一、io.vavr

1).Optional怎麼玩?jdk8

// 有則更新,無則新增,不推薦
if(optional.isPresent){
    // 有則更新
    optional.get();
} else {
    // 無則新增
}
           

或者

optional.map(record -> {
  // 有則更新
  update(record);
})
// 無則新增
.orElse(getNewRecord());
           

2).如果不用Optional如何寫?

// 把Java的Optional轉為io.vavr的Option,然後判斷空和非空
Option.ofOptional(optional).onEmpty(() -> {
    // 處理空的情況,無則新增
    insert();
}).peek(record -> {
    // 處理非空的情況,有則更新
    update(record);
});

           

io.vavr介紹:

https://www.vavr.io/

如何用函數式優雅的寫一個增删改查@落雨

用到的武器:Optional -> Option

// optional *value*, no more nulls
Option<T> option = Option.of(...);
           

引入pom

<dependency>
     <groupId>io.vavr</groupId>
     <artifactId>vavr</artifactId>
     <version>0.10.3</version>
</dependency>
           

二、寫一個Repo

/**
 * @author luoyu.lzy
 * @Title: AppUserRepo.java
 * @Description:
 * @date 2021/9/1.
 */
public interface AppUserRepo {

    /**
     * 持久化,有則更新,無則新增
     * @param appUser
     * @return
     */
    AppUser save(AppUser appUser);

    /**
     * 根據id擷取DTO
     * @param id
     * @return
     */
    AppUser getById(Long id);
}

           

三、實作這個Repo

/**
 * @author luoyu.lzy
 * @Title:AppUserRepoImpl.java
 * @Description:使用者Repo實作類
 * @date 2021/9/1.
 */
@Component
public class AppUserRepoImpl implements AppUserRepo {
    
    @Autowired
    private AppUserDAO appUserDAO;
    
   /**
    * 查詢DB,根據id擷取Record
    */
    private Optional<AppUserDO> getRecordById(Long id) {
       return Optional.ofNullable(appUserDAO.selectByPrimaryKey(id));
    }
    
    /**
    * bean轉換 DO -> DTO
    */
     @Override
    public AppUser getById(Long id) {
        Optional<AppUserDO> optional = getRecordById(id);
        // toAppUser bean轉換
        return optional.map(this::toAppUser).orElse(null);
    }
    
   /**
    * 儲存,有則更新部分字段,無則新增
    */
    @Override
    public AppUser save(AppUser appUser) {
        Option.ofOptional(getRecordById(appUser.getId())).onEmpty(() -> {
            AppUserDO record = toAppUserDO(appUser);
            record.setGmtCreate(Calendar.getInstance().getTime());
            record.setGmtModified(Calendar.getInstance().getTime());
            int insertResult = appUserDAO.insert(record);
            if (insertResult > 0) {
                record.setId(record.getId());
            }
        }).peek(record -> {
            record.setStatus(appUser.getStatus());
            appUserDAO.updateByPrimaryKey(record);
        });
        return appUser;
    }

}
           

四、Option.of 源碼分析

我們回到這段代碼, 分别是

Option.of(Optional)

.onEmpty()

.peek()

這3個api,我們來看看核心代碼。

// 把Java的Optional轉為io.vavr的Option
Option.ofOptional(xxxOptinal).onEmpty(() -> {
    // 處理空的情況
}).peek(record -> {
    // 處理非空的情況
});

           

再來看看

Option.ofOptional

源碼

/**
     * 包裹Optional對象,傳回Option對象     
     */
    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
    static <T> Option<T> ofOptional(Optional<? extends T> optional) {
    // optional不能為null
        Objects.requireNonNull(optional, "optional is null");
        //通過java.util.Optional.map(Option.of)方法來包裝optional每個元素
        return optional.<Option<T>>map(Option::of).orElseGet(Option::none);
    }
       
     static <T> Option<T> of(T value) {
        // none和same分别用來生産None對象和Same對象,None和Same代表無元素(None=無元素)和有元素(Same=相同類型的元素)
        return (value == null) ? none() : some(value);
    }
    
    /**
     * java.util.Optional
     * 通過Optional.map方法,傳回Optional對象
    */
    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            // 執行mapper函數
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    
   /**
    * 處理OnEmpty事件,如果元素為空,則執行 Runnable.run
    */
    default Option<T> onEmpty(Runnable action) {
        Objects.requireNonNull(action, "action is null");
        if (isEmpty()) {
            action.run();
        }
        return this;
    }
    
   /**
    * 處理peek事件,如果不為empty,則執行Consumer.accept
    */
    default Option<T> peek(Consumer<? super T> action) {
        Objects.requireNonNull(action, "action is null");
        if (isDefined()) {
            action.accept(get());
        }
        return this;
    }
    
    
           

五、整完,CRUD更簡潔

六、Optional的進化

Java 9 Optional API 新增方法

or

,可以實作類似

Option.onEmpty

的功能

Optional<T> optional = Optional.empty();
Optional<T> result = optional.or(() -> doSomeThionEmpty);
           

落雨 http://js-dev.cn

2021-09-13 21:13:00