Hibernate硬事实第3部分

在本周的文章中,我将展示如何调整Hibernate,以便将任何数据库数据类型与任何Java类型进行相互转换,从而使数据库模型与对象模型脱钩。

这是第3 后在Hibernate中铁的事实集中series.Other职位包括:

  1. Hibernate硬事实第1部分
  2. Hibernate硬事实第2部分
  3. Hibernate硬事实第3部分 (本文)
  4. Hibernate硬事实-第4部分
  5. Hibernate硬事实–第5部分
  6. Hibernate硬事实-第6部分
  7. Hibernate事实–第7部分

自定义类型映射

在任何需要持久存储数据的应用程序中,Hibernate都是一项非常强大的资产。 举例来说,我本周受命为遗留数据库生成面向对象的模型。 乍一看似乎很简单。 然后,我发现了一个较大的传统设计缺陷:由于历史原因,日期以YYYYMMDD格式存储为数字。 例如,2009年12月11日是20091211。我不能或不愿意更改数据库,但是,我不想使用Integer而不是java.util.Date污染整洁的OO模型。

浏览完Hibernate文档后,我相信它可以以非常简单的方式实现这一目标。

创建自定义类型映射器

第一步也是最大的一步,就是创建一个自定义类型。 此类型不是真正的“类型”,而是知道如何从数据库类型转换为Java类型,反之亦然的映射器。 为此,您所要做的就是创建一个实现org.hibernate.usertype.UserType的类。 让我们详细了解每种实现的方法。

以下方法给出了在读取过程结束时将返回的类。 因为我想要一个Date而不是Integer ,所以我自然返回Date类。

publicClassreturnedClass(){
  returnDate.class;
}

下一个方法返回要从中读取的列的类型(在Types常量中)。 有趣的是,Hibernate允许您映射多个列,因此具有与JPA @Embedded批注相同的功能。 就我而言,我从单个数字列读取,因此我应该返回一个填充有Types.INTEGER的单个对象数组。

publicint[]sqlTypes(){
  returnnewint[]{Types.INTEGER};
}

此方法将检查返回的类实例是不可变的(就像任何普通的Java类型保存原始类型和String一样)还是可变的(像其余的一样)。 这非常重要,因为如果返回false ,则不会检查使用此自定义类型的字段以查看是否应该执行更新。 当然,在所有情况下(可变或不可变)都将替换该字段。 尽管Java API中存在很大争议,但Date 可变的,因此该方法应返回true

publicbooleanisMutable(){

  returntrue;

}

我无法猜测如何使用以下方法,但API指出:

返回持久状态的深层副本,在实体和集合处停止。 不必复制不可变的对象或null值,在这种情况下,只需返回参数即可。

由于我们刚刚说过Date实例是可变的,所以我们不能只返回对象,而必须返回一个克隆:之所以可行,是因为Date clone()方法是公共的。

publicObjectdeepCopy(Objectvalue)throwsHibernateException{
  return((Date)value).clone();
}

接下来的两种方法可以完成实际工作,分别从数据库读取和读取数据库。 请注意,API如何公开要读取的ResultSet对象和要写入的PreparedStatement对象。

publicObjectnullSafeGet(ResultSetrs,String[]names,Objectowner)
  throwsHibernateException,SQLException{
  Dateresult=null;
  if(!rs.wasNull()){
    Integerinteger=rs.getInt(names[0]);
    if(integer!=null){
      try{
        result=newSimpleDateFormat("yyyyMMdd").parse(String.valueOf(integer));
      }catch(ParseExceptione){
        thrownewHibernateException(e);
      }
    }
  }
  returnresult;
}

publicvoidnullSafeSet(PreparedStatementstatement,Objectvalue,intindex)
  throwsHibernateException,SQLException{
  if(value==null){
    statement.setNull(index,Types.INTEGER);
  }else{
    Integerinteger=Integer.valueOf(newSimpleDateFormat("yyyyMMdd").format((String)value));
    statement.setInt(index,integer);
  }
}

从持久性的角度来看 ,接下来的两个方法是equals()hasCode() 的实现

publicinthashCode(Objectx)throwsHibernateException{
  returnx==null?0:x.hashCode();
}

publicbooleanequals(Objectx,Objecty)throwsHibernateException{
  if(x==null){
    returny==null;
  }
  returnx.equals(y);
}

对于equals() ,由于Date是可变的,因此我们不能仅检查对象是否相等,因为同一对象可能已更改。

replace()方法用于合并目的。 这再简单不过了。

publicObjectreplace(Objectoriginal,Objecttarget,Objectowner)throwsHibernateException{
  Ownero=(Owner)owner;
  o.setDate(target);
  return((Date)original).clone();
}

我对replace()方法的实现不可重用:应同时知道拥有类型和setter方法的名称,这使得重用自定义类型有些困难。 如果我想重用它,该方法的主体将需要使用lang.reflect包,并对所使用的方法名称进行猜测。 因此,用于创建可重用用户类型的算法将遵循以下原则:

  1. 列出所有设置方法,并以目标类作为参数。
    1. 如果没有方法匹配,则抛出错误
    2. 如果单个方法匹配,请使用目标参数进行调用
    3. 如果有多个方法匹配,请调用关联的getter以检查哪个方法返回了原始对象

接下来的两种方法在缓存过程中分别用于序列化和反序列化。 由于Date实例是可序列化的,因此易于实现。

publicSerializabledisassemble(Objectvalue)throwsHibernateException
  return(Date)((Date)value).clone();
}

publicObjectassemble(Serializablecached,Objectowner)throwsHibernateException{
  return((Date)cached).clone();
}

在实体上声明类型

实施自定义UserType ,您需要使该实体可以访问它。

@TypeDef(name="dateInt",typeClass=DateIntegerType.class)
publicclassOwner{
  ...
}

使用类型

最后一步是对字段进行注释。

@TypeDef(name="dateInt",typeClass=DateIntegerType.class)
publicclassOwner{

  privateDatedate;

  @Type(type="dateInt")
  publicDategetDate(){
    returndate;
  }

  publicvoidsetDate(Datedate){
    this.date=date;
  }
}

您可以在此处下载本文的资源。

翻译自: https://blog.frankel.ch/hibernate-hard-facts/3/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值