如何用函数式优雅的写一个增删改查@落雨
如何用函数式优雅的写一个增删改查@落雨
作为一名专业的
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