当前位置: 首页 > 知识库问答 >
问题:

如何将I18n实现为JPA或Hibernate实体的JSON对象?

竺和洽
2023-03-14

我有一个使用JPAHibernate的Spring Boot应用程序。您可以在这个GitHub存储库中找到整个代码。

我的问题是,如何在没有任何外键的情况下,通过使用JSON结构向特定列添加国际化功能?

例如,我想定义一个JPA实体,如下所示:

@Entity
class Book {

    @Id
    private int id;

    private Author author;

    @I18n  //<- this annotation is something that I am looking for 
    private String title;

}

然后,对于ende地区,title列中的数据将按如下方式存储:

{"en":"Cologne","de":"Köln"}

然后,当当前区域设置为deKöln时,当en设置为区域设置时,则Cologne在读取数据时获取数据!

此外,当我们存储数据时,传递的字符串以JSON格式存储在相关属性中。例如,如果区域设置设置为es,并且用户通过Kolne,则数据库中必须有以下数据:

{"en":"Cologne","de":"Köln","es":"Kolne"}

对我来说,有趣的是,hibernate和JPA在web上的大多数解决方案都基于一种旧方法,即我们有语言翻译表。比如这里或者这里。

然而,我正在寻找的是一些类似于此的解决方案,这是为Laravel建议的,并以我解释的方式(即在JSON对象和同一列中)存储翻译!

我找到的唯一解决方案可能与此相关(不是100%),但当我尝试测试它时,它不起作用,似乎不再受支持!

共有2个答案

杨星纬
2023-03-14

几周后,我可以再次回到我的olingo2 odata服务器项目。

我想做的比我预期的要简单。

Vlad Mihalcea提出的解决方案很好,我很感激,但是正如我在问题中提到的,我需要一个在Olingo JPA库旁边工作的解决方案!然而,建议的解决方案存在这样一个问题:Olingo无法处理JsonBinaryType

以下是我对Olingo JPA实施国际化的建议。

假设我们有一个基本模型。java像这样:

import java.io.Serializable;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;

import java.io.IOException;

public abstract class BaseModel implements Serializable {
    private static final long serialVersionUID = 1L;
    private static ObjectMapper mapper = new ObjectMapper();

    @SuppressWarnings("unchecked")
    protected static Map<String, String> jsonToMap(String json) {
        Map<String, String> map = new HashMap<>();
        try {
            // convert JSON string to Map
            if (json != null) {
                map = (Map<String, String>) mapper.readValue(json, Map.class);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }

    protected static String mapToJson(Map<String, String> map) {
        String json = "";
        try {
            // convert map to JSON string
            json = mapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return json;
    }

    protected static String getLang() {
        Locale currentLocale = LocaleContextHolder.getLocale();
        String[] localeStrings = (currentLocale.getLanguage().split("[-_]+"));
        return localeStrings.length > 0 ? localeStrings[0] : "en";
    }
}

此类为我们提供了一种将JSON字符串转换为Map的机制,反之亦然。

转换器的代码已经从这里改编。为了使用这段代码,我们需要添加这个maven依赖项:

  <!-- Convert JSON string to Map -->        
  <dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-databind</artifactId>
  </dependency>

最后,在JPA实体模型中,每当我们希望字符串属性具有i18n时,我们只需要稍微修改setter和getter方法。例如:


import javax.persistence.*;

import java.util.Map;
import java.util.Set;

/**
 * The persistent class for the actions database table.
 * 
 */
@Entity
@Table(name = "actions")
@NamedQuery(name = "Action.findAll", query = "SELECT a FROM Action a")
public class Action extends BaseModel {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "id", unique = true, nullable = false, length = 255)
    private String id;

    @Column(nullable = false, length = 255)
    private String name;

    public Action() {
    }

    public String getId() {
        return this.id;
    }

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

    public String getName() {
        Map<String, String> map = jsonToMap(this.name);
        return map.get(getLang());
    }

    public void setName(String name) {
        Map<String, String> map = jsonToMap(this.name);
        map.put(getLang(), name);
        this.name = mapToJson(map);
    }

}
利俊迈
2023-03-14

首先,需要添加Hibernate类型的项目依赖项。

之后,您可以使用HStoreJSONB列来存储特定于定位的标题:

@Entity
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
class Book {

    @Id
    private int id;

    private Author author;

    @Type(type = "jsonb")
    @Column(name = "localized_titles", columnDefinition = "jsonb")
    private Map<String, String> localizedTitles = new HashMap<>();

    public String getLocalizedTitle(String locale) {
        return localizedTitles.get(locale);
    }

    public String getLocalizedTitle() {
        return localizedTitles.get(LocaleUtil.getDefaultLocale());
    }
}

因此,您可以调用getLocalizedTitle并传递当前区域设置以获取当前的本地化标题。

Book book = entityManager.find(Book.class, bookId);
String title = book.getLocalizedTitle("en");

或者,您可以将当前区域设置存储在一个名为LocaleUtil的类中的ThreadPlace中:

public class LocaleUtil {
  
    private static final ThreadLocal<String> LOCALE_HOLDER =
        new ThreadLocal<>();

    public static String getLocale() {
        return LOCALE_HOLDER.get();
    }
 
    public static void setLocale(String locale) {
        LOCALE_HOLDER.set(locale);
    }
 
    public static void reset() {
        LOCALE_HOLDER.remove();
    }
}

并按如下方式存储当前区域设置:

LocaleUtil.setLocale("en");

然后,只需调用不带参数的getLocalizedTitle方法:

Book book = entityManager.find(Book.class, bookId);
String title = book.getLocalizedTitle();

查看GitHub上的这个PostgreSQLJsonMapTest测试用例,了解有关使用Hibernate类型将JavaMap持久化为JSON列类型的更多详细信息。

 类似资料:
  • 问题内容: 我将开始一个使用Spring和Hibernate管理的REST应用程序项目。 我知道Spring允许您从HTTP Request(带有注释)中获取Java对象。如果此Java对象也是Hibernate实体,是否有冲突?嵌套对象是否起作用(如关系)? 问题答案: 我们正在使用这种方法来简化设计并摆脱许多dto(我们滥用它们太多了)。基本上,它对我们有用。 但是,在我们的REST模型中,我

  • 问题内容: 在Hibernate期间,我正在加载一些对象,由于延迟加载,其中一些对象已作为代理加载。一切正常,我不想关闭延迟加载。 但是稍后我需要通过RPC将一些对象(实际上是一个对象)发送到GWT客户端。碰巧这个具体对象是代理。所以我需要将其变成一个真实的对象。我在Hibernate中找不到类似“实现”的方法。 我如何才能将某些对象从代理变为真实,从而知道它们的类和ID? 目前,我看到的唯一解决

  • 我想知道在Spring项目中更新JPA实体的最佳实践是什么-更新原始实体还是创建新实体?我看到了这两种方法: 使用原始-在原始实体中实现必要的字段,并将更新后的实体保存回存储库 使用复制-手动创建实体的新实例,将原始实体中的所有字段(更新的字段)设置为新实体,并将实体保存回存储库 您使用/推荐什么方法?为什么呢?

  • 问题内容: 当业务层创建一个新的实体时,该实体在逻辑上表示应该更新的现有实体的实例(例如,它们共享相同的业务密钥),这是合并不良做法的方法吗? 我问是因为在分离的实体上显式设置ID对我来说很奇怪,但是即使User实体的equals和hashcode方法得到了适当实现,在这里设置ID是确保合并发生的唯一方法。 有更好的做法吗? 此方法是否有特定的缺点,以后会困扰我? 谢谢参观! 问题答案: 该代码将

  • 问题内容: 我使用Hibernate 4和Spring 3。 我有两个实体。 图书实体 和作者实体 和JSON取决于pom.xml 我的根上下文在这里- … servlet-context.xml 控制器。 在我的DAO中找到findAll: 在调试中,我看到该方法返回2条记录,但是Spring无法将结果转换为JSON并返回406 HTTP错误。怎么了? 我附上我在调试中看到的图像。- http:

  • 我有一个相当大的系统,它的规范是由子实体上的几个方法构建的。所以我有一个用户,他有一个宠物木偶,就像这个问题一样。我的关系是双向的,所以Pet在用户上也有多对一的关系,我正在努力将子实体上的规范转换为应用于父实体。 我查找的大多数问题都显示了如何在不同的实体上应用规范,或者在规范执行后获得不同的实体。这不是我想要的。 我试着写一个这样的方法: 但我不知道如何编写它(我尝试过使用Join,但没能“告