我有一个Spring-boot
应用程序,它使用JPA
和Hibernate
。您可以在这个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;
}
然后,title
列中的数据将存储如下,用于en
和de
区域设置:
{"en":"Cologne","de":"Köln"}
然后,当当前区域设置为de
时,Köln
和en
设置为locale时,Cologne
在读取数据时提取!
另外,当我们存储数据时,传递的字符串以JSON格式存储在相关属性中。例如,如果区域设置为es
,并且用户传递Kolne
,那么我们必须在DB中拥有以下数据:
{"en":"Cologne","de":"Köln","es":"Kolne"}
对我来说很有趣的是,web中大多数hibernate和JPA的解决方案都是基于我们有languages
和translations
表的一种旧方法。比如这里或这里。
然而,我要寻找的是一些解决方案,如这一个,这是为Laravel建议的,并按照我解释的方式存储翻译(即在JSON对象中和在同一列中)!
我找到的唯一的解决方案,并可能在某种程度上相关(不是100%)是这一个,然而,当我试图测试它时,它不工作,它似乎不再支持!
发布于 2020-09-16 16:12:46
几周后,我可以再次回到我的olingo2 odata服务器项目。
我想做的事情比我预期的要简单。
弗拉德·米哈西建议的解决方案是好的,我很感激,但是正如我在问题中提到的,我需要一个解决方案,它可以在Olingo JPA库之外工作!但是,建议的解决方案存在一个问题,Olingo无法处理JsonBinaryType
。
这里是我的建议,实现国际化,除了Olingo JPA。
假设我们有这样一个BasicModel.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);
}
}
发布于 2020-06-18 08:36:28
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");
或者,您可以将当前区域设置存储在名为ThreadLocal
的类LocaleUtil
中。
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();
有关使用Hibernate类型将测试用例
Map
作为JSON
列类型持久化的更多详细信息,请参阅GitHub上的JSON
。
https://stackoverflow.com/questions/62409871
复制相似问题