我的开发平台是java,主要方面是web方面。我在用国际化资源文件是感觉其不够灵活,如果我要修改某此内容的话,还要进行一次服务重启。麻烦。所以我就想把国际化资源的文件放到数据库中。通过取key来获取相应的value。
以下步骤:
第一步:创建一个数据表.
CREATE TABLE KEYVALUE (
KEY VARCHAR(255) NOT NULL,
VALUE VARCHAR(2000) NOT NULL,
LANGUAGE VARCHAR(255) DEFAULT 'zh_CN' NOT NULL,
KEYREMARK VARCHAR(255),
CONSTRAINT PK_KEYVALUE PRIMARY KEY (KEY, LANGUAGE)
);
COMMENT ON TABLE KEYVALUE IS
'设定一个key_value的国际化资源表';
COMMENT ON COLUMN KEYVALUE.KEY IS
'key值,可以有占位符';
COMMENT ON COLUMN KEYVALUE.VALUE IS
'对应key值的内容值';
COMMENT ON COLUMN KEYVALUE.LANGUAGE IS
'选择语言,默认,zh_CN';
COMMENT ON COLUMN KEYVALUE.KEYREMARK IS
'key值的说明项。';
主键是采用联合主键的形式。一个是key值,另一个是语言,语言默认是"zh_CN"。
这样就避免了同一种语言环境下不会出现两个相同的name.当然KEYREMARK 是为了给自己添加备注用的。
第二步:建一个接口,此接口主要有几个方法,功能请看注释。
/**
* 操作keyValue类的接口
*
* @author <br>
* 2012-3-22 下午12:37:25<br>
* @aim
*/
public interface KeyValueDao {
/**
* 通过key获取指定的value值 2012-3-22 下午03:08:30 <br>
*
* @param key key值
* @return 对应key的value值
*/
public String getValue(String key);
/**
* 通过key和指定占位符获取值 2012-3-22 下午03:08:44 <br>
*
* @param key key值
* @param obj 占位符填充内容
* @return 对应的value值
*/
public String getValueObj(String key, Object[] obj);
/**
* 通过key和本地语言获取值 2012-3-22 下午03:09:28 <br>
*
* @param key
* key值
* @param language
* 语言
* @return 对应的value值
*/
public String getValue(String key, String language);
/**
*
* 2012-3-22 下午03:09:59 <br>
*
* @param key
* key值
* @param obj
* 占位符填充内容
* @param language
* 语言
* @return 对应的value值
*/
public String getValueObj(String key, Object[] obj, String language);
}
第三步:
编写一个实现接口的类。因为我是用hibernate来操作数据库的,所以读者可以根据自己的实际情况,
改用JDBC,但只要获取到数据库中对应key和language的value值即可。
同时还可以使用占位符。如下,数据表中的数据。
// key value language keyremark
// welcome 你好,欢迎{0},你的员工号是:{1},所在的{$depart}{$name}是{2} zh_CN 测试占位符
在测试的时候,可以设定3个值。
结果:
Object obj[] = new Object[] { "用户姓名", "用户ID", "用户部门" };
KeyValueDao kdao = (KeyValueDao) App.getBean("keyValueImpl");//此用调用的是spring容器的实现类。
//如果不用spring的话,可以直接用实现类。如果也没有用到hibernate的话,就自己用jdbc。
String val = kdao.getValueObj("welcome", obj);
你好,欢迎用户姓名,你的员工号是:用户ID,所在的部门姓名是用户部门
其中关键的代码是如何实现占位符替代和自身引用,如{$depart}将取得key值为depart的内容,这个内容不能有占位符。
import java.util.HashMap;
import java.util.ListIterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.hibernate.Query;
import landa.Dao.KeyValueDao;
import landa.POJO.KeyValue;
import landa.config.BaseDaoImpl;
/**
* 实现接口KeyValueDao的方法
*
* @author <br>
* 2012-3-22 下午03:20:21<br>
* @aim
*/
public class KeyValueImpl implements KeyValueDao {
private BaseDaoImpl baseDao = null;
/**
* 通过bean容器注入依赖的对象 2012-3-22 下午03:55:24 <br>
*
* @param bd
*/
public void setBaseDao(BaseDaoImpl baseDao) { //这是对应spring容器自动注入值。
this.baseDao = baseDao;
}
@Override
public String getValue(String key) {
String value = getHaveSecondKeyValue(key, "zh_CN");// 默认语言
if (value != null) {// 此语言对应的key值已经被访问过了,则直接返回对应的内容。
return value;
}
value = "";// 如果value==null,第一次访问此语言的key值,将value设置为空字符串。
String sql = "from KeyValue where key=:key and language=:language";
Query q = baseDao.getSession().createQuery(sql);
q.setString("key", key);
q.setString("language", "zh_CN");
ListIterator<KeyValue> lt = null;
lt = q.list().listIterator();
if (lt.hasNext()) {
value = lt.next().getValue();
}
//这是主要是在多次调用的时候,不用再访问数据库。
//如果读者不是用hibernate,而是用jdbc的话,这里的value值应该是ResultSet 对象为rs, value=rs.getString("key"),
//相应的sql="select key from keyvalue where language=?"
setHaveSecondKeyValue(key, "zh_CN", value);// 因为是第一次访问,所以要将此语言和key值放在HashMap中保存。
return value;
}
@Override
public String getValueObj(String key, Object[] obj) {
return returnValue(getValue(key), obj, null);
}
@Override
public String getValue(String key, String language) {
if (language == null) {
return getValue(key);
}
String value = getHaveSecondKeyValue(key, language);// 默认语言
if (value != null) {// 此语言对应的key值已经被访问过了,则直接返回对应的内容。
return value;
}
value = "";
String sql = "from KeyValue where key=:key and language=:language";
Query q = baseDao.getSession().createQuery(sql);
q.setString("key", key);
q.setString("language", language);
ListIterator<KeyValue> lt = null;
lt = q.list().listIterator();
if (lt.hasNext()) {
value = lt.next().getValue();
}
setHaveSecondKeyValue(key, language, value);// 因为是第一次访问,所以要将此语言和key值放在HashMap中保存。
return value;
}
@Override
public String getValueObj(String key, Object[] obj, String language) {
return returnValue(getValue(key, language), obj, language);
}
/**
* 匹配占位符的正则表达式
*/
private final static String regexNum = "\\{\\d{1,}\\}";// 匹配{数字}如{1}
private final static String regexAZ = "\\{\\$[a-z||A-Z]{1,}\\}";// 匹配{$字母}如{$depart}
// 匹配是否有数字可引用,即综合regexNum和regexAZ
private final static String regex2 = "\\{[\\d{1,}]{1}\\}|\\{\\$[a-z||A-Z]{1,}\\}";
/**
*
* 算法:<br>
* 第一步:取得当前{0}占位符的开始下标;即:start = m.start();<br>
* 第二步:截取带占位符的key对应的内容.如:key.substring(lastStart, start)<br>
* 表示方法:即上一个占位符的位置和当前占位符之间的内容,即为追回的内容。<br>
* 示例:this is {0} example about{1} regexp.<br>
* 则第一次截取的时候lastStart为0,start = m.start(),即=8;则截取的内容为substring(0,8),<br>
* 用一个StringBuilder变量添加截取的内容,同时添加替换占位符的内容。<br>
* 第二次截取的时候lastStart = start + m.group().length();<br>
* lastStart=上一次占位符位置+占位符长度{0},即:lastStart=8+3。<br>
* 一起这样循环。 <br>
*
* 2012-3-26 上午10:38:09 <br>
*
* @param valueOfKey键值
* @param obj
* 填充占位符的对象
* @return 对应key的值
*/
private String returnValue(String valueOfKey, Object[] obj, String language) {
//关键代码。将从数据库中取出对应key值的带占位符的内容进行替代。用到正则验证。
如:你好,欢迎{0},你的员工号是:{1},所在的{$depart}{$name}是{2}
//{0}用obj[0],替代,{$depart}用key值为depart的进行替代。
Pattern p2 = Pattern.compile(regex2);
Pattern pNum = Pattern.compile(regexNum);
Pattern pAZ = Pattern.compile(regexAZ);
Matcher m2 = p2.matcher(key);
StringBuilder newval = new StringBuilder();
String group = "";
int glen = 0;
int lastStart = 0;
int start = 0;
while (m2.find()) { //循环获取匹配的内容。
group = m2.group();
start = m2.start();
newval.append(valueOfKey.substring(lastStart, start));
glen = group.length();
if (pNum.matcher(group).find()) {//匹配{数字}
// 根据{num}中获取num的值,如:{2},则获取obj中obj[2]元素的值。group.substring(1,
// glen - 1))是为了获取{num}中的num
newval.append(obj[Integer.parseInt(group.substring(1, glen - 1))]);
} else if (pAZ.matcher(group).find()) {//匹配{$字母}
// 添加引用。即如:{$depart}则获取key=depart的内容。
// group.substring(2, glen - 1)为了获取{$key}中key的值
newval.append(getValue(group.substring(2, glen - 1), language));
}
lastStart = start + m2.group().length();
}
newval.append(valueOfKey.substring(start + glen));
return newval.toString();
}
private static HashMap<Object, String> HashmapObj = new HashMap<Object, String>();
/**
* 对所有读取不存在占位符的key值时,先判断是否已经存在key值中一个HashMap中,<br>
* 如果已经存在,则在HashMap中读取,如果不存在,则从数据库中读取此key值,同时存在HashMap中。<br>
*
* 通过这种方式减少对数据库的访问 <br>
* 2012-3-27 下午05:00:36 <br>
*
* @param key
* 指定的key
* @param language
* 语言
* @param value
* 对应key的值
* @return
*/
private String getHaveSecondKeyValue(String key, String language) {
// 取得key值,key值的构造高:语言+key,组成
// 已经存在对应:语言+key,组成的值,则直接返回
return HashmapObj.get(key + language);
}
/**
* 设置一个新的HashMap对应的:语言+key组成的值。 <br>
* 2012-3-27 下午05:15:46 <br>
*
* @param key
* 设置的key值之一
* @param language
* 语言
* @param value
* 对应key的内容
*/
private void setHaveSecondKeyValue(String key, String language, String value) {
HashmapObj.put(key + language, value);
}
}
第四步:结束。
其实setHaveSecondKeyValue和getHaveSecondKeyValue
这两个方法,主要是为了在程序中保存已经取得的key内容进行保存。
这样就不用多次访问数据库了。如有不懂的话,请再看一看看,如果是不知道用hibernate或spring的话,
就主要看一下方法returnValue就可以了,因为这才是最关键的。
说明:第一次在CSDN中乱写,如有错误或不足,请多指教!
本想上传代码供下载的,但不懂如何上传,求教!