数据库字符国际化是大家提问最多的问题例如MySQL数据库大家可能在JDBCURL添加useUnicode=true&CharacterEncoding=GBK作为中文支持的基本条件但这有时破坏了数据的完整性如果某些人粗心大意就会导致数据编码错误产生乱码因此我们需要一些手段在程序内部进行编码处理人们一般通过在应用上使用 String(bytes:byte[] enc:String)/StringgetBytes(enc:String)进行字符串编解码这样做虽然易懂但是如果遇到大字段表格手动编码时费时费力
我的方法通过研究JDK类库可以感觉到多层处理机制在数据处理上的优越性我们完全有可能在数据库上建立一个中间层用于字符的国际化处理我就是这么做的仔细研究一下JDBC操作数据库出现字符编码问题的根源很容易发现多数情况是ResultSet的几个String方法在作怪因此我们就完全可以编写一个ResultSet中间层进行国际化处理源码如下
public class InResultSet implements ResultSet{ private String encoding; private ResultSet rs; public InResultSet(ResultSet rs String encoding) throws javaioUnsupportedEncodingException{ //检查该编码名称是否被系统支持 getBytes(encoding); thisrs = rs; thisencoding = encoding; } … … //以下几个方法是进行String字符串的重编码 public String getString(int index) throws SQLException{ String data = null; try{ data = new String(rsgetBytes(index) encoding); }catch(javaioUnsupportedEncodingException uee){} } public String getString(Stirng field) throws SQLException{ String data = null; try{ data = new String(rsgetBytes(field) encoding); }catch(javaioUnsupportedEncodingException uee){} } public void updateString(int index String value) throws SQLException{ try{ rsupdateBytes(index valuegetBytes(encoding)); }catch(javaioUnsupportedEncodingException uee){} } public void updateString(String field String value) throws SQLException{ try{ rsupdateBytes(field valuegetBytes(encoding)); }catch(javaioUnsupportedEncodingException uee){} } … …}
可以看出 所有的String操作都使用特定编码的字节数组进行存取这样通过定义encoding的值实现数据库存取数据编码的一致性且encoding完全可以通过在配置信息中动态定义
同时上面的程序又可以解决一些固有的字符串处理问题例如控制符如\r\n导入到数据库中很有可能被解析为\\r\\n使其不能换行通过字节数组操作就可以解决这个问题这样像文章固有格式就可以完整地保留下来而不需要进行额外转换操作
结论通过多层处理机制使用中间层对数据库数据进行层层处理可使处理环节之间形成松耦合关系从而可以进行有效的控制
下面给一个使用动态代理进行字符控制的代码(原创):
package comyipsiloncrocodiledatabase;import javasqlResultSet;import javalangreflectInvocationHandler;import javalangreflectMethod;import javaioUnsupportedEncodingException;/** * 作者 yipsilon * 如要转载 请通知作者 */public class InResultSetHandler implements InvocationHandler{ private ResultSet rs; private String encoding; public InResultSetHandler(ResultSet rs String encoding) throws UnsupportedEncodingException{ thisrs = rs; getBytes(encoding); thisencoding = encoding; } public Object invoke(Object proxy Method method Object[] args) throws Throwable{ String methodName = methodgetName(); if(methodNameequals(getString)){ Object obj = args[]; if(obj instanceof Integer){ return decodeString(rsgetBytes(((Integer)obj)intValue()) encoding); }else{ return decodeString(rsgetBytes((String)obj) encoding); } }else if(methodNameequals(updateString)){ Object obj = args[]; if(obj instanceof Integer){ rsupdateBytes(((Integer)obj)intValue() encodeString((String)args[] encoding)); }else{ rsupdateBytes((String)obj encodeString((String)args[] encoding)); } return null; } return methodinvoke(rs args); } private String decodeString(byte[] bytes String enc){ try{ return new String(bytes enc); } catch(UnsupportedEncodingException uee){ return new String(bytes); } } private byte[] encodeString(String str String enc){ try{ return strgetBytes(enc); } catch(UnsupportedEncodingException uee){ return strgetBytes(); } }}
使用时调用:
ResultSet rs = ; //原始的ResultSet结果集String encoding = GBK; //字符编码(ResultSet)ProxynewProxyInstance(rsgetClass()getClassLoader() rsgetClass()getInterfaces() new InResultSetHandler(rs encoding));