优雅的Java:01.数据更新如何更优雅

编者按:如何写出更优雅的代码?这是一个恒久的问题,

在 Java 的世界里,SpringBoot 框架最为流程,几乎已经成为事实标准。本系列将围绕 SpringBoot 实战案例进行,介绍如何写出更优雅的 Java 代码。

笔者试图通过一个个的实际案例,抽丝剥茧,探讨 Spring 的设计哲学,探讨 Java 美学。

数据更新案例

数据更新的业务逻辑,很常见,举例例子,这里有一个更新发票信息的业务逻辑,按常规的思路,很容易写出这样的代码:

java 复制代码
public function updateInvoice(UpdateInvoiceForm form) {
    Invoice updateInvoice = new Invoice();
    updateInvoice.setId(form.getId());
    // 更新发票号
    if(StringUtils.isNotBlank(form.getInvoiceTaxNumber())) {
        updateInvoice.setInvoiceTaxNumber(form.getInvoiceTaxNumber());
    }
    // 税额
    if(form.getTaxAmount() != null) {
        updateInvoice.setTaxAmount(form.getTaxAmount());
    }
    //  税前金额
    if(form.getTotalAmountPreTax() != null) {
        updateInvoice.setTotalAmountPreTax(form.getTotalAmountPreTax());
    }

    invoiceMapper.updateByPrimaryKeySelective(updateInvoice);
}

上面的逻辑也简单清晰,只有当某个上传的字段不为空时,才会更新数据库中相应的字段。那么,如果字段很多时,这样的代码就看起来非常冗余,那么,如何优化呢?

我们知道,Java 8 的 Optional 类,有着这样的用户:

java 复制代码
  optionalVal.ifPresent(value -> xxx);

当前我们可以直接使用 Optional 类,将字段的更新逻辑封装在 Optional 类中,这样,代码就显得非常简洁,就像这样:

java 复制代码
  Optional.ofNullable(form.getTaxAmount()).ifPresent(taxAmount -> {updateInvoice.setTaxAmount(taxAmount);})

不过这也有个问题,ifPresent 只支持判断对象是否为 null,无法过滤字符串为空的情况,基于这样的需要,我们可以仿照 Optional 类,自己实现一个封装

其核心代码像这样:

java 复制代码
    public OptionalUtil<T> ifNotNull(Consumer<T> action) {
        if (value != null) {
            action.accept(value);
        }
        return this;
    }

    public OptionalUtil<T> ifNotBlank(Consumer<String> action) {
        if (value instanceof String && StringUtils.isNotBlank((String) value)) {
            action.accept((String) value);
        }
        return this;
    }

上面定义了两个核心方法,ifNotNull 和 ifNotBlank,分别用于判断对象是否为 null 和字符串是否为空,当满足条件时,再执行闭包里面的操作。

以下是完整的代码:

java 复制代码
import java.util.function.Consumer;

import org.apache.commons.lang3.StringUtils;

public class OptionalUtil<T> {

    private final T value;

    private OptionalUtil(T value) {
        this.value = value;
    }

    public static <T> OptionalUtil<T> valueOf(T value) {
        return new OptionalUtil<>(value);
    }

    public OptionalUtil<T> ifBlank(Consumer<String> action) {
        if (value instanceof String && StringUtils.isBlank((String) value)) {
            action.accept((String) value);
        }
        return this;
    }

    public OptionalUtil<T> ifNotBlank(Consumer<String> action) {
        if (value instanceof String && StringUtils.isNotBlank((String) value)) {
            action.accept((String) value);
        }
        return this;
    }

    public OptionalUtil<T> ifNull(Consumer<T> action) {
        if (value == null) {
            action.accept(value);
        }
        return this;
    }

    public OptionalUtil<T> ifNotNull(Consumer<T> action) {
        if (value != null) {
            action.accept(value);
        }
        return this;
    }

    public OptionalUtil<T> ifZero(Consumer<T> action) {
        if (isZeroValue(value)) {
            action.accept(value);
        }
        return this;
    }

    private boolean isZeroValue(Object value) {
        if (value instanceof Number && ((Number) value).doubleValue() == 0.0d) {
            return true;
        }
        if (value instanceof java.util.Date && ((java.util.Date) value).getTime() <= 0) {
            return true;
        }
        return false;
    }

    public OptionalUtil<T> ifNotZero(Consumer<T> action) {
        if (!isZeroValue(value)) {
            action.accept(value);
        }
        return this;
    }

    public void execute() {
        // 可以用于触发链式调用的结束,也可以扩展为执行某些默认操作
    }
}

可以看到,这里面封装了一个 OptionalUtil 类,用于处理 Optional 类的链式调用。里面包括一个静态构造方法 valueOf, 同时支持各类不同的类型,

不论是 String,还是继承 Number 的类,如 Integer,Long,BigDecimal 等,进行判断。

时间 ifZero 也支持时间类型,如果传的时间为 1970-01-01 00:00:00 及以前的时间,也视做零值。

这样,我们的更新代码就可以写成这样:

java 复制代码
public function updateInvoice(UpdateInvoiceForm form) {
    Invoice updateInvoice = new Invoice();
    updateInvoice.setId(form.getId());
    // 更新发票号
    OptionalUtil.valueOf(form.getInvoiceTaxNumber()).ifNotBlank(updateInvoice::setInvoiceTaxNumber);
    // 税额
    OptionalUtil.valueOf(form.getTaxPreAmount()).ifNotBlank(updateInvoice::setTaxPreAmount);
    //  税前金额
    OptionalUtil.valueOf(form.getTotalAmountPreTax()).ifNotBlank(updateInvoice::setTotalAmountPreTax);

    invoiceMapper.updateByPrimaryKeySelective(updateInvoice);
}

这样,优雅的数据更新就实现了,你觉得怎么样呢,欢迎在评论区交流。

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔2 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海3 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说7 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json