多语言 JPA 实体映射
Multilingual JPA entity mapping
使用这个例子:
CREATE TABLE IF NOT EXISTS COUNTRY (
COUNTRY_CODE VARCHAR(3) NOT NULL,
DIALLING_CODE VARCHAR(5) NOT NULL,
CREATION_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CREATED_BY VARCHAR(20) NOT NULL,
LAST_UPDATE_TIMESTAMP TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
LAST_UPDATED_BY VARCHAR(20),
DELETION_TIMESTAMP TIMESTAMP NULL,
DELETED_BY VARCHAR(20),
PRIMARY KEY(NUMERIC_CODE),
UNIQUE(ALPHA2_CODE),
UNIQUE(ALPHA3_CODE),
UNIQUE(DIALLING_CODE),
FOREIGN KEY(CREATED_BY) REFERENCES USER(USER_NAME),
FOREIGN KEY(LAST_UPDATED_BY) REFERENCES USER(USER_NAME),
FOREIGN KEY (DELETED_BY) REFERENCES USER(USER_NAME)
) CHARACTER SET utf8;
CREATE TABLE IF NOT EXISTS COUNTRY_NAME (
COUNTRY_CODE VARCHAR(3) NOT NULL,
LANGUAGE_CODE VARCHAR(2) NOT NULL,
NAME VARCHAR(20) NOT NULL,
CREATION_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CREATED_BY VARCHAR(20) NOT NULL,
LAST_UPDATE_TIMESTAMP TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
LAST_UPDATED_BY VARCHAR(20),
PRIMARY KEY (COUNTRY_CODE, LANGUAGE_CODE),
FOREIGN KEY (COUNTRY_CODE) REFERENCES COUNTRY (COUNTRY_CODE),
FOREIGN KEY (LANGUAGE_CODE) REFERENCES LANGUAGE (ALPHA2_CODE),
FOREIGN KEY (CREATED_BY) REFERENCES USER(USER_NAME),
FOREIGN KEY (LAST_UPDATED_BY) REFERENCES USER(USER_NAME)
) CHARACTER SET utf8;
国家/地区的 JPA 表示如下:
@XmlRootElement
@Entity
@Table(name="COUNTRY")
@Access(AccessType.FIELD)
@Cacheable
public class Country extends AbstractSoftDeleteAuditableEntity<String> implements za.co.sindi.persistence.entity.Entity<String>, Serializable {
private static final long serialVersionUID = -4019436514961967327L;
@Id
@Column(name="COUNTRY_CODE", length=3, nullable=false)
@Size(min=3, max=3)
@Pattern(regexp="^\d{3}$")
private String id;
@Column(name="DIALLING_CODE", length=5, nullable=false)
@Pattern(regexp="^\+[0-9]{2,5}$")
private String diallingCode;
@OneToMany(cascade= CascadeType.ALL, mappedBy="country")
private List<CountryName> names;
/* (non-Javadoc)
* @see za.co.sindi.entity.IDBasedEntity#getId()
*/
public String getId() {
// TODO Auto-generated method stub
return id;
}
/* (non-Javadoc)
* @see za.co.sindi.entity.IDBasedEntity#setId(java.io.Serializable)
*/
public void setId(String id) {
// TODO Auto-generated method stub
this.id = id;
}
/**
* @return the diallingCode
*/
public String getDiallingCode() {
return diallingCode;
}
/**
* @param diallingCode the diallingCode to set
*/
public void setDiallingCode(String diallingCode) {
this.diallingCode = diallingCode;
}
/**
* @return the names
*/
public List<CountryName> getNames() {
return names;
}
/**
* @param names the names to set
*/
public void setNames(List<CountryName> names) {
this.names = names;
}
}
使用上述模型,我将不得不迭代 names
属性以找到匹配语言代码的 CountryName
。
是否有更好的方法根据语言代码检索 Country
和 CountryName
?如果没有,我怎样才能实现一种更简单的方法,使我能够将带有语言代码的 Country
信息提取到如下实体:
public class Country implements Serializable {
private static final long serialVersionUID = -4019436514961967327L;
@Id
@Column(name="COUNTRY_CODE", length=3, nullable=false)
@Size(min=3, max=3)
@Pattern(regexp="^\d{3}$")
private String id;
@Column(name="DIALLING_CODE", length=5, nullable=false)
@Pattern(regexp="^\+[0-9]{2,5}$")
private String diallingCode;
private String name;
private Language language;
//Getters and setters will be auto generated here....
}
这样就可以实现:
Country country = countryDAO.find(countryCode);
System.out.println(country.getName());
正如我在评论中所说,我认为最好的解决方案是为 CountryName
使用单独的查询(和 DAO),因为当您只需要一个指定的国家名称时,它会避免加载不必要的国家名称countryCode
.
一种选择是使用 @Transient java.util.Map
字段,该字段将加载国家名称,countryCode
作为映射键,
@Transient
private Map<String, CountryName> countryNameMap;
...
public CountryName getName(String countryCode) {
if (countryNameMap == null) {
countryNameMap = loadNamesIntoMap(); // a loop with countryNameMap.put(countryName.getCountryCode(), countryName)
}
return countryNameMap.get(countryCode);
}
另一个有趣的选择是实际使用 java.util.Map
来表示 Country
和 CountryName
之间的关系
@OneToMany(mappedBy="country")
@MapKey(name="countryCode")
private Map<String, CountryName> names;
然后您将使用 country.getNames().get(countryCode)
访问该名称。
使用这个例子:
CREATE TABLE IF NOT EXISTS COUNTRY (
COUNTRY_CODE VARCHAR(3) NOT NULL,
DIALLING_CODE VARCHAR(5) NOT NULL,
CREATION_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CREATED_BY VARCHAR(20) NOT NULL,
LAST_UPDATE_TIMESTAMP TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
LAST_UPDATED_BY VARCHAR(20),
DELETION_TIMESTAMP TIMESTAMP NULL,
DELETED_BY VARCHAR(20),
PRIMARY KEY(NUMERIC_CODE),
UNIQUE(ALPHA2_CODE),
UNIQUE(ALPHA3_CODE),
UNIQUE(DIALLING_CODE),
FOREIGN KEY(CREATED_BY) REFERENCES USER(USER_NAME),
FOREIGN KEY(LAST_UPDATED_BY) REFERENCES USER(USER_NAME),
FOREIGN KEY (DELETED_BY) REFERENCES USER(USER_NAME)
) CHARACTER SET utf8;
CREATE TABLE IF NOT EXISTS COUNTRY_NAME (
COUNTRY_CODE VARCHAR(3) NOT NULL,
LANGUAGE_CODE VARCHAR(2) NOT NULL,
NAME VARCHAR(20) NOT NULL,
CREATION_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CREATED_BY VARCHAR(20) NOT NULL,
LAST_UPDATE_TIMESTAMP TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
LAST_UPDATED_BY VARCHAR(20),
PRIMARY KEY (COUNTRY_CODE, LANGUAGE_CODE),
FOREIGN KEY (COUNTRY_CODE) REFERENCES COUNTRY (COUNTRY_CODE),
FOREIGN KEY (LANGUAGE_CODE) REFERENCES LANGUAGE (ALPHA2_CODE),
FOREIGN KEY (CREATED_BY) REFERENCES USER(USER_NAME),
FOREIGN KEY (LAST_UPDATED_BY) REFERENCES USER(USER_NAME)
) CHARACTER SET utf8;
国家/地区的 JPA 表示如下:
@XmlRootElement
@Entity
@Table(name="COUNTRY")
@Access(AccessType.FIELD)
@Cacheable
public class Country extends AbstractSoftDeleteAuditableEntity<String> implements za.co.sindi.persistence.entity.Entity<String>, Serializable {
private static final long serialVersionUID = -4019436514961967327L;
@Id
@Column(name="COUNTRY_CODE", length=3, nullable=false)
@Size(min=3, max=3)
@Pattern(regexp="^\d{3}$")
private String id;
@Column(name="DIALLING_CODE", length=5, nullable=false)
@Pattern(regexp="^\+[0-9]{2,5}$")
private String diallingCode;
@OneToMany(cascade= CascadeType.ALL, mappedBy="country")
private List<CountryName> names;
/* (non-Javadoc)
* @see za.co.sindi.entity.IDBasedEntity#getId()
*/
public String getId() {
// TODO Auto-generated method stub
return id;
}
/* (non-Javadoc)
* @see za.co.sindi.entity.IDBasedEntity#setId(java.io.Serializable)
*/
public void setId(String id) {
// TODO Auto-generated method stub
this.id = id;
}
/**
* @return the diallingCode
*/
public String getDiallingCode() {
return diallingCode;
}
/**
* @param diallingCode the diallingCode to set
*/
public void setDiallingCode(String diallingCode) {
this.diallingCode = diallingCode;
}
/**
* @return the names
*/
public List<CountryName> getNames() {
return names;
}
/**
* @param names the names to set
*/
public void setNames(List<CountryName> names) {
this.names = names;
}
}
使用上述模型,我将不得不迭代 names
属性以找到匹配语言代码的 CountryName
。
是否有更好的方法根据语言代码检索 Country
和 CountryName
?如果没有,我怎样才能实现一种更简单的方法,使我能够将带有语言代码的 Country
信息提取到如下实体:
public class Country implements Serializable {
private static final long serialVersionUID = -4019436514961967327L;
@Id
@Column(name="COUNTRY_CODE", length=3, nullable=false)
@Size(min=3, max=3)
@Pattern(regexp="^\d{3}$")
private String id;
@Column(name="DIALLING_CODE", length=5, nullable=false)
@Pattern(regexp="^\+[0-9]{2,5}$")
private String diallingCode;
private String name;
private Language language;
//Getters and setters will be auto generated here....
}
这样就可以实现:
Country country = countryDAO.find(countryCode);
System.out.println(country.getName());
正如我在评论中所说,我认为最好的解决方案是为 CountryName
使用单独的查询(和 DAO),因为当您只需要一个指定的国家名称时,它会避免加载不必要的国家名称countryCode
.
一种选择是使用 @Transient java.util.Map
字段,该字段将加载国家名称,countryCode
作为映射键,
@Transient
private Map<String, CountryName> countryNameMap;
...
public CountryName getName(String countryCode) {
if (countryNameMap == null) {
countryNameMap = loadNamesIntoMap(); // a loop with countryNameMap.put(countryName.getCountryCode(), countryName)
}
return countryNameMap.get(countryCode);
}
另一个有趣的选择是实际使用 java.util.Map
来表示 Country
和 CountryName
@OneToMany(mappedBy="country")
@MapKey(name="countryCode")
private Map<String, CountryName> names;
然后您将使用 country.getNames().get(countryCode)
访问该名称。