欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

简要分析Java的Hibernate框架中的自定义类型

程序员文章站 2024-03-08 20:05:16
最近看到hibernate的自定义类型,这个以前没接触过,在这里记录一下,当是对自己知识的巩固,也让没有接触过的朋友一起学习研究一番。  1)自定义类型,顾名思...

最近看到hibernate的自定义类型,这个以前没接触过,在这里记录一下,当是对自己知识的巩固,也让没有接触过的朋友一起学习研究一番。
 1)自定义类型,顾名思义,当然就是由于内部的类型不满足需求,而自己来进行实现的类型。这种情况不多,但我们还是有必要学习一下,技多不压身嘛。也学习一下,别人在做框架的时候是怎么去考虑的,怎么去思考扩展性的。
 自定义类型有两个方法来实现,一种是实现usertype,另外一种实现compositeusertype,另外可能还有一些方法,但我暂时没用到,先不讲了。
 我暂时只用到usertype,我们就先看一下usertype接口的定义:

public interface usertype { 
  /** 
   * return the sql type codes for the columns mapped by this type. the 
   * codes are defined on <tt>java.sql.types</tt>. 
   */ 
  public int[] sqltypes(); 
 
  /** 
   * the class returned by <tt>nullsafeget()</tt>. 
   */ 
  public class returnedclass(); 
 
  /** 
   * compare two instances of the class mapped by this type for persistence "equality". 
   * equality of the persistent state. 
   */ 
  public boolean equals(object x, object y) throws hibernateexception; 
 
  /** 
   * get a hashcode for the instance, consistent with persistence "equality" 
   */ 
  public int hashcode(object x) throws hibernateexception; 
 
  /** 
   * retrieve an instance of the mapped class from a jdbc resultset. implementors 
   * should handle possibility of null values. 
   */ 
  public object nullsafeget(resultset rs, string[] names, object owner) throws hibernateexception, sqlexception; 
 
  /** 
   * write an instance of the mapped class to a prepared statement. implementors 
   * should handle possibility of null values. a multi-column type should be written 
   * to parameters starting from <tt>index</tt>. 
   */ 
  public void nullsafeset(preparedstatement st, object value, int index) throws hibernateexception, sqlexception; 
 
  /** 
   * return a deep copy of the persistent state, stopping at entities and at 
   * collections. it is not necessary to copy immutable objects, or null 
   * values, in which case it is safe to simply return the argument. 
   */ 
  public object deepcopy(object value) throws hibernateexception; 
 
  /** 
   * are objects of this type mutable? 
   * 
   * @return boolean 
   */ 
  public boolean ismutable(); 
 
  /** 
   * transform the object into its cacheable representation. at the very least this 
   * method should perform a deep copy if the type is mutable. that may not be enough 
   * for some implementations, however; for example, associations must be cached as 
   * identifier values. (optional operation) 
   * 
   * @param value the object to be cached 
   * @return a cachable representation of the object 
   * @throws hibernateexception 
   */ 
  public serializable disassemble(object value) throws hibernateexception; 
 
  /** 
   * reconstruct an object from the cacheable representation. at the very least this 
   * method should perform a deep copy if the type is mutable. (optional operation) 
   */ 
  public object assemble(serializable cached, object owner) throws hibernateexception; 
 
  /** 
   * during merge, replace the existing (target) value in the entity we are merging to 
   * with a new (original) value from the detached entity we are merging. for immutable 
   * objects, or null values, it is safe to simply return the first parameter. for 
   * mutable objects, it is safe to return a copy of the first parameter. for objects 
   * with component values, it might make sense to recursively replace component values. 
   */ 
  public object replace(object original, object target, object owner) throws hibernateexception; 
} 

  其实大家看英文一般情况下都能理解,不再多做解释了,这里我们最主要的就是实现nullsafeset() 方法,这个方法主要用到把此类型的值保存到数据库,这一次我们先学怎么用,以后我们再慢慢研究内部是怎么来实现的。
 2)我学习时写的例子是参照夏昕的例子,所以肯定和网上的大部分都一样,我们只是大概分析一下:
 下面是user类

package org.hibernate.tutorial.domain; 
import java.io.serializable; 
import java.util.list; 
public class user implements serializable{ 
  public long id; 
  private string name; 
  private list emails; 
    省略get/set方法 
} 

  下来是自定义的emaillist类:

package org.hibernate.tutorial.domain; 
import java.io.serializable; 
import java.sql.preparedstatement; 
import java.sql.resultset; 
import java.sql.sqlexception; 
import java.sql.types; 
import java.util.arraylist; 
import java.util.list; 
import org.hibernate.hibernate; 
import org.hibernate.hibernateexception; 
import org.hibernate.usertype.usertype; 
 
public class emaillist implements usertype { 
 
  private static final char splitter = ';'; 
  private static final int[] types = new int[] {types.varchar}; 
   
  private string assemble(list emaillist) { 
    stringbuilder strbuf = new stringbuilder(); 
    for (int i = 0; i < emaillist.size() - 1; i++){ 
      strbuf.append(emaillist.get(i)).append(splitter); 
    } 
    strbuf.append(emaillist.get(emaillist.size()-1)); 
    return strbuf.tostring(); 
  } 
   
  private list parse(string value) { 
    string[] strs = org.hibernate.util.stringhelper.split(value,string.valueof(splitter)); 
    list emaillist = new arraylist(); 
    for (int i = 0;i < strs.length; i++) { 
      emaillist.add(strs[i]); 
    } 
    return emaillist; 
  } 
 
  public object deepcopy(object value) throws hibernateexception { 
    list sourcelist = (list)value; 
    list targetlist = new arraylist(); 
    targetlist.add(sourcelist); 
    return targetlist; 
  } 
 
  public serializable disassemble(object value) throws hibernateexception { 
    return null; 
  } 
 
  public boolean equals(object x, object y) throws hibernateexception { 
    if (x == y) return true; 
     
    system.out.println("x:"+x+"y:"+y); 
     
    if (x != null && y != null) { 
      list xlist = (list)x; 
      list ylist = (list)y; 
       
      if(xlist.size() != ylist.size()) return false; 
       
      for (int i = 0; i < xlist.size(); i++) { 
        string str1 = (string)xlist.get(i); 
        string str2 = (string)ylist.get(i); 
         
        if (!str1.equals(str2)) return false; 
      } 
       
      return true; 
    } 
     
    return false; 
  } 
 
  public boolean ismutable() { 
    return false; 
  } 
 
  public object nullsafeget(resultset rs, string[] names, object owner) 
      throws hibernateexception, sqlexception { 
    string value = (string)hibernate.string.nullsafeget(rs, names[0]); 
    if (value != null) { 
      return parse(value);//把list通过;分割 
    } else{ 
      return null; 
    } 
  } 
 
  public void nullsafeset(preparedstatement st, object value, int index) 
      throws hibernateexception, sqlexception { 
    system.out.println("set method executed!"); 
     
    system.out.println("value:" + value); 
     
    if (value != null){ 
      string str = assemble((list)value);//把字符串用;拼接 
       
      hibernate.string.nullsafeset(st, str, index); 
    } else { 
      hibernate.string.nullsafeset(st, value, index); 
    } 
  } 
 
  public class returnedclass() { 
    return list.class; 
  } 
 
  public int[] sqltypes() { 
    return types; 
  } 
  //省略其他不需要修改的方法 
} 

  类中实现的方法是需要修改的方法,其他不需要修改暂时不用的方法则没有写出来,但还是需要实现的。
 3)接下来就是user类的映射文件:

<class name="user" table="user"> 
    <id name="id" column="user_id" type="java.lang.long"> 
      <generator class="native" /> 
    </id> 
    <property name="name" type="string" column="user_name"/> 
    <property name="emails" type="org.hibernate.tutorial.domain.emaillist" column="emails"/> 
  </class> 

  相信大家都知道怎么进行修改,这里也不进行讲解了,主要是修改emails的type,修改为我们刚才定义的emaillist类。
 4)最后我们来写一个测试类:

import java.util.hashmap; 
import java.util.list; 
import java.util.map; 
import java.util.arraylist; 
import junit.framework.testcase; 
 
import org.hibernate.entitymode; 
import org.hibernate.session; 
import org.hibernate.sessionfactory; 
import org.hibernate.transaction; 
import org.hibernate.cfg.configuration; 
import org.hibernate.tutorial.domain.user; 
 
public class hibernatetest extends testcase{ 
 
  private session session = null; 
   
  protected void setup() throws exception { 
     
    configuration cfg = new configuration().configure(); 
    sessionfactory sessionfactory = cfg.buildsessionfactory(); 
     
    session = sessionfactory.opensession(); 
 
  } 
   
  public void testinsert(){ 
    transaction tran = null; 
    try{ 
      tran = session.begintransaction(); 
       
      user user = new user(); 
       
      user.setname("shun"); 
       
      list list = new arraylist(); 
       
      list.add("12312@sfsdf.com"); 
      list.add("123@123.com"); 
 
      user.setemails(list); 
      session.save(user); 
       
      tran.commit(); 
    } catch (exception ex) { 
      ex.printstacktrace(); 
      if (tran != null){ 
        tran.rollback(); 
      } 
    } 
  } 
   
  protected void teardown() throws exception { 
    session.close(); 
  } 
} 

  这里可能会出现问题,当我们只保存一个email时,它会出现异常,在数据库里面是email字段是空的,而当我们如上面代码一样,有两个时,并不会出现问题,数据库中结果如图:

简要分析Java的Hibernate框架中的自定义类型

而当我们只保存一个时,异常如下:

java.lang.classcastexception: java.util.arraylist cannot be cast to java.lang.string 

 它发生在emaillist的equals方法中的string str1 = (string)xlist.get(i);这句代码中,经检查是在插入数据传到emaillist的nullsafeset方法时变成了list的list,即
value:[[12312@sfsdf.com, 123@123.com]]这样的形式,这样在比较的时候就会出问题,它永远都只有一个值,而在比较的时候却是不同的,

if(xlist.size() != ylist.size()) return false; 

 所以在强制转换时会出问题。
 而经过检查,equals方法里:
 

x:[[12312@sfsdf.com, 123@123.com]]y:[12312@sfsdf.com, 123@123.com] 

 这样的结果却是很奇怪的。网上并没有讲到为什么会出现这种情况。这里提出一下:我用的hibernate版本是hibernate 3.3.2.ga。不知道是版本问题还是其他问题,我们明天再研究一下。如果有哪位兄弟知道为什么的,希望也不吝告诉我一下。