如何用函數式優雅的寫一個增删改查@落雨
作為一名專業的,本篇介紹如何使用
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