关于Hibernate ORM映射实体通用hashCode与equals方法的实现
程序员文章站
2022-05-24 12:00:48
...
公司框架使用的是spring mvc +spring+hibernate 各个版本都很新,hibernate主要使用的是注解映射,说实话,对开发上来说,比xml配置文件要方便得多,而且注解也支持继承,因此父类的实现可以大大降低工作量了,想以前hibernate还是配置文件的年代的时候,很多表的字段是重复固定的,如ID,创建人,创建时间等等,那个时候虽然java层面的实体可以基类,然后通过继承来减轻工作量,可是配置文件却要每个xml都复制一道(也许有方便的方式,可惜我没找到),现在有了注解,就不用这样恶心的复制了。
其实前面废话这么多,也就做个铺垫,因为这次主要说说的是hashCode与equals方法,hashCode方法很多开发人员也许不会有多少感性的认知,但是更多的人对equals方法应该很熟悉才是,hashCode方法其实与其一样,也是用于对象比较的,且其更实用(就是获得其值比较大小,很多jdk内置的一些工具的比较与排序都是使用其来完成的,因此它是一个极其重要的方法)。对于java的一些开发规范来说,对于一个java类的开发,必须实现hashCode与equals来重实现对象比较逻辑。
这次主要任务就是要实现一个hibernate映射父类的hashCode方法与equals方法,因为我并不希望每一个orm映射类都要去实现一个这样的方法,其一这没有必要,其二也为了大大减轻一些工作量,最终目的也是能够实现一个能够适用于jdk一些内置工具的orm映射体系,不会让orm对象在使用HashSet或者Map之类的工具时,让orm 对象表得奇怪。
对于表的映射orm的比较其实逻辑上多很简单,我们约定每个表的内置主键名都是id字段,因此同一个orm映射对象的比较,只要比较id就可以了,id不同的话,那么表示此对象不同,否则就相同,排序也根据其来排序。
初步的实现为下
我们的id是java.lang.Long类型的,因此转换成hashCode需要intValue()一下。
但是我们实现的逻辑在父类中,也就是说,这两个方法会被其继承的子类所使用,而不同子类映射不同的表,而不同的表中所生成的主键id是完全可以相同的,在这里比对ID之前,必须比对类型,这样就可以确保对象的比对是在同一个类型下比较的,于是在之前加入如下代码:
这里为什么不直接比较Class,而要比较Class对象的name属性,因为Class没有自己实现hashCode方法与equals方法,且某个类的Class对象在虚拟机中也不一定会是单例的,因为同样的类可能由不同的类装载器完成装载,这样在虚拟机中就会存在两个完全相同的类的Class对象,而且这两个对象会在一些时候碰头,最典型的例子就是你用oracle驱动包中的Timestamp的class模板去比较一个由jdbc装载的一个Timestamp对象的getClass的模板,你会发现它们的全限定名完全相同,但是equals却返回false。因此此处必须比较name,因为name转换为String,比较是不会出现歧义的。
在使用一段时间后,又发现问题,比较类模板也是不行的,这是因为hibernate的延迟加载机制的缘故,因为延迟加载改变了原始的类模板,使用了重新生成的代理类,这时,我们自己生产的对象与hibernate装载的对象,我们自以为是相同的,但实际却不同,因为我们使用的类模板是原始类模板,而hibernate装载的却是修改过的代理类模板,如此一来,简单的类模板比对则无用了,后来经过我冥思苦想,终于找到一个替代性的办法,也许这个方式并不成熟,因为我们使用的是注解,因此我打算利用注解的@Table的name属性来完成比对(我们规定每个orm映射都要指定表名,因此这种方式对我们的系统来说可行的),如今代码如下,这是先行版的完整代码:
里面实现了一些equals的一些其它标准。
其实前面废话这么多,也就做个铺垫,因为这次主要说说的是hashCode与equals方法,hashCode方法很多开发人员也许不会有多少感性的认知,但是更多的人对equals方法应该很熟悉才是,hashCode方法其实与其一样,也是用于对象比较的,且其更实用(就是获得其值比较大小,很多jdk内置的一些工具的比较与排序都是使用其来完成的,因此它是一个极其重要的方法)。对于java的一些开发规范来说,对于一个java类的开发,必须实现hashCode与equals来重实现对象比较逻辑。
这次主要任务就是要实现一个hibernate映射父类的hashCode方法与equals方法,因为我并不希望每一个orm映射类都要去实现一个这样的方法,其一这没有必要,其二也为了大大减轻一些工作量,最终目的也是能够实现一个能够适用于jdk一些内置工具的orm映射体系,不会让orm对象在使用HashSet或者Map之类的工具时,让orm 对象表得奇怪。
对于表的映射orm的比较其实逻辑上多很简单,我们约定每个表的内置主键名都是id字段,因此同一个orm映射对象的比较,只要比较id就可以了,id不同的话,那么表示此对象不同,否则就相同,排序也根据其来排序。
初步的实现为下
public int hashCode() { return id.intValue(); } public boolean equals(Object obj) { return this.id.longValue()==entity.id.longValue(); }
我们的id是java.lang.Long类型的,因此转换成hashCode需要intValue()一下。
但是我们实现的逻辑在父类中,也就是说,这两个方法会被其继承的子类所使用,而不同子类映射不同的表,而不同的表中所生成的主键id是完全可以相同的,在这里比对ID之前,必须比对类型,这样就可以确保对象的比对是在同一个类型下比较的,于是在之前加入如下代码:
public int hashCode() { int id=this.id==null?0:this.id.intValue(); return id^this.getClass().getName().hashCode(); } public boolean equals(Object obj) { if(!this.getClass().getName().equals(obj.getClass().getName())){ return false; } return this.id.longValue()==entity.id.longValue(); }
这里为什么不直接比较Class,而要比较Class对象的name属性,因为Class没有自己实现hashCode方法与equals方法,且某个类的Class对象在虚拟机中也不一定会是单例的,因为同样的类可能由不同的类装载器完成装载,这样在虚拟机中就会存在两个完全相同的类的Class对象,而且这两个对象会在一些时候碰头,最典型的例子就是你用oracle驱动包中的Timestamp的class模板去比较一个由jdbc装载的一个Timestamp对象的getClass的模板,你会发现它们的全限定名完全相同,但是equals却返回false。因此此处必须比较name,因为name转换为String,比较是不会出现歧义的。
在使用一段时间后,又发现问题,比较类模板也是不行的,这是因为hibernate的延迟加载机制的缘故,因为延迟加载改变了原始的类模板,使用了重新生成的代理类,这时,我们自己生产的对象与hibernate装载的对象,我们自以为是相同的,但实际却不同,因为我们使用的类模板是原始类模板,而hibernate装载的却是修改过的代理类模板,如此一来,简单的类模板比对则无用了,后来经过我冥思苦想,终于找到一个替代性的办法,也许这个方式并不成熟,因为我们使用的是注解,因此我打算利用注解的@Table的name属性来完成比对(我们规定每个orm映射都要指定表名,因此这种方式对我们的系统来说可行的),如今代码如下,这是先行版的完整代码:
public int hashCode() { String tableName=this.tableName(); int id=this.id==null?0:this.id.intValue(); if(StringUtils.isNotBlank(tableName)){ return id^tableName.hashCode(); }else{ return id^this.getClass().hashCode(); } } public boolean equals(Object obj) { if(obj==null){ return false; } if(!(obj instanceof IdEntity)){ return false; } IdEntity entity=(IdEntity) obj; if(this.id==null||entity.id==null){ return false; } boolean typecheck=false; if(StringUtils.isNotBlank(this.tableName())){ this.tableName().equals(entity.tableName()); }else{ typecheck=this.entityName().equals(entity.entityName()); } return (this.id.longValue()==entity.id.longValue())&&typecheck; }
里面实现了一些equals的一些其它标准。
下一篇: Linux中oracle多库监听配置