我目前正在大型数据库的某些表中启用UTF-8字符。这些表已经是MS-SQL类型NVARCHAR。另外,我也有几个使用VARCHAR的字段。
Hibernate与JDBC驱动程序的交互存在一个众所周知的问题。简而言之,Hibernate / JDBC生成的SQL会将所有字符串作为Unicode传递,而不管底层的SQL类型如何。将数据库中的非unicode(varchar)字段与Unicode输入字符串进行比较时,该列的索引与编码不匹配,因此将执行全表扫描。在JDBC驱动程序(JTDS和MS版本)中,都有一个参数将Unicode字符串作为ASCII传递,但这是一个全有或全无的命题,不允许将国际字符输入到数据库中。
我在这个问题上见过的大多数帖子都提出了以下两种解决方案之一-1)将数据库中的所有内容更改为NVARCHAR或2)设置sendStringParametersAsUnicode=false,然后我的问题是这-是否存在用于VARCHAR的已知解决方案和NVARCHAR在一起玩的很好吗?由于下游依赖性和其他外部问题,将所有内容更改为NVARCHAR对我的环境来说是一个巨大的 问题。
我决定尝试这种方法,使其在不接触数据库的情况下也 _可能_起作用。为此,我为NVARCHAR字段创建了自定义类型。这需要JDBC4驱动程序(使用Microsoft的驱动程序)和Hibernate3.6.0。sendStringParametersAsUnicode为false。
这是方法,我仍在验证它的正确性-来自经验超过我的人的任何评论都值得欢迎
添加新的方言以支持新的数据类型
public class SQLAddNVarCharDialect extends SQLServerDialect { public SQLAddNVarCharDialect(){ super(); registerColumnType( Types.NVARCHAR, 8000, "nvarchar($1)" ); registerColumnType( Types.NVARCHAR, "nvarchar(255)" ); } }
添加新的类型。注意setNString在nullSafeSet
setNString
nullSafeSet
public class NStringUserType implements UserType { @Override public Object assemble(Serializable arg0, Object owner) throws HibernateException { return deepCopy(arg0); } @Override public Object deepCopy(Object arg0) throws HibernateException { if(arg0==null) return null; return arg0.toString(); } @Override public Serializable disassemble(Object arg0) throws HibernateException { return (Serializable)deepCopy(arg0); } @Override public boolean equals(Object arg0, Object arg1) throws HibernateException { if(arg0 == null ) return arg1 == null; return arg0.equals(arg1); } @Override public int hashCode(Object arg0) throws HibernateException { return arg0.hashCode(); } @Override public boolean isMutable() { return false; } @Override public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException { if(value == null) st.setNull(index,Types.NVARCHAR); else st.setNString(index, value.toString()); } @Override public Object replace(Object arg0, Object target, Object owner) throws HibernateException { return deepCopy(arg0); } @Override public Class returnedClass() { return String.class; } @Override public int[] sqlTypes() { return new int[]{Types.NVARCHAR}; } @Override public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException { String result = resultSet.getString(names[0]); return result == null || result.trim().length() == 0 ? null : result; } }
更新所有NVARCHAR字段的映射
<property name="firstName" type="NStringUserType"> <column name="firstName" length="40" not-null="false" /> </property>
之前的原始SQL(带有sendUnicode .. = true):
exec sp_prepexec @p1 output,N'@P0 nvarchar(4000),@P1 datetime,@P2 varchar(8000),@P3 nvarchar(4000),@P4 nvarchar(4000),@P5 nvarchar(4000),@P6 nvarchar(4000)... ,N'update Account set ... where AccountId=@P35
之后:
exec sp_prepexec @p1 output,N'@P0 varchar(8000),@P1 .... @P6 nvarchar(4000),@P7 ... ,N'update Account set ... Validated=@P4, prefix=@P5, firstName=@P6 ... where AccountId=@P35
似乎对“ SELECT ..”工作类似。”