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

如何泛化JPA的JsonConverter实现以消除代码重复?

泛化JSON转换器以消除代码重复

你的场景中多个AttributeConverter仅泛型类型不同,逻辑完全重复,可以通过泛型抽象类提取通用逻辑,让具体转换器仅需指定目标类型即可。

方案1:基础泛型抽象类(无Spring依赖)

首先创建通用的抽象转换器,封装所有重复的序列化/反序列化逻辑:

@Slf4j
public abstract class GenericJsonConverter<T> implements AttributeConverter<T, String> {

    private final ObjectMapper objectMapper;
    private final Class<T> targetClass;

    // 构造方法传入目标类型的Class对象
    protected GenericJsonConverter(Class<T> targetClass) {
        this.targetClass = targetClass;
        this.objectMapper = new ObjectMapper();
        // 仅初始化一次JavaTimeModule,避免重复注册
        objectMapper.registerModule(new JavaTimeModule());
    }

    @Override
    public String convertToDatabaseColumn(T attribute) {
        if (attribute == null) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(attribute);
        } catch (JsonProcessingException ex) {
            log.error("无法将对象 {} 序列化为JSON", attribute, ex);
            throw new InternalServerErrorException("无法将对象转换为JSON字符串", ex);
        }
    }

    @Override
    public T convertToEntityAttribute(String dbData) {
        if (dbData == null) {
            return null;
        }
        try {
            return objectMapper.readValue(dbData, targetClass);
        } catch (JsonProcessingException ex) {
            log.error("无法将JSON字符串 {} 反序列化为对象", dbData, ex);
            throw new InternalServerErrorException("无法将JSON字符串转换为对象", ex);
        }
    }
}

然后,每个具体的转换器只需继承该抽象类,指定泛型并传入目标类:

// IndUserInfo对应的转换器
@Converter(autoApply = true)
public class IndUserInfoJsonConverter extends GenericJsonConverter<IndUserInfo> {
    public IndUserInfoJsonConverter() {
        super(IndUserInfo.class);
    }
}

// UserInfo对应的转换器
@Converter(autoApply = true)
public class UserInfoJsonConverter extends GenericJsonConverter<UserInfo> {
    public UserInfoJsonConverter() {
        super(UserInfo.class);
    }
}

// 其他如DuplicateInfo、PartnershipInfo等转换器依此类推

方案2:Spring环境下复用全局ObjectMapper

如果你的项目基于Spring,建议复用容器中配置好的ObjectMapper(比如已经配置了JavaTimeModule、自定义序列化规则等),避免重复创建实例:

@Slf4j
public abstract class GenericJsonConverter<T> implements AttributeConverter<T, String> {

    private final ObjectMapper objectMapper;
    private final Class<T> targetClass;

    // 注入Spring容器中的全局ObjectMapper
    protected GenericJsonConverter(ObjectMapper objectMapper, Class<T> targetClass) {
        this.objectMapper = objectMapper;
        this.targetClass = targetClass;
    }

    // 序列化、反序列化方法同方案1
    @Override
    public String convertToDatabaseColumn(T attribute) {
        if (attribute == null) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(attribute);
        } catch (JsonProcessingException ex) {
            log.error("无法将对象 {} 序列化为JSON", attribute, ex);
            throw new InternalServerErrorException("无法将对象转换为JSON字符串", ex);
        }
    }

    @Override
    public T convertToEntityAttribute(String dbData) {
        if (dbData == null) {
            return null;
        }
        try {
            return objectMapper.readValue(dbData, targetClass);
        } catch (JsonProcessingException ex) {
            log.error("无法将JSON字符串 {} 反序列化为对象", dbData, ex);
            throw new InternalServerErrorException("无法将JSON字符串转换为对象", ex);
        }
    }
}

具体子类通过@Autowired注入ObjectMapper:

@Converter(autoApply = true)
public class IndUserInfoJsonConverter extends GenericJsonConverter<IndUserInfo> {
    @Autowired
    public IndUserInfoJsonConverter(ObjectMapper objectMapper) {
        super(objectMapper, IndUserInfo.class);
    }
}

最终使用

实体类中的@Convert注解无需修改,继续使用对应的具体转换器即可:

@Entity
@Table(name = "orders")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {

    @Convert(converter = UserInfoJsonConverter.class)
    private UserInfo userInfo;

    @Convert(converter = IndUserInfoJsonConverter.class)
    private IndUserInfo indUserInfo;

    // 其他字段保持不变
}

核心优化点

  • 所有重复的序列化/反序列化逻辑集中在抽象类,仅需维护一次
  • ObjectMapper仅初始化/配置一次,避免重复注册模块带来的性能损耗
  • 具体转换器代码极简,仅需指定目标类型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 16:30:16