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
相关产品推荐
相关产品推荐

