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

如何用Hibernate实现多业务实体共享统一本地化表建模?

基于Hibernate实现共享本地化表的实体映射方案

一、全局唯一ID实现

要避免不同业务实体的ID重复,最直接的方案是使用UUID作为所有实体的主键,无需额外逻辑就能保证全局唯一性;也可以给不同实体ID添加专属前缀(如PROD-xxxx、ADDR-xxxx),这里推荐UUID方案:

通用ID基类

@MappedSuperclass
public abstract class BaseEntity {
    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
    @Column(name = "ID", nullable = false, length = 36)
    private String id;

    // getter、setter方法
}

业务实体继承基类

@Entity
@Table(name = "PRODUCTS")
public class Product extends BaseEntity {
    @Column(name = "PRICE")
    private BigDecimal price;

    // 关联本地化数据集合
    @OneToMany(mappedBy = "entity", fetch = FetchType.LAZY)
    private Set<LocalizedData> localizedData = new HashSet<>();

    // 添加本地化数据的便捷方法
    public void addLocalizedData(String type, String locale, String value) {
        LocalizedData data = new LocalizedData(this.getId(), type, locale, value);
        localizedData.add(data);
    }

    // getter、setter方法
}
@Entity
@Table(name = "ADDRESS")
public class Address extends BaseEntity {
    @OneToMany(mappedBy = "entity", fetch = FetchType.LAZY)
    private Set<LocalizedData> localizedData = new HashSet<>();

    // 添加本地化数据的便捷方法同Product
    // getter、setter方法
}

二、LocalizedData实体映射

LOCALIZED_DATA表的复合主键由ENTITY_ID、ENTITY_TYPE、LOCALE组成,需先定义复合主键类:

@Embeddable
public class LocalizedDataId implements Serializable {
    @Column(name = "ENTITY_ID", length = 36)
    private String entityId;

    @Column(name = "ENTITY_TYPE", length = 50)
    private String entityType;

    @Column(name = "LOCALE", length = 10)
    private String locale;

    // 构造方法、equals、hashCode、getter、setter
    public LocalizedDataId() {}

    public LocalizedDataId(String entityId, String entityType, String locale) {
        this.entityId = entityId;
        this.entityType = entityType;
        this.locale = locale;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        LocalizedDataId that = (LocalizedDataId) o;
        return Objects.equals(entityId, that.entityId) &&
                Objects.equals(entityType, that.entityType) &&
                Objects.equals(locale, that.locale);
    }

    @Override
    public int hashCode() {
        return Objects.hash(entityId, entityType, locale);
    }
}

本地化数据实体类

@Entity
@Table(name = "LOCALIZED_DATA")
public class LocalizedData {
    @EmbeddedId
    private LocalizedDataId id;

    @Column(name = "VALUE", length = 255)
    private String value;

    // 双向关联回业务实体,方便操作
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ENTITY_ID", insertable = false, updatable = false)
    private BaseEntity entity;

    // 构造方法
    public LocalizedData() {}

    public LocalizedData(String entityId, String entityType, String locale, String value) {
        this.id = new LocalizedDataId(entityId, entityType, locale);
        this.value = value;
    }

    // getter、setter方法
}

三、使用注意事项

  • 统一ENTITY_TYPE标识:比如Product的名称用PRODUCT_NAME,地址的街道用ADDRESS_STREET,避免不同实体的类型标识混淆。
  • 懒加载优化:本地化数据建议设置为FetchType.LAZY,避免查询实体时自动加载所有本地化数据,影响查询性能。
  • 本地化数据查询示例:
String hql = "FROM LocalizedData ld WHERE ld.id.entityId = :entityId AND ld.id.entityType = :type AND ld.id.locale = :locale";
LocalizedData data = entityManager.createQuery(hql, LocalizedData.class)
        .setParameter("entityId", productId)
        .setParameter("type", "PRODUCT_NAME")
        .setParameter("locale", "en_US")
        .getSingleResult();

四、简化方案:使用@ElementCollection

如果不需要双向关联,可直接用@ElementCollection在实体中映射本地化数据,无需单独定义LocalizedData实体:

@Entity
@Table(name = "PRODUCTS")
public class Product extends BaseEntity {
    @Column(name = "PRICE")
    private BigDecimal price;

    @ElementCollection
    @CollectionTable(
            name = "LOCALIZED_DATA",
            joinColumns = @JoinColumn(name = "ENTITY_ID"),
            uniqueConstraints = @UniqueConstraint(columnNames = {"ENTITY_ID", "ENTITY_TYPE", "LOCALE"})
    )
    @MapKeyColumns({
            @MapKeyColumn(name = "ENTITY_TYPE"),
            @MapKeyColumn(name = "LOCALE")
    })
    @Column(name = "VALUE")
    private Map<String, String> localizedData = new HashMap<>();

    // 使用示例:product.getLocalizedData().put("PRODUCT_NAME_en_US", "English Name");
}

这种方式更简洁,但灵活性稍弱,适合简单的本地化场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 18:12:39