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

Java对接Firestore时com.google.type.Money出现getter冲突如何解决

问题原因

Firebase Firestore的Java SDK序列化对象时会基于Java Bean规范反射扫描所有get开头的方法,映射为文档字段。你使用的com.google.type.Money是Protobuf协议生成的类,生成逻辑中同时在类本体和内部实现的接口逻辑中生成了同名的getDefaultInstanceForType方法,两个方法签名一致,触发Firestore序列化的冲突校验。

解决方案

方案1:自定义Firestore类型转换器(推荐,改动最小)

实现Firestore的自定义转换器,将Money类直接序列化为Firestore支持的Map结构,避开默认的反射扫描逻辑:
首先实现转换器类:

import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.FirestoreCustomConverter;
import com.google.type.Money;
import java.util.HashMap;
import java.util.Map;

public class MoneyConverter implements FirestoreCustomConverter<Money, Map<String, Object>> {
    @Override
    public Map<String, Object> toFirestore(Money value) {
        if (value == null) return null;
        Map<String, Object> map = new HashMap<>();
        map.put("currencyCode", value.getCurrencyCode());
        map.put("units", value.getUnits());
        map.put("nanos", value.getNanos());
        return map;
    }

    @Override
    public Money fromFirestore(Map<String, Object> value) throws FirebaseFirestoreException {
        if (value == null) return null;
        return Money.newBuilder()
                .setCurrencyCode((String) value.get("currencyCode"))
                .setUnits((Long) value.get("units"))
                .setNanos((Integer) value.get("nanos"))
                .build();
    }
}

然后在你的实体类的Money字段上加上转换器注解即可:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Document(collectionName = "ModelClassCollection")
public class MyModelClass {

    @DocumentId
    private String id;

 .... HIDDEN FIELDS  ....

    @FirestorePropertyConverter(converter = MoneyConverter.class)
    private Money total;

    @FirestorePropertyConverter(converter = MoneyConverter.class)
    private Money municipalTax;

    @FirestorePropertyConverter(converter = MoneyConverter.class)
    private Money federalTax;
}

方案2:自定义业务金额类(稳定性最高)

Protobuf生成的Money类本身是为RPC通信设计的,并不适合作为ORM实体类的字段类型,你可以自行实现适配业务的金额类,完全避开序列化冲突:

import java.math.BigDecimal;
import java.math.RoundingMode;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class BizMoney {
    private String currencyCode;
    private long units;
    private int nanos;

    // 按需增加和BigDecimal互转的工具方法
    public static BizMoney fromBigDecimal(BigDecimal amount, String currencyCode) {
        BigDecimal unitsPart = amount.setScale(0, RoundingMode.DOWN);
        BigDecimal nanosPart = amount.subtract(unitsPart).multiply(new BigDecimal(1_000_000_000));
        return new BizMoney(
                currencyCode,
                unitsPart.longValue(),
                nanosPart.intValue()
        );
    }

    public BigDecimal toBigDecimal() {
        return new BigDecimal(units).add(new BigDecimal(nanos, 9));
    }
}

直接把实体类里的Money类型替换为BizMoney即可,无需额外配置。

方案3:改用字符串存储金额

如果你的业务不需要多币种,也可以直接把金额转成保留固定小数位的数字字符串存储,读取后再转回BigDecimal处理,实现更简单。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 22:36:05