Как обновить несколько записей в БД в Spring Boot?

Есть у меня ситуация: в запросе передается список элементов, мне нужно обновить эти элементы в базе данных(они там уже есть).

Есть ли способ обновить их с помощью JPA/Hibernate? Или же лучше положиться на код и обновить их в цикле да сохранить сразу все? Суть метода в том, чтобы к полю settlmentAmount добавить(или отнять) определённое значение.

public void updateListSettlementAmountById(List<UpdateListSettlementAmountRequest> requests) {
    Iterable<Ingredient> ingredients = ingredientRepository
            .findAllById(requests.stream()
            .map(UpdateListSettlementAmountRequest::getId).toList());
    Map<Short, UpdateListSettlementAmountRequest> requestMap = requests
            .stream()
            .collect(Collectors.toMap(UpdateListSettlementAmountRequest::getId, param -> param));
    ingredients.forEach(ingredient -> {
        UpdateListSettlementAmountRequest param = requestMap.get(ingredient.getId());
        if(param.isReceipt() && param.getId() != null){
            ingredient.setSettlementAmount(ingredient.getSettlementAmount().add(param.getAmount()));
        } else if(!param.isReceipt() && param.getId() != null){
            ingredient.setSettlementAmount(ingredient.getSettlementAmount().subtract(param.getAmount()));
        }
    });
    ingredientRepository.saveAll(ingredients);

}

Сущности, которые использую в методе:

package ru.buzynnikov.inventory_service.models;

import jakarta.persistence.*;

import java.math.BigDecimal;
import java.util.Objects;

/**
* Модель сущности ингредиента, хранящейся в базе данных.
* Эта сущность предназначена для хранения данных об ингредиенте и поддерживает следующие поля:
 *  уникальный идентификатор ({@code id}),
 *  наименование ингредиента ({@code name}),
 *  количество доступного ингредиента ({@code amount}),
 *  расчетная количество ингредиента ({@code settlementAmount}),
 *  процент отходов при использовании ингредиента ({@code wastePercent}).
 */
@Entity
public class Ingredient {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Short id;

    private String name;

    @Column(precision = 7, scale = 3)           // Аннотация задаёт точность числа и количество знаков после запятой
    private BigDecimal amount;

    @Column(name = "settlement_amount", precision = 7, scale = 3)
    private BigDecimal settlementAmount;

    @Column(name = "waste_percentage", precision = 4, scale = 3)
    private BigDecimal wastePercent;

    public Short getId() {
        return id;
    }

    public void setId(Short id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public BigDecimal getSettlementAmount() {
        return settlementAmount;
    }

    public void setSettlementAmount(BigDecimal settlementAmount) {
        this.settlementAmount = settlementAmount;
    }

    public BigDecimal getWastePercent() {
        return wastePercent;
    }

    public void setWastePercent(BigDecimal wastePercent) {
        this.wastePercent = wastePercent;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Ingredient that = (Ingredient) o;
        return Objects.equals(id, that.id) && Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Ingredient{");
        sb.append("id=").append(id);
        sb.append(", name='").append(name).append('\'');
        sb.append(", amount=").append(amount);
        sb.append(", settlementAmount=").append(settlementAmount);
        sb.append(", wastePercent=").append(wastePercent);
        sb.append('}');
        return sb.toString();
    }
}

И вот:

package ru.buzynnikov.inventory_service.controllers.dto;

import java.math.BigDecimal;

public class UpdateListSettlementAmountRequest {
    private Short id;
    private BigDecimal amount;
    private Boolean receipt;

    public Short getId() {
        return id;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public Boolean isReceipt() {
        return receipt;
    }
}

Ответы (1 шт):

Автор решения: Денис Старакожев

Суть JPA это работа с persience контекстом, которая предполагает загрузку данных из БД, их модификацию на уровне кода и отправку обратно диффа который вычисляется автоматически.
То что вы хотите противоречит концепции JPA и если нет проблем с производительностью, и имеет смысл только для для оптимизации.

Есть синтаксис на JPQL для такого:

@Modifying
@Query("UPDATE Ingredient i 
        SET i.settlementAmount= i.settlementAmount+ :delta 
        WHERE a.id = :id")
int midify(@Param("delta") BigDecimal amount, @Param("id") Long id);

Конкретно в вашем случае лучше оставить как есть, потому что для каждого значения число может быть уникальным и собрать запрос будет себе дороже.

→ Ссылка