`
polygoncell
  • 浏览: 56159 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Hibernate user type with Annotation

阅读更多
使用UserType首先要弄清楚它的目的。大家知道Hibernate解决的主要是对象数据库阻抗失衡的问题,也就是如何将一个或多个对象保存到一个或多个数据库表格中。这其中有很多方法,其实大部分情况下采用@Embeddable和 @Embedded 就可以解决问题了,只有嵌入对象方式无法满足要求时,或者是Hibernate默认的持久化方式无法满足要求时,才应该考虑UserType。总之记住一 个原则,不到山穷水尽,不要轻易使用UserType。还有一个要慎重考虑使用UserType的原因是:一旦采用了UserType,你的项目就脱离了 JPA,而直接和Hibernate耦合在一起了。

扩展UserType主要分为两种:
  1. immutable
  2. 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类。

类似的情况朋友们可以自由发挥了。


 
分享到:
评论
4 楼 sorphi 2008-07-23  
downpour 写道
http://www.hibernate.org/272.html

hibernate官方论坛早有实现。还是通用的GenericEnumUserType。不是很明白,为什么碰到问题总是喜欢自己实现,而不去google一下别人早已实现了很多遍并经过实践证明的东西。


官方这个更好一些

其实官方这个例子的不仅仅限于EnumUserType的实现,可以适用于任何type和value相互转换的情况,下面是对官方例子做的小改动:

@Parse(toMethod = "literal", fromMethod = "fromLiteral")
public enum Gender{
  ...
  public String literal();

  public static Gender fromLiteral(final String literal);
}

enum关键字改成class也是一样的。
3 楼 打倒小日本 2008-07-22  
<div class='quote_title'>polygoncell 写道</div>
<div class='quote_div'>
<div class='postText'> 扩展UserType主要分为两种:<br/>
<ol>
<li>immutable</li>
<li>mutable</li>
</ol><span style='color: #000000;'>
</span>
  </div>
</div>
<p><br/>我很感兴趣 这之间的不同 </p>
<p>查了很多资料也没搞明白</p>
<p>期待楼主的下一篇文章能给与解答</p>
2 楼 downpour 2008-07-22  
http://www.hibernate.org/272.html

hibernate官方论坛早有实现。还是通用的GenericEnumUserType。不是很明白,为什么碰到问题总是喜欢自己实现,而不去google一下别人早已实现了很多遍并经过实践证明的东西。
1 楼 careprad 2008-07-22  
很好 我终于看到你这篇文章正确使用了replace方法
我有个merge的问题找了好半天才发现是这个方法没写好造成的

相关推荐

    hibernate-validator-annotation-processor-5.1.0.CR1.zip

    【标题】"Hibernate Validator Annotation Processor 5.1.0.CR1" Hibernate Validator 是一个流行的开源验证框架,它基于JSR 303/JSR 349(Bean Validation)标准,为Java应用程序提供了强大的数据验证功能。...

    Hibernate distribution and annotation

    标题“Hibernate distribution and annotation”涉及到的是Hibernate ORM框架的一个特定版本及其相关的注解功能。Hibernate是一个流行的Java对象关系映射(ORM)工具,它允许开发者使用面向对象的编程模型来操作...

    Hibernate Annotation jar

    这里面包涵了需要用Hibernate Annotation时,所需要的所有jar包! 现在我们公司在做web项目的时候,已经不用*.hbm.xml这种映射文件了,都是用Annotation(注解)方式来完成实体与表之间的映射关系,这样看起来比用...

    hibernate-annotation-helloword

    **hibernate-annotation-helloworld** 是一个基于Hibernate框架,使用注解方式实现的Hello World示例项目。在Java世界中,Hibernate是一个流行的持久层框架,它极大地简化了数据库操作,尤其是在对象关系映射(ORM)...

    hibernate annotation hibernate3

    《Hibernate注解与Hibernate3深度解析》 在Java开发领域,Hibernate作为一种强大的对象关系映射(ORM)框架,极大地简化了数据库操作。本篇将深入探讨Hibernate 3版本中的注解使用,帮助开发者理解如何利用注解进行...

    hibernate 注解 annotation 教程

    hibernate 注解 annotation 教程

    hibernate annotation中文文档

    hibernate annotation中文文档

    Hibernate继承映射(annotation)

    **标题:“Hibernate继承映射(Annotation)详解”** 在Java持久化框架Hibernate中,继承映射是一种关键特性,它允许开发者将对象模型的继承结构映射到数据库的表结构。在传统的面向对象编程中,继承是实现代码复用和...

    hibernate-annotation

    8. `@Temporal`和`@Type`还可以配合使用自定义类型,如货币类型、邮政编码等。 三、优势与实践 使用Hibernate注解的优势在于: 1. 简化配置:注解使得代码更加整洁,无需编写XML映射文件。 2. 代码即配置:注解...

    最全的Hibernate Annotation API文档

    在Hibernate中,注解(Annotation)是一种声明式的方法,用于配置实体类、属性以及它们与数据库表之间的映射关系。本文将深入探讨“最全的Hibernate Annotation API文档”中的关键知识点。 一、实体类(Entity) 在...

    struts2 hibernate3 spring2.5 annotation 整合

    这里主要讨论的是如何将这三者结合,并利用注解(Annotation)进行配置,以简化开发过程。 Struts2是MVC(模型-视图-控制器)框架,主要负责处理HTTP请求和控制应用的流程。它通过Action类处理业务逻辑,使用拦截器...

    Hibernate Annotation

    Hibernate Annotation

    hibernate 中文文档 and _annotation.chm

    《Hibernate中文文档与Annotation》 Hibernate是一款开源的对象关系映射(ORM)框架,它极大地简化了Java应用程序对数据库的操作。Hibernate允许开发人员将Java对象模型与数据库表进行映射,从而避免了传统的JDBC...

    Hibernate Annotation 中文文档

    **Hibernate Annotation 中文文档** 在Java的持久化框架中,Hibernate是一个非常重要的工具,它极大地简化了数据库操作。随着技术的发展,Hibernate Annotation逐渐成为主流,因为它提供了更直观、更简洁的方式来...

    hibernate annotation 中文文档

    《Hibernate Annotation 中文文档》是Java开发者们的重要参考资料,它详细介绍了如何在Hibernate框架中使用注解进行对象关系映射(ORM)。Hibernate是一款强大的开源Java持久化框架,它简化了数据库与Java对象之间的...

    Spring4 MVC Hibernate4集成(Annotation)

    Spring4 MVC Hibernate4集成,使用Annotation,封装dao层和service层。 环境: spring 4.0.3.RELEASE hibernate 4.3.5.Final mysql 5.1.29

    SpringMVC+hibernate+velocity+Annotation

    综合这些技术,"SpringMVC+hibernate+velocity+Annotation"的例子提供了一个完整的Web应用开发基础架构。SpringMVC处理HTTP请求,调用由Hibernate管理的业务逻辑,最后通过Velocity模板生成动态网页。这种架构充分...

    第一个Hibernate with Annotation程式(转)

    【标题】:“第一个Hibernate with Annotation程式(转)” 在Java世界中,Hibernate是一个非常流行的ORM(对象关系映射)框架,它允许开发人员使用面向对象的编程方式来操作数据库,而无需直接编写SQL语句。这个标题...

Global site tag (gtag.js) - Google Analytics