邊城愚人

          如果我不在邊城,我一定是在前往邊城的路上。

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            31 隨筆 :: 0 文章 :: 96 評論 :: 0 Trackbacks
          ?? ?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屬性的清況下)。
          ?? ?

          posted on 2007-08-15 10:32 kafka0102 閱讀(1527) 評論(0)  編輯  收藏 所屬分類: Framework
          主站蜘蛛池模板: 渭源县| 石台县| 建昌县| 新闻| 张家口市| 平顶山市| 石渠县| 壤塘县| 江西省| 乌拉特前旗| 格尔木市| 怀集县| 桂平市| 汉沽区| 南漳县| 邢台县| 广饶县| 山阳县| 锡林浩特市| 祥云县| 玉屏| 扬中市| 沙坪坝区| 林口县| 竹山县| 大悟县| 双流县| 越西县| 镇远县| 遵义市| 东方市| 大埔区| 咸阳市| 新龙县| 台山市| 长兴县| 读书| 五莲县| 新平| 韶关市| 德化县|