我有一个使用JPA
和Hibernate
的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;
}
然后,对于en
和de
地区,title
列中的数据将按如下方式存储:
{"en":"Cologne","de":"Köln"}
然后,当当前区域设置为de
Köln时,当en
设置为区域设置时,则Cologne
在读取数据时获取数据!
此外,当我们存储数据时,传递的字符串以JSON格式存储在相关属性中。例如,如果区域设置设置为es
,并且用户通过Kolne
,则数据库中必须有以下数据:
{"en":"Cologne","de":"Köln","es":"Kolne"}
对我来说,有趣的是,hibernate和JPA在web上的大多数解决方案都基于一种旧方法,即我们有语言
和翻译
表。比如这里或者这里。
然而,我正在寻找的是一些类似于此的解决方案,这是为Laravel建议的,并以我解释的方式(即在JSON对象和同一列中)存储翻译!
我找到的唯一解决方案可能与此相关(不是100%),但当我尝试测试它时,它不起作用,似乎不再受支持!
几周后,我可以再次回到我的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);
}
}
首先,需要添加Hibernate类型的项目依赖项。
之后,您可以使用HStore
或JSONB
列来存储特定于定位的标题:
@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,但没能“告