`
dreamoftch
  • 浏览: 486490 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

使用hibernate中的UserType解决乱码问题

 
阅读更多
转自:http://hi.baidu.com/gacmotor/item/3ea2e62952c99acbddf69aea

总的思想还是一样:
页面编码都是utf-8,数据库是xxx,那就new String("你好".getBytes("utf-8"),"xxx")

然后读取出来的时候new String("...".getBytes("xxx"),"utf-8")
 

hibernate 访问 oracle 乱码 oracle的字符集是WE8ISO8859P1,由于历史原因,不可修改。已经修改本地 NLS_LANG,因此使用PL/SQL developer可以正常访问。 但是 hibernate使用thin方式连接数据库,中文乱码。 为了使页面可以正常显示,在取数据时在form的get方法中使用 
String new_str = new String(old_str.getBytes("iso-8859-1"));

 得到的new_str可以正常显示中文。 但是这样增加了冗余代码,而且使用好多第三方组件时,由于无法控制转码细节,导致乱码。 在互联网上搜索解决方案,搜到的大多是一个方法,加入filter,使用org.springframework.web.filter.CharacterEncodingFilter 但是对我这个情况完全无效,存取数据都是乱码,存到数据库里的数用客户端看也是乱码。 我觉得CharacterEncodingFilter应该是在oracle的字符集正确设置的情况下,比如使用ZHS16GBK,解决乱码的问题。 现在请教各位高手有没有遇到过同样的问题,如何解决这种情况下的乱码。是否需要写一个filter或者是hibernate直接可以对oracle字符集进行配置。 问题已经解决,说一下我的解决方案吧。使用hibernate的UserType 在hbm.xml中配置,

比如 <property name="roleName" type="com.xtmcc.framework.dao.GBKString"> <column name="ROLE_NAME" length="50" />
 </property> 

自定义GBKString 类
package two;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import oracle.jdbc.driver.OracleTypes;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;

public class a1 implements UserType {
	public a1() {
		super();
	}

	public int[] sqlTypes() {
		return new int[] { OracleTypes.VARCHAR };
	}

	public Class returnedClass() {
		return String.class;
	}

	public boolean equals(Object x, Object y) throws HibernateException {
		return (x == y) || (x != null && y != null && (x.equals(y)));
	}

	public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
			throws HibernateException, SQLException {
		String val = rs.getString(names[0]);
		if (null == val) {
			return null;
		} else {
			try {
				val = new String(val.getBytes("iso-8859-1"), "GBK");
			} catch (UnsupportedEncodingException e) {
				throw new HibernateException(e.getMessage());
			}
			return val;
		}
	}

	public void nullSafeSet(PreparedStatement st, Object value, int index)
			throws HibernateException, SQLException {
		if (value == null) {
			st.setNull(index, OracleTypes.VARCHAR);
		} else {
			String val = (String) value;
			try {
				val = new String(val.getBytes("GBK"), "ISO_8859_1");
			} catch (UnsupportedEncodingException e) {
				throw new HibernateException(e.getMessage());
			}
			st.setObject(index, val, OracleTypes.VARCHAR);
		}
	}

	public Object deepCopy(Object value) throws HibernateException {
		if (value == null)
			return null;
		return new String((String) value);
	}

	public boolean isMutable() {
		return false;
	}

	public Object assemble(Serializable arg0, Object arg1)
			throws HibernateException {
		// TODO Auto-generated method stub
		return null;
	}

	public Serializable disassemble(Object arg0) throws HibernateException {
		// TODO Auto-generated method stub
		return null;
	}

	public int hashCode(Object arg0) throws HibernateException {
		return HashCodeBuilder.reflectionHashCode(this);
	}

	public Object replace(Object arg0, Object arg1, Object arg2)
			throws HibernateException {
		// TODO Auto-generated method stub//
		return null;
	}
}
 
分享到:
评论
8 楼 小天蝎 2014-02-19  
xush_319 写道
问题解决 下面贴出我的代码

谢谢xush_319 ,我也遇到与你一样的问题,主要是与未使用那个deepCopy引起中文值的丢失,多谢!
7 楼 dreamoftch 2013-04-17  
xush_319 写道
public class GBKString implements UserType
{

public GBKString()
{
super();
}

/**   
  * 返回UserType所映射字段的SQL类型(java.sql.Types)   
  * 返回类型为int[],其中包含了映射个字段的SQL类型代码   
  * (UserType可以映射到一个或者多个字段)   
  * @return   
  */
@Override
public int[] sqlTypes()
{
return new int[] { Types.VARCHAR}; 
}

/**   
  * UserType.nullSafeGet()所返回的自定义数据类型   
  * @return   
  */
@Override
public Class<?> returnedClass()
{
return String.class; 
}

    /**   
  * 自定义数据类型的比对方法   
  * 此方法将用作脏数据检查,参数x、y分别为数据的两个副本   
  * 如果equals方法返回false,则Hibernate将认为数据发生变化,并将变化更新到数据库表中   
  * @param x   
  * @param y   
  * @return   
  * @throws HibernateException   
  */
@Override
public boolean equals(Object x, Object y) throws HibernateException
{
return (x == y) || (x != null && y != null && (x.equals(y))); 
}

@Override
public int hashCode(Object x) throws HibernateException
{
return HashCodeBuilder.reflectionHashCode(this); 
}

/**   
  * 从JDBC ResultSet读取数据,将其转换为自定义类型后返回   
  * (此方法要求对克能出现null值进行处理)   
  * names中包含了当前自定义类型的映射字段名称   
  * @param rs   
  * @param names   
  * @param owner   
  * @return   
  * @throws HibernateException   
  * @throws SQLException   
  */ 
@Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException
{

String val = rs.getString(names[0]); 
        if (val != null) { 
        try { 
                val = new String(val.getBytes("ISO-8859-1"), "GBK"); 
            } catch (UnsupportedEncodingException e) { 
                throw new HibernateException(e.getMessage()); 
            } 
            return val; 
           
        } else { 
        return null; 
        } 
}

/**   
  * 本方法将在Hibernate进行数据保存时被调用   
  * 我们可以通过PreparedStateme将自定义数据写入到对应的数据库表字段   
  * 这个方法将在数据保存时使用。本方法可以使用PreparedStatement将数据写入对应的数据库字段中。 
  * 其中的value表示的是要写入的值。index表示的是在statement的参数中的index.
  * @param st   
  * @param value   
  * @param index   
  * @throws HibernateException   
  * @throws SQLException   
  */ 
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException
{
if (value != null) { 
String val = (String) value; 
            try { 
                val = new String(val.getBytes("GBK"), "ISO-8859-1"); 
            } catch (UnsupportedEncodingException e) { 
                throw new HibernateException(e.getMessage()); 
            } 
            st.setObject(index, val, Types.VARCHAR); 
        } else { 
        st.setNull(index, Types.VARCHAR); 
        } 
}

/**   
  * 提供自定义类型的完全复制方法   
  * 本方法将用构造返回对象   
  * 当nullSafeGet方法调用之后,我们获得了自定义数据对象,在向用户返回自定义数据之前,   
  * deepCopy方法将被调用,它将根据自定义数据对象构造一个完全拷贝,并将此拷贝返回给用户   
  * 此时我们就得到了自定义数据对象的两个版本,第一个是从数据库读出的原始版本,其二是我们通过   
  * deepCopy方法构造的复制版本,原始的版本将有Hibernate维护,复制版由用户使用。原始版本用作   
  * 稍后的脏数据检查依据;Hibernate将在脏数据检查过程中将两个版本的数据进行对比(通过调用   
  * equals方法),如果数据发生了变化(equals方法返回false),则执行对应的持久化操作   
  *   
  * @param value   
  * @return   
  * @throws HibernateException   
  */ 
@Override
public Object deepCopy(Object value) throws HibernateException
{
if (value == null) 
            return null; 
        return new String((String) value); 
//        return (String) value; 
}

/**  
  * 本类型实例是否可变  
  * @return  
  */
@Override
public boolean isMutable()
{
return false;
}

@Override
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) deepCopy(value);
}

@Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException
{
return deepCopy(cached);
}

@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException
{
return deepCopy(original);
}

}

是后面那三个方法的原因?
@Override
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) deepCopy(value);
}

@Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException
{
return deepCopy(cached);
}

@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException
{
return deepCopy(original);
}
这三个方法肿么会有影响(⊙o⊙)…。。。不过问题解决了就行,O(∩_∩)O哈哈~
6 楼 xush_319 2013-04-16  
public class GBKString implements UserType
{

public GBKString()
{
super();
}

/**   
  * 返回UserType所映射字段的SQL类型(java.sql.Types)   
  * 返回类型为int[],其中包含了映射个字段的SQL类型代码   
  * (UserType可以映射到一个或者多个字段)   
  * @return   
  */
@Override
public int[] sqlTypes()
{
return new int[] { Types.VARCHAR}; 
}

/**   
  * UserType.nullSafeGet()所返回的自定义数据类型   
  * @return   
  */
@Override
public Class<?> returnedClass()
{
return String.class; 
}

    /**   
  * 自定义数据类型的比对方法   
  * 此方法将用作脏数据检查,参数x、y分别为数据的两个副本   
  * 如果equals方法返回false,则Hibernate将认为数据发生变化,并将变化更新到数据库表中   
  * @param x   
  * @param y   
  * @return   
  * @throws HibernateException   
  */
@Override
public boolean equals(Object x, Object y) throws HibernateException
{
return (x == y) || (x != null && y != null && (x.equals(y))); 
}

@Override
public int hashCode(Object x) throws HibernateException
{
return HashCodeBuilder.reflectionHashCode(this); 
}

/**   
  * 从JDBC ResultSet读取数据,将其转换为自定义类型后返回   
  * (此方法要求对克能出现null值进行处理)   
  * names中包含了当前自定义类型的映射字段名称   
  * @param rs   
  * @param names   
  * @param owner   
  * @return   
  * @throws HibernateException   
  * @throws SQLException   
  */ 
@Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException
{

String val = rs.getString(names[0]); 
        if (val != null) { 
        try { 
                val = new String(val.getBytes("ISO-8859-1"), "GBK"); 
            } catch (UnsupportedEncodingException e) { 
                throw new HibernateException(e.getMessage()); 
            } 
            return val; 
           
        } else { 
        return null; 
        } 
}

/**   
  * 本方法将在Hibernate进行数据保存时被调用   
  * 我们可以通过PreparedStateme将自定义数据写入到对应的数据库表字段   
  * 这个方法将在数据保存时使用。本方法可以使用PreparedStatement将数据写入对应的数据库字段中。 
  * 其中的value表示的是要写入的值。index表示的是在statement的参数中的index.
  * @param st   
  * @param value   
  * @param index   
  * @throws HibernateException   
  * @throws SQLException   
  */ 
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException
{
if (value != null) { 
String val = (String) value; 
            try { 
                val = new String(val.getBytes("GBK"), "ISO-8859-1"); 
            } catch (UnsupportedEncodingException e) { 
                throw new HibernateException(e.getMessage()); 
            } 
            st.setObject(index, val, Types.VARCHAR); 
        } else { 
        st.setNull(index, Types.VARCHAR); 
        } 
}

/**   
  * 提供自定义类型的完全复制方法   
  * 本方法将用构造返回对象   
  * 当nullSafeGet方法调用之后,我们获得了自定义数据对象,在向用户返回自定义数据之前,   
  * deepCopy方法将被调用,它将根据自定义数据对象构造一个完全拷贝,并将此拷贝返回给用户   
  * 此时我们就得到了自定义数据对象的两个版本,第一个是从数据库读出的原始版本,其二是我们通过   
  * deepCopy方法构造的复制版本,原始的版本将有Hibernate维护,复制版由用户使用。原始版本用作   
  * 稍后的脏数据检查依据;Hibernate将在脏数据检查过程中将两个版本的数据进行对比(通过调用   
  * equals方法),如果数据发生了变化(equals方法返回false),则执行对应的持久化操作   
  *   
  * @param value   
  * @return   
  * @throws HibernateException   
  */ 
@Override
public Object deepCopy(Object value) throws HibernateException
{
if (value == null) 
            return null; 
        return new String((String) value); 
//        return (String) value; 
}

/**  
  * 本类型实例是否可变  
  * @return  
  */
@Override
public boolean isMutable()
{
return false;
}

@Override
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) deepCopy(value);
}

@Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException
{
return deepCopy(cached);
}

@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException
{
return deepCopy(original);
}

}
5 楼 xush_319 2013-04-16  
问题解决 下面贴出我的代码
4 楼 xush_319 2013-04-12  
自己写的转码方法 在查询的时候是没有问题的。下面是代码



/**
* 序列化号码
*/
private static final long serialVersionUID = 5801390347850112447L;

public GBKString()
{
super();
}

/**   
  * 返回UserType所映射字段的SQL类型(java.sql.Types)   
  * 返回类型为int[],其中包含了映射个字段的SQL类型代码   
  * (UserType可以映射到一个或者多个字段)   
  * @return   
  */
@Override
public int[] sqlTypes()
{
return new int[] { Types.VARCHAR}; 
}

/**   
  * UserType.nullSafeGet()所返回的自定义数据类型   
  * @return   
  */
@Override
public Class<?> returnedClass()
{
return String.class; 
}

    /**   
  * 自定义数据类型的比对方法   
  * 此方法将用作脏数据检查,参数x、y分别为数据的两个副本   
  * 如果equals方法返回false,则Hibernate将认为数据发生变化,并将变化更新到数据库表中   
  * @param x   
  * @param y   
  * @return   
  * @throws HibernateException   
  */
@Override
public boolean equals(Object x, Object y) throws HibernateException
{
return (x == y) || (x != null && y != null && (x.equals(y))); 
}

@Override
public int hashCode(Object x) throws HibernateException
{
return HashCodeBuilder.reflectionHashCode(this); 
}

/**   
  * 从JDBC ResultSet读取数据,将其转换为自定义类型后返回   
  * (此方法要求对克能出现null值进行处理)   
  * names中包含了当前自定义类型的映射字段名称   
  * @param rs   
  * @param names   
  * @param owner   
  * @return   
  * @throws HibernateException   
  * @throws SQLException   
  */ 
@Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException
{

String val = rs.getString(names[0]); 
        if (val != null) { 
        try { 
                val = new String(val.getBytes("ISO-8859-1"), "GBK"); 
            } catch (UnsupportedEncodingException e) { 
                throw new HibernateException(e.getMessage()); 
            } 
            return val; 
           
        } else { 
        return null; 
        } 
}

/**   
  * 本方法将在Hibernate进行数据保存时被调用   
  * 我们可以通过PreparedStateme将自定义数据写入到对应的数据库表字段   
  * 这个方法将在数据保存时使用。本方法可以使用PreparedStatement将数据写入对应的数据库字段中。 
  * 其中的value表示的是要写入的值。index表示的是在statement的参数中的index.
  * @param st   
  * @param value   
  * @param index   
  * @throws HibernateException   
  * @throws SQLException   
  */ 
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException
{
if (value != null) { 
String val = (String) value; 
            try { 
                val = new String(val.getBytes("GBK"), "ISO-8859-1"); 
            } catch (UnsupportedEncodingException e) { 
                throw new HibernateException(e.getMessage()); 
            } 
            st.setObject(index, val, Types.VARCHAR); 
        } else { 
        st.setNull(index, Types.VARCHAR); 
        } 
}

/**   
  * 提供自定义类型的完全复制方法   
  * 本方法将用构造返回对象   
  * 当nullSafeGet方法调用之后,我们获得了自定义数据对象,在向用户返回自定义数据之前,   
  * deepCopy方法将被调用,它将根据自定义数据对象构造一个完全拷贝,并将此拷贝返回给用户   
  * 此时我们就得到了自定义数据对象的两个版本,第一个是从数据库读出的原始版本,其二是我们通过   
  * deepCopy方法构造的复制版本,原始的版本将有Hibernate维护,复制版由用户使用。原始版本用作   
  * 稍后的脏数据检查依据;Hibernate将在脏数据检查过程中将两个版本的数据进行对比(通过调用   
  * equals方法),如果数据发生了变化(equals方法返回false),则执行对应的持久化操作   
  *   
  * @param value   
  * @return   
  * @throws HibernateException   
  */ 
@Override
public Object deepCopy(Object value) throws HibernateException
{
if (value == null) 
            return null; 
//        return new String((String) value); 
        return (String) value; 
}

/**  
  * 本类型实例是否可变  
  * @return  
  */
@Override
public boolean isMutable()
{
return false;
}

@Override
public Serializable disassemble(Object value) throws HibernateException
{
return null;
}

@Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException
{
return null;
}

@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException
{
return null;
}

3 楼 xush_319 2013-04-12  
我在实体类 属性上是这样写的@Type(type = "commons.util.hibernate.GBKString")
@Column(name="admintruename", length=100)
private String realname;
2 楼 dreamoftch 2013-04-12  
xush_319 写道
我在使用UserType 的时候发现更新数据时 不能获取到该属性的值。正在研究。谁有解决方案共享下。
你是怎么做的?详细问题方便说下么
1 楼 xush_319 2013-04-11  
我在使用UserType 的时候发现更新数据时 不能获取到该属性的值。正在研究。谁有解决方案共享下。

相关推荐

    Hibernate实战(第2版 中文高清版)

     1.1.3 在Java中使用SQL   1.1.4 面向对象应用程序中的持久化   1.2 范式不匹配   1.2.1 粒度问题   1.2.2 子类型问题   1.2.3 同一性问题   1.2.4 与关联相关的问题   1.2.5 数据导航的问题   ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

     11.1.5 使用Hibernate内置映射类型  11.2 客户化映射类型  11.2.1 用客户化映射类型取代Hibernate组件  11.2.2 用UserType映射枚举类型  11.2.3 实现CompositeUserType接口  11.2.4 运行本节范例程序  11.3 ...

    VC开发工具使用技巧源代码_usertype.zip

    VC开发工具使用技巧源代码_usertype.zipVC开发工具使用技巧源代码_usertype.zipVC开发工具使用技巧源代码_usertype.zipVC开发工具使用技巧源代码_usertype.zipVC开发工具使用技巧源代码_usertype.zip

    ssh(structs,spring,hibernate)框架中的上传下载

     以上是Spring+Hibernate将文件二进制数据持久化到数据库的解决方案,而Struts通过将表单中file类型的组件映射为ActionForm中类型为org.apache.struts.upload. FormFile的属性来获取表单提交的文件数据。  工程...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part4

     11.1.5 使用Hibernate内置映射类型  11.2 客户化映射类型  11.2.1 用客户化映射类型取代Hibernate组件  11.2.2 用UserType映射枚举类型  11.2.3 实现CompositeUserType接口  11.2.4 运行本节范例程序  11.3 ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part3

     11.1.5 使用Hibernate内置映射类型  11.2 客户化映射类型  11.2.1 用客户化映射类型取代Hibernate组件  11.2.2 用UserType映射枚举类型  11.2.3 实现CompositeUserType接口  11.2.4 运行本节范例程序  11.3 ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part1.rar

     11.1.5 使用Hibernate内置映射类型  11.2 客户化映射类型  11.2.1 用客户化映射类型取代Hibernate组件  11.2.2 用UserType映射枚举类型  11.2.3 实现CompositeUserType接口  11.2.4 运行本节范例程序  11.3 ...

    CUDA SDK 语法高亮必备 usertype.dat

    在VS中配置CUDA时,本身是不识别CUDA关键字的,关键字不能高亮。早期的CUDA版本在SDK中提供了usertype.dat,其中包含CUDA的关键字,但后来的CUDA没有这个文件,网上找了好久,现在贴出来亲测可用~

    商业编程-源码-VC开发工具使用技巧源代码 usertype.zip

    商业编程-源码-VC开发工具使用技巧源代码 usertype.zip

    usertype.dat

    HLSL的关键字,放在vs的IDE文件下,以在vs中高亮HLSL的关键字

    VS2005编辑器支持着色器语法高亮,一个完整的usertype.dat

    比如我的就在D:\Program Files\Microsoft Visual Studio 8\Common7\IDE\usertype.dat),添加如下的关键字列表,每个单词占一行,如果你想添加你自己的关键字,也可以按照这种方式添加在usertype.dat文件中 ...

    hibernate3.6 文档(pdf 格式)

    1.1. Part 1 - The first Hibernate Application ................................................................ 1 1.1.1. Setup .............................................................................

    微信息发布系统 3.0.rar

    3.部分浏览器小分类选择乱码导致无法在小分类显示问题。 4.解决五处conn提前关闭导致有的时候分类导航不显示问题。 5.解决数据库user表是check而不是quick导致的错误。 6.做了改进,可以设置是否限制本地号码发布...

    hibernate_test

    需要使用这个:添加到 build.gradle compile 'org.jadira.usertype:usertype.jodatime:2.0.1' add to *.hbm.xml 数据库详细信息:CREATE DATABASE datamanagerdb WITH OWNER = datamanageruser ENCODING = 'UTF8' ...

    7384迷你广告信息发布系统 v3.0

    3.部分浏览器小分类选择乱码导致无法在小分类显示问题。 4.解决五处conn提前关闭导致有的时候分类导航不显示问题。 5.解决数据库user表是check而不是quick导致的错误。 6.做了改进,可以设置是否限制本地号码发布...

    解决Django 在ForeignKey中出现 non-nullable field错误的问题

    在django的model中建立了如下的类 class UserType(models.Model): name = models.CharField(max_length=40, verbose_name=u'用户类型') ........ def __str__(self): return self.name class UserProfile&#40;...

    网页短视频管理系统,框架 vs2019 + asp.net webform + sql server

    注意sql server类和access数据库可以使用vs自带的数据库启动,不需要安装庞大的sql安装包。 测试用户 qqq 123456 后台管理用户 admin 123456 模块介绍 管理员 上传电影模块 电影信息管理模块 用户信息管理模块 ...

    网页短视频播客视频管理系统,框架 vs2019 + asp.net webform + sql server

    注意sql server类和access数据库可以使用vs自带的数据库启动,不需要安装庞大的sql安装包。 测试用户 qqq 123456 后台管理用户 admin 123456 模块介绍 管理员 上传电影模块 电影信息管理模块 用户信息管理模块 ...

    有安全需求的选题系统

    Session的使用要求用户浏览器必须支持Cookie,如果浏览器不支持使用Cookie,或者设置为禁用Cookie,那么将不能使用Session。Session信息对客户来说,不同的用户用不同的Session信息来记录。当用户启用Session时,...

Global site tag (gtag.js) - Google Analytics