`

Hibernate Annotation driven equals and hashCode

阅读更多

The following implementation of equals, hashcode and toString is using the concept of one or more business keys defined by annotations.The annotation @BusinessKey can be applied with an include/exclude filter on field or method level.

Enumeration for include/exclude filter:

public enum Method {
    ALL, NONE, EQUALS, HASH_CODE, TO_STRING
}

Business key annotation:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.METHOD })
public @interface BusinessKey {
    Method[] include() default Method.ALL;
    Method[] exclude() default Method.NONE;
}

Implementation of equals, hashCode and toString using cached reflection:

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

public class BeanUtils {
    private static Map<String, List<AccessibleObject>> cache = new Hashtable<String, List<AccessibleObject>>();

    private BeanUtils() {}

    public static boolean equals(Object obj1, Object obj2) {
        if (obj1 == obj2) {
            return true;
        }

        if (obj2 == null || obj2.getClass() != obj1.getClass()) {
            return false;
        }

        EqualsBuilder builder = new EqualsBuilder();

        for (AccessibleObject ao : getAccessibleObjects(obj1, 1)) {
            try {
                if (ao instanceof Field) {
                    builder.append(((Field) ao).get(obj1), ((Field) ao).get(obj2));
                } else {
                    builder.append(((java.lang.reflect.Method) ao).invoke(obj1, (Object[]) null), ((java.lang.reflect.Method) ao).invoke(obj2, (Object[]) null));
                }
            } catch (Exception e) {}
        }

        return builder.isEquals();
    }

    public static int hashCode(Object obj) {
        HashCodeBuilder builder = new HashCodeBuilder();

        for (AccessibleObject ao : getAccessibleObjects(obj, 2)) {
            try {
                if (ao instanceof Field) {
                    builder.append(((Field) ao).get(obj));
                } else {
                    builder.append(((java.lang.reflect.Method) ao).invoke(obj, (Object[]) null));
                }
            } catch (Exception e) {}
        }

        return builder.toHashCode();
    }

    public static String toString(Object obj) {
        ToStringBuilder builder = new ToStringBuilder(obj, ToStringStyle.SHORT_PREFIX_STYLE);

        for (AccessibleObject ao : getAccessibleObjects(obj, 4)) {
            try {
                if (ao instanceof Field) {
                    builder.append(((Field) ao).getName(), ((Field) ao).get(obj));
                } else {
                    builder.append(((java.lang.reflect.Method) ao).getName(), ((java.lang.reflect.Method) ao).invoke(obj, (Object[]) null));
                }
            } catch (Exception e) {}
        }

        return builder.toString();
    }

    private static List<AccessibleObject> getAccessibleObjects(Object obj, int filter) {
        Class<?> clazz = obj.getClass();

        String name = clazz.getName() + filter;

        if (!cache.containsKey(name)) {
            List<AccessibleObject> aos = new ArrayList<AccessibleObject>();

            do {
                Field[] fields = clazz.getDeclaredFields();

                for (Field field : fields) {
                    BusinessKey bk = field.getAnnotation(BusinessKey.class);
                    if (bk != null && (filter(bk) & filter) == filter) {
                        field.setAccessible(true);
                        aos.add(field);
                    }
                }

                java.lang.reflect.Method[] methods = clazz.getDeclaredMethods();

                for (java.lang.reflect.Method method : methods) {
                    BusinessKey bk = method.getAnnotation(BusinessKey.class);
                    if (bk != null && (filter(bk) & filter) == filter) {
                        method.setAccessible(true);
                        aos.add(method);
                    }
                }

                clazz = clazz.getSuperclass();
            } while (clazz != null);

            Collections.sort(aos, new AccessibleObjectComparator());

            cache.put(name, aos);
        }

        return cache.get(name);
    }

    private static int filter(BusinessKey bk) {
        int filter = 0;

        for (Method method : bk.include()) {
            switch (method) {
            case ALL:
                filter = filter | 7;
                break;
            case EQUALS:
                filter = filter | 1;
                break;
            case HASH_CODE:
                filter = filter | 2;
                break;
            case TO_STRING:
                filter = filter | 4;
                break;
            }
        }

        for (Method method : bk.exclude()) {
            switch (method) {
            case ALL:
                filter -= filter & 7;
                break;
            case EQUALS:
                filter -= filter & 1;
                break;
            case HASH_CODE:
                filter -= filter & 2;
                break;
            case TO_STRING:
                filter -= filter & 4;
                break;
            }
        }

        return filter;
    }

    private static class AccessibleObjectComparator implements Comparator<AccessibleObject> {
        public int compare(AccessibleObject o1, AccessibleObject o2) {
            boolean o1IsField = o1 instanceof Field;
            boolean o2IsField = o2 instanceof Field;

            if (!o1IsField && o2IsField) {
                return 1;
            } else if (o1IsField && !o2IsField) {
                return -1;
            }

            if (o1IsField) {
                return ((Field) o1).getName().compareTo(((Field) o2).getName());
            } else {
                return ((java.lang.reflect.Method) o1).getName().compareTo(((java.lang.reflect.Method) o2).getName());
            }
        }
    }
}

 

Example of usage in a JPA annotated bean:

@Entity
public class User {
    private Long id;
    private String username;
    private byte[] password;
    private Set<Role> roles = new TreeSet<Role>();

    protected User() {}

    public User(String username, String password) {
        this.username = username;
        setPassword(password);
    }

    @BusinessKey(include = Method.TO_STRING)
    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    protected void setId(Long id) {
        this.id = id;
    }

    @BusinessKey
    @Column(nullable = false, unique = true)
    public String getUsername() {
        return username;
    }

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

    @BusinessKey
    @Column(length = 32, nullable = false)
    public byte[] getPassword() {
        return password;
    }

    public void setPassword(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            this.password = md.digest(password.getBytes());
        } catch (NoSuchAlgorithmException e) {}
    }

    @ManyToMany
    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
    
    @Override
    public boolean equals(Object obj) {
        return BeanUtils.equals(this, obj);
    }

    @Override
    public int hashCode() {
        return BeanUtils.hashCode(this);
    }

    @Override
    public String toString() {
        return BeanUtils.toString(this);
    }    
}

If you don't mind to use a superclass in your beans the following superclass can be added as a convenience:

public abstract class Bean {
    @Override
    public boolean equals(Object obj) {
        return BeanUtils.equals(this, obj);
    }

    @Override
    public int hashCode() {
        return BeanUtils.hashCode(this);
    }

    @Override
    public String toString() {
        return BeanUtils.toString(this);
    }
}
分享到:
评论

相关推荐

    Hibernate Annotation jar

    这里面包涵了需要用Hibernate Annotation时,所需要的所有jar包! 现在我们公司在做web项目的时候,已经不用*.hbm.xml这种映射文件了,都是用Annotation(注解)方式来完成实体与表之间的映射关系,这样看起来比用...

    hibernate annotation中文文档

    hibernate annotation中文文档

    hibernate annotation hibernate3

    《Hibernate注解与Hibernate3深度解析》 在Java开发领域,Hibernate作为一种强大的对象关系映射(ORM)框架,极大地简化了数据库操作。本篇将深入探讨Hibernate 3版本中的注解使用,帮助开发者理解如何利用注解进行...

    hibernate annotation 中文文档

    《Hibernate Annotation 中文文档》是Java开发者们的重要参考资料,它详细介绍了如何在Hibernate框架中使用注解进行对象关系映射(ORM)。Hibernate是一款强大的开源Java持久化框架,它简化了数据库与Java对象之间的...

    最全的Hibernate Annotation API文档

    在Hibernate中,注解(Annotation)是一种声明式的方法,用于配置实体类、属性以及它们与数据库表之间的映射关系。本文将深入探讨“最全的Hibernate Annotation API文档”中的关键知识点。 一、实体类(Entity) 在...

    Hibernate Annotation 中文文档

    **Hibernate Annotation 中文文档** 在Java的持久化框架中,Hibernate是一个非常重要的工具,它极大地简化了数据库操作。随着技术的发展,Hibernate Annotation逐渐成为主流,因为它提供了更直观、更简洁的方式来...

    Hibernate Annotation库

    Hibernate Annotation库是Java开发中用于简化对象关系映射(ORM)的一个重要组件,它使得开发者能够在Java类上直接使用注解来定义数据库映射,从而避免了传统的XML配置文件。这三个特定的jar包是Hibernate ORM框架中...

    Hibernate Annotation 学习笔记

    《Hibernate Annotation 学习笔记》 在Java的持久化框架中,Hibernate以其强大的功能和易用性成为开发者首选之一。而Hibernate Annotation则是Hibernate提供的一种基于注解的实体映射方式,它极大地简化了传统XML...

    Hibernate Annotation入门

    **Hibernate Annotation 入门** Hibernate 是一款非常流行的Java对象关系映射(ORM)框架,它允许开发者使用面向对象的方式操作数据库。而Hibernate Annotation是Hibernate的一个重要特性,它通过在Java类和字段上...

    Hibernate Annotation

    Hibernate Annotation

    sping hibernate Annotation(注释配置) demo(例子)

    总结来说,这个 "Spring Hibernate Annotation demo" 展示了如何在 Spring 框架中使用注解配置来管理依赖,以及如何利用 Hibernate 的注解进行数据持久化。同时,它还涉及到了 Flex 前端与后端的交互。通过学习这个 ...

    hibernate annotation api chm文件

    Hibernate Annotation API是Hibernate ORM的一种扩展,允许开发者使用Java注解(Annotations)来定义对象-关系映射。这种API避免了传统的Hibernate XML配置文件,使得ORM配置更加内聚且易于维护。 2. **核心注解**...

    Hibernate_annotation3.4_api.CHM

    Hibernate annotation 3.4 api CHM

    Hibernate distribution and annotation

    标题“Hibernate distribution and annotation”涉及到的是Hibernate ORM框架的一个特定版本及其相关的注解功能。Hibernate是一个流行的Java对象关系映射(ORM)工具,它允许开发者使用面向对象的编程模型来操作...

    Hibernate Annotation 笔记 总结 注解

    【标题】:深入理解Hibernate Annotation及其使用 【描述】:本文将全面介绍Hibernate Annotation的使用,包括事务管理和声明式事务处理,以及如何通过注解简化数据库持久化操作。 【标签】:Hibernate, ...

    hibernate 注解 annotation 教程

    hibernate 注解 annotation 教程

    HibernateAnnotation

    ### HibernateAnnotation 技术概述 在Java开发领域中,Hibernate框架是进行对象关系映射(Object-Relational Mapping,简称ORM)的一种非常流行的工具。它能够帮助开发者将面向对象的模型与关系型数据库进行桥接,...

    Hibernate-Annotation中文教程.pdf

    Hibernate Annotation中文教程 Hibernate 是 Java 数据库持久性的事实标准之一,它非常强大、灵活,而且具备了优异的性能。传统上,Hibernate 的配置依赖于外部 XML 文件,而最近发布的几个 Hibernate 版本中,...

Global site tag (gtag.js) - Google Analytics