Hibernate中用户类型(User Type)应用???? 在项目中许多地方使用枚举类,枚举对象值对应数据一个常量。增加代
Hibernate中用户类型(User Type)应用
???? 在项目中许多地方使用枚举类,枚举对象值对应数据一个常量。增加代码的可读性等。具体使用如下:
?
? JPA参考手册:JPA注释参考手册? http://www.oidn.net/blog/article.asp?id=250?
Hibernate Annotation 用户类型(User Type)
使用UserType首先要弄清楚它的目的。大家知道Hibernate解决的主要是对象数据库阻抗失衡的问题,也就是如何将一个或多个对象保存到一个或多个数据库表格中。
这其中有很多方法,其实大部分情况下采用@Embeddable和 @Embedded 就可以解决问题了,只有嵌入对象方式无法满足要求时,或者是Hibernate默认的持久化方式无法满足要求时,才应该考虑UserType。
总之记住一 个原则,不到山穷水尽,不要轻易使用UserType。还有一个要慎重考虑使用UserType的原因是:一旦采用了UserType,你的项目就脱离了 JPA,而直接和Hibernate耦合在一起了。
扩展UserType主要分为两种:
immutable mutable
今天我先举个immutable的例子。
Java 5提出了一个新的enum类,JPA提供的标准方法是保存enum的name或者是ordinal。这种默认方式能够满足新开发的项目,但是对于一些老项目翻新并不一定适用。下面我们来看一个例子:
public ? class ?Status?{
???? public ? static ? final ? int ?ACTIVATED? = ? 5 ;
???? public ? static ? final ? int ?DEACTIVATED? = ? 6 ;
}
这个是在java5之前常用的常量定义方法,老项目数据库里面已经保存了很多的5啊6的。现在要把Status改写成enum,而且不希望修改数据库中已有的数据,怎么做?第一反应,status enum可以这么写:
public ? enum ?Status?{
????????ACTIVATED,
????????DEACTIVATED;
}
持久化enum的name属性是肯定不用考虑了,ordinal属性呢?这里要保存的可是5和6,而Status enum只有两个实体,他们的ordinal只是0和1。而且项目中还会有其他很多类似的常量类需要改写成enum,JPA的默认方式无法完成任务,这时 候可以开始考虑使用UserType了。
先定义一个接口,这样可以使用一个UserType支持所有类似的enum:
public ? interface ?DescriptionID?{
????String?getDescription();
???? int ?getId();
}
然后改写Status enum:
public ? enum ?Status? implements ?DescriptionID?{
????ACTIVATED( 5 ,? " This?object?is?activated " ),??
??? DEACTIVATED( 9 ,? " This?object?is?deactivated " );
???? private ?Integer?id;
???? private ?String?description;
???? private ? static ?List < Status > ?list;
???? static ?{
????????list? = ? new ?ArrayList < Status > ( 2 );
????????list.add(ACTIVATED);
????????list.add(DEACTIVATED);
????}
???? private ?Status( int ?statusNr,?String?description)?{
???????? this .id? = ?statusNr;
???????? this .description? = ?description;
????}
???? public ?String?getDescription()?{
???????? return ? this .description;
????}
???? public ?Integer?getId()?{
???????? return ?id;
????}
???? public ? static ?List < Status > ?getAll()?{
???????? return ?list;
????}
???? public ? static ?Status?findById(Integer?id)?{
???????? for ?(Status?status?:?getAll())?{
???????????? if ?(id? == ?status.getId())?{
???????????????? return ?status;
????????????}
????????}
???????? return ? null ;
????}
}
注意这里每个enum都必须有两个static方法,这些方法名必须在所有的enum中保持一致。
List()方法是为了方便获取所有的Status常 量,例如在用户界面通过ComboBox展示,findById()方法是为了通过给定Id获得对应的Enum实例。其中findById()方法参数一 定要是Integer,原因后面会讲到。
下面编写DescriptionIDUserType:
public ? class ?DescriptionIDUserType? implements ?UserType,?ParameterizedType?{
???? private ?Class?enumClass;
???? public ? void ?setParameterValues(Properties?parameters)?{
???????? try ?{
????????????enumClass? = ?ReflectHelper.classForName(parameters.getProperty( " class " ));
????????}? catch ?(ClassNotFoundException?e)?{
???????????? // ?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}
????????
????}
????
???? public ?Object?assemble(Serializable?cached,?Object?arg1)
???????????? throws ?HibernateException?{
???????? return ?cached;
????}
????
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#deepCopy(java.lang.Object)
????? */
???? public ?Object?deepCopy(Object?value)? throws ?HibernateException?{
???????? // ?TODO?Auto-generated?method?stub
???????? return ?value;
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#disassemble(java.lang.Object)
????? */
???? public ?Serializable?disassemble(Object?value)? throws ?HibernateException?{
???????? // ?TODO?Auto-generated?method?stub
???????? return ?(Serializable)?value;
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#equals(java.lang.Object,
?????*??????java.lang.Object)
????? */
???? public ? boolean ?equals(Object?id1,?Object?id2)? throws ?HibernateException?{
???????? if ?(id1? == ?id2)?{
???????????? return ? true ;
????????}
???????? if ?(id1? == ? null ? || ?id2? == ? null )?{
???????????? return ? false ;
????????}
???????? final ?DescriptionID?did1? = ?(DescriptionID)?id1;
???????? final ?DescriptionID?did2? = ?(DescriptionID)?id2;
???????? return ?did1.getId()? == ?did2.getId()
???????????????? && ?StringUtils.equals(did1.getDescription(),?did2
????????????????????????.getDescription());
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#hashCode(java.lang.Object)
????? */
???? public ? int ?hashCode(Object?value)? throws ?HibernateException?{
???????? // ?TODO?Auto-generated?method?stub
???????? return ?value.hashCode();
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#isMutable()
????? */
???? public ? boolean ?isMutable()?{
???????? // ?TODO?Auto-generated?method?stub
???????? return ? false ;
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#nullSafeGet(java.sql.ResultSet,
?????*??????java.lang.String[],?java.lang.Object)
????? */
???? public ?Object?nullSafeGet(ResultSet?resultSet,?String[]?names,?Object?owner)
???????????? throws ?HibernateException,?SQLException?{
???????? try ?{
???????????? int ?id? = ?resultSet.getInt(names[ 0 ]);
???????????? if ?(resultSet.wasNull())?{
???????????????? return ? null ;
????????????}
???????????? return ?enumClass.getMethod( " findById " ,? new ?Class[]?{?Integer. class ?})
????????????????????.invoke( null ,?id);
????????}? catch ?(IllegalArgumentException?e)?{
???????????? // ?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}? catch ?(SecurityException?e)?{
???????????? // ?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}? catch ?(IllegalAccessException?e)?{
???????????? // ?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}? catch ?(InvocationTargetException?e)?{
???????????? // ?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}? catch ?(NoSuchMethodException?e)?{
???????????? // ?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}
???????? return ? null ;
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#nullSafeSet(java.sql.PreparedStatement,
?????*??????java.lang.Object,?int)
????? */
???? public ? void ?nullSafeSet(PreparedStatement?statement,?Object?value,? int ?index)
???????????? throws ?HibernateException,?SQLException?{
???????? if ?(value? == ? null )?{
????????????statement.setNull(index,?Hibernate.INTEGER.sqlType());
????????}? else ?{
????????????DescriptionID?dID? = ?(DescriptionID)?value;
????????????statement.setInt(index,?dID.getId());
????????}
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#replace(java.lang.Object,
?????*??????java.lang.Object,?java.lang.Object)
????? */
???? public ?Object?replace(Object?original,?Object?arg1,?Object?arg2)
???????????? throws ?HibernateException?{
???????? // ?TODO?Auto-generated?method?stub
???????? return ?original;
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#returnedClass()
????? */
???? public ?Class?returnedClass()?{
???????? return ?DescriptionID. class ;
????}
???? /*
?????*?(non-Javadoc)
?????*?
?????*?@see?org.hibernate.usertype.UserType#sqlTypes()
????? */
???? public ? int []?sqlTypes()?{
???????? return ? new ? int []{Hibernate.INTEGER.sqlType()};
????}
}
我们的这个UserType是要支持实现DescriptionID的各种不同的enum,而enum是没法继承的。所以我们需要用户给出具体的参数,以进一步确定到底是哪个enum类。这也就导致了,我们的这个类需要实现 ParameterizedType接口。
由于enum类本身是immutable的,所以这个UserType的实现类相对比较简单,主要的两个方法是 nullSafeGet和 nullSafeSet。
在nullSaftGet中我们使用Java Reflection并借助用户给出的enum类参数直接调用该enum类的findById()方法,这样我们就可以使用数据库中的integer找到 对应的enum实例。
注意,由于使用了Java Reflection,所以findById()方法参数必须是Integer而非int。 在nullSafeSet中,我们则通过 DescriptionID接口直接获取enum实例的id属性,并且将它保存到数据库中去。
最后看看怎么使用这个UserType:
@TypeDefs({@TypeDef(name? = ? " status " ,?typeClass? = ?DescriptionIDUserType. class ,?
????????????????????parameters? = ?{@Parameter(name? = ? " class " ,?value? = ? " com.yourpackage.Status " )})})
@Entity
public ? class ?SomeObject?{
???? private ?Integer?objectId;
???? private ?Status?status;
????@Id
??? @GeneratedValue(strategy=GenerationType.AUTO)???
??? public ?Integer?getObjectId()?{
???????? return ?objectId;
????}
???? public ? void ?setObjectId(Integer?objectId)?{
???????? this .objectId? = ?objectId;
????}
????@Type(type? = ? " status " )
???? public ?Status?getStatus()?{
???????? return ?status;
????}
???? public ? void ?setStatus(Status?status)?{
???????? this .status? = ?status;
????}
}
其中值得讲讲的就是定义Type时使用的parameter,"class"参数是我们自己定义的,该参数为DescriptionIDUserType提供 了具体的enum类。前面已经讲过了,DescriptionIDUserType就是在运行时态利用这个参数自定义enum与数据库之间的持久化逻辑。
使用这个UserType之后,我们就可以在确保数据库数据不变的情况下,成功地将类型不保险的常量类改写成enum,而且这个UserType支持所有实现了 DescriptionID接口的enum类。
1 楼 ThinkingInAll 2010-05-21 感觉有点复杂,算了还是在get,set里面操作把