使用Hibernate的客户化映射类型

使用Hibernate的客户化映射类型
  Hibernate提供客户化映射类型接口,使用户能以编程方式创建自定义的映射类型来将持久化类任意类型的属性映射到数据库中。使用客户化映射类型,需要实现org.hibernate.usertype.UserType接口。这是个强大的功能,也是Hibernate的最佳实践之一。我们经常提到ORM中很困难的一点便是O的属性和R的属性不能一一映射,而Hibernate提供的UserType无疑给出了一个很好的解决方案。本文给出使用客户化映射类型的两个例子,算是对Hibernate初学者的抛砖。
    第一个例子是使用UserType映射枚举类型。假设Account表中含有一sex列,类型为tinyint(当前其0代表男,1代表女,将来可能出现2等代表其他性别类型);我们当然可以在对应的Account类中添加int类型的sex属性,但这种数字化无显示意义且类型不安全的枚举不是很好的解决方式,这里就采用了java5的enum来作为Account类的性别属性(如果不熟悉java5的enum,也可采用《effective java》中提到的经典的类型安全的枚举方案)。在Account添加enum Gender:

public   class  Account  extends  AbstractDomain < Long > {
    
    
public   enum  Gender{
        Male(
" male " , 0 ),
        Female(
" female " , 1 );
        
        
private  String name;
        
private   int  value;
        
        
public  String getName() {
            
return  name;
        }
        
public   int  getValue() {
            
return  value;
        }
        
        
private  Gender(String name, int  value){
            
this .name  =  name;
            
this .value  =  value;
        }
        
        
public   static  Gender getGender( int  value){
            
if ( 0   ==  value) return  Male;
            
else   if ( 1   ==  value) return  Female;
            
else   throw   new  RuntimeException();
        }
        
    }
    
    
private  Gender gender;
    
public  Gender getGender() {
        
return  gender;
    }
    
public   void  setGender(Gender gender) {
        
this .gender  =  gender;
    }
       
// 省略其他    
}

    接下来定义实现UserType接口的GenderUserType:

public   class  GenderUserType  implements  UserType{

    
public  Object assemble(Serializable arg0, Object arg1)  throws  HibernateException {
        
return   null ;
    }

    
/*
     *  这是用于Hibernate缓存生成的快照,由于Gender是不可变的,直接返回就好了。
     
*/
    
public  Object deepCopy(Object arg0)  throws  HibernateException {
        
return  arg0;
    }

    
public  Serializable disassemble(Object arg0)  throws  HibernateException {
        
return   null ;
    }

    
/*
     * 由于Gender是不可变的,因此直接==了,这个方法将在insert、update时用到。
     
*/
    
public   boolean  equals(Object x, Object y)  throws  HibernateException {
        
return  x  ==  y;
    }

    
public   int  hashCode(Object o)  throws  HibernateException {
        
return  o.hashCode();
    }

    
/*
     * 表明Gender是不是可变类(很重要的概念哦),这里的Gender由于是枚举所以是不可变的
     
*/
    
public   boolean  isMutable() {
        
return   false ;
    }

    
/*
     *  从ResultSet读取sex并返回Gender实例,这个方法是在从数据库查询数据时用到。
     
*/
    
public  Object nullSafeGet(ResultSet rs, String[] names, Object owner)  throws  HibernateException, SQLException {
        
int  value  =  rs.getInt(names[ 0 ]);
        
return  Account.Gender.getGender(value);
    }

    
/*
     *  将Gender的value设置到PreparedStatement。
     
*/
    
public   void  nullSafeSet(PreparedStatement ps, Object value,  int  index)  throws  HibernateException, SQLException {
        if(value == null){
            ps.setInt(index,Account.Gender.Male.getValue());
        }else{
            ps.setInt(index,((Account.Gender)value).getValue());
        }

    }

    
public  Object replace(Object arg0, Object arg1, Object arg2)  throws  HibernateException {
        
return   null ;
    }

    
/*
     * 设置映射的Gender类
     
*/
    
public  Class returnedClass() {
        
return  Account.Gender. class ;
    }

    
/*
     *  设置Gender枚举中的value属性对应的Account表中的sex列的SQL类型
     
*/
    
public   int [] sqlTypes() {
        
int [] typeList  =  {Types.TINYINT};
        
return  typeList;
    }
}

    最后在Account的配置文件中配置gender属性就好了:
<property name="gender" type="org.prague.domain.util.GenderUserType" column="sex"></property>
    除了可以使用 UserType映射枚举类型,也可以使用Hibernate的PersistentEnum来实现同样的功能,感兴趣的朋友可以参考文章http://www.hibernate.org/203.html。

    
    第二个例子是关于email的。假设Account表中email是一个varchar型的字段,而Account中的Email是如下的类:

public   class  Email {
    String username;

    String domain;

    
public  Email() {
    }

    
public  Email(String username, String domain) {
        
this .username  =  username;
        
this .domain  =  domain;
    }

    
public  String getUsername() {
        
return  username;
    }

    
public  String getDomain() {
        
return  domain;
    }

    
    
public   void  setDomain(String domain) {
        
this .domain  =  domain;
    }

    
public   void  setUsername(String username) {
        
this .username  =  username;
    }

    
public  String toString() {
        
return  username  +   ' @ '   +  domain;
    }

    
public   static  Email parse(String email) {
        Email e 
=   new  Email();
        
int  at  =  email.indexOf( ' @ ' );
        
if  (at  ==   - 1 ) {
            
throw   new  IllegalArgumentException( " Invalid email address " );
        }

        e.username 
=  email.substring( 0 , at);
        e.domain 
=  email.substring(at  +   1 );

        
return  e;
    }

    @Override
    
public   int  hashCode() {
        
final   int  PRIME  =   31 ;
        
int  result  =   1 ;
        result 
=  PRIME  *  result  +  ((domain  ==   null ?   0  : domain.hashCode());
        result 
=  PRIME  *  result  +  ((username  ==   null ?   0  : username.hashCode());
        
return  result;
    }

    @Override
    
public   boolean  equals(Object obj) {
        
if  ( this   ==  obj)     return   true ;
      
if ( null   ==  obj) return   false ;
        
if  (getClass()  !=  obj.getClass())
            
return   false ;
        
final  Email other  =  (Email) obj;
        
if  (domain  ==   null ) {
            
if  (other.domain  !=   null )
                
return   false ;
        } 
else   if  ( ! domain.equals(other.domain))
            
return   false ;
        
if  (username  ==   null ) {
            
if  (other.username  !=   null )
                
return   false ;
        } 
else   if  ( ! username.equals(other.username))
            
return   false ;
        
return   true ;
    }
}
    email是Account类的一个属性:
public   class  Account  extends  AbstractDomain < Long > {
    
    
private  Email email;
    
public  Email getEmail() {
        
return  email;
    }
    
public   void  setEmail(Email email) {
        
this .email  =  email;
    }

    
// 省略其他    
}

    这样的情况下,需要将email的username + '@' + domain映射到Account表的email列,定义一个EmailUserType如下:
 
    public   class  EmailUserType  implements  UserType{

    
public  Object assemble(Serializable arg0, Object arg1)  throws  HibernateException {
        
return   null ;
    }

    
public  Object deepCopy(Object o)  throws  HibernateException {
        
if ( null   ==  o) return   null ;
        Email e 
=  (Email)o;
        
return   new  Email(e.getUsername(),e.getDomain());
    }

    
public  Serializable disassemble(Object arg0)  throws  HibernateException {
        
return   null ;
    }

    
public   boolean  equals(Object x, Object y)  throws  HibernateException {
        
if (x  ==  y) return   true ;
        
if (x  ==   null   ||  y  ==   null ) return   false ;
        
boolean   f  =  x.equals(y);
        
return  f;
    }

    
public   int  hashCode(Object o)  throws  HibernateException {
        
return  o.hashCode();
    }

    
public   boolean  isMutable() {
        
return   true ;
    }

    
public  Object nullSafeGet(ResultSet rs, String[] names, Object o)  throws  HibernateException, SQLException {
        String email 
=  rs.getString(names[ 0 ]);
        
if (email  ==   null ) return   null ;
        
int  index  =  email.indexOf( " @ " );
        
if (index  <   0 ) throw   new  RuntimeException();
        
return   new  Email(email.substring( 0 ,index),email.substring(index + 1 ));
    }

    
public   void  nullSafeSet(PreparedStatement ps, Object o,  int  index)  throws  HibernateException, SQLException {
        
if (o  ==   null  )ps.setNull(index, Types.VARCHAR);
        
else {
            Email e 
=  (Email)o;
            
if (e.getDomain()  ==   null   ||  e.getUsername()  ==   null )ps.setNull(index, Types.VARCHAR);
            
else {
                String email 
=  e.getUsername()  +   " @ "   +  e.getDomain();
                ps.setString(index, email);
            }
        }
        
    }

    
public  Object replace(Object arg0, Object arg1, Object arg2)  throws  HibernateException {
        
return   null ;
    }

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

    
public   int [] sqlTypes() {
        
int [] typeList  =  {Types.VARCHAR};
        
return  typeList;
    }
}

    最后配置下 email 属性:
<property name="email" type="org.prague.domain.util.EmailUserType" column="email"></property>
    相比于Gedner,Email是一个可变类(如果想将其变为不可变类,只需要去掉属性的set方法),因此EmailUserType中的equals要用到Email的equals(hashCode())方法,而deepCopy(Object o) 要做到是深拷贝,否则即便Email属性内容改变,由于Hibernate缓存中的快照指向的对象不变,在update时可能不起作用(在指定了dynamic-update属性的清况下)。

你可能感兴趣的:(使用Hibernate的客户化映射类型)