You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus中是否有等价于Spring BeanUtils.copyProperties的属性复制方法?

在Quarkus中替代Spring BeanUtils.copyProperties的方案

你在Spring里常用的BeanUtils.copyProperties属性复制功能,在Quarkus里可以通过以下几种方案实现,避免逐个编写setter方法,同时支持忽略指定属性、跳过null值等需求:

方案1:使用Apache Commons BeanUtils/PropertyUtils

这是通用的Java Bean属性复制工具,Quarkus中可以直接引入依赖使用:

步骤1:添加依赖(Maven)

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>

步骤2:在Update方法中使用

可以配置忽略指定属性,同时跳过null值:

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.IntrospectionContext;

// ...

@PUT
@Path("/{id}")
@Transactional
public MyEntity update(@PathParam("id") Long id, MyEntity inputBean) {
    MyEntity entity = myEntityRepository.findById(id);
    if(null == entity) {
        throw new NotFoundException();
    }

    // 配置BeanUtils:忽略id属性,不复制null值
    BeanUtilsBean beanUtils = new BeanUtilsBean();
    // 跳过null值
    beanUtils.getConvertUtils().register(false, false, 0);
    // 添加忽略属性
    beanUtils.getPropertyUtils().addBeanIntrospector((IntrospectionContext context) -> {
        context.removeProperty("id");
        // 可添加更多忽略属性,比如"createdAt"、"updatedAt"等
    });
    
    // 执行属性复制
    try {
        beanUtils.copyProperties(entity, inputBean);
    } catch (Exception e) {
        // 异常处理,比如日志打印
        e.printStackTrace();
    }

    persistAndFlush(entity);
    return entity;
}

方案2:MapStruct(推荐,编译期安全)

MapStruct是编译期生成映射代码的工具,类型安全、性能优异,非常适合复杂实体的属性映射,支持灵活配置忽略属性和null值处理:

步骤1:添加依赖(Maven)

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
    <scope>provided</scope>
</dependency>

步骤2:创建Mapper接口

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValuePropertyMappingStrategy;

@Mapper(componentModel = "cdi", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface MyEntityMapper {

    // 忽略id属性,并且跳过输入中的null值(不覆盖实体已有非null值)
    @Mapping(target = "id", ignore = true)
    void updateEntityFromInput(MyEntity inputBean, @MappingTarget MyEntity entity);
}

步骤3:在Update方法中注入使用

import jakarta.inject.Inject;

// ...

@Inject
MyEntityMapper myEntityMapper;

@PUT
@Path("/{id}")
@Transactional
public MyEntity update(@PathParam("id") Long id, MyEntity inputBean) {
    MyEntity entity = myEntityRepository.findById(id);
    if(null == entity) {
        throw new NotFoundException();
    }

    // 调用MapStruct生成的映射方法
    myEntityMapper.updateEntityFromInput(inputBean, entity);

    persistAndFlush(entity);
    return entity;
}

方案3:Quarkus Panache原生更新(简单场景)

如果你的更新字段较少,可以直接使用Panache的update方法执行JPQL批量更新,无需先查询实体:

@PUT
@Path("/{id}")
@Transactional
public MyEntity update(@PathParam("id") Long id, MyEntity inputBean) {
    // 直接执行更新语句
    int updatedCount = MyEntity.update(
        "attribute1 = ?1, attribute2 = ?2 where id = ?3",
        inputBean.getAttribute1(),
        inputBean.getAttribute2(),
        id
    );

    if(updatedCount == 0) {
        throw new NotFoundException();
    }

    return MyEntity.findById(id);
}

方案4:自定义反射工具类(无额外依赖)

如果不想引入第三方依赖,可以自己实现一个轻量的属性复制工具,支持忽略指定属性和跳过null值:

import java.lang.reflect.Field;

public class BeanCopyUtil {

    public static void copyProperties(Object source, Object target, String... ignoreProperties) {
        if (source == null || target == null) {
            return;
        }

        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();

        for (Field sourceField : sourceClass.getDeclaredFields()) {
            sourceField.setAccessible(true);
            // 检查是否需要忽略当前属性
            boolean isIgnored = false;
            for (String ignoreProp : ignoreProperties) {
                if (sourceField.getName().equals(ignoreProp)) {
                    isIgnored = true;
                    break;
                }
            }
            if (isIgnored) {
                continue;
            }

            try {
                Object value = sourceField.get(source);
                // 跳过null值,避免覆盖实体非null属性
                if (value == null) {
                    continue;
                }

                Field targetField = targetClass.getDeclaredField(sourceField.getName());
                targetField.setAccessible(true);
                targetField.set(target, value);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                // 异常处理,比如记录日志
                e.printStackTrace();
            }
        }
    }
}

使用方式

@PUT
@Path("/{id}")
@Transactional
public MyEntity update(@PathParam("id") Long id, MyEntity inputBean) {
    MyEntity entity = myEntityRepository.findById(id);
    if(null == entity) {
        throw new NotFoundException();
    }

    // 复制属性,忽略id
    BeanCopyUtil.copyProperties(inputBean, entity, "id");

    persistAndFlush(entity);
    return entity;
}

内容的提问来源于stack exchange,提问作者develox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 03:44:53