20

Do you have a common base class for Hibernate entities, i.e. a MappedSuperclass with id, version and other common properties? Are there any drawbacks?

Example:

@MappedSuperclass()
public class BaseEntity {

    private Long id;
    private Long version;
    ...

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    public Long getId() {return id;}

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

    @Version
    public Long getVersion() {return version;}
    ...

    // Common properties
    @Temporal(TemporalType.TIMESTAMP)
    public Date creationDate() {return creationDate;}
    ...
}

@Entity
public class Customer extends BaseEntity {
    private String customerName;
    ...
}
Răzvan Flavius Panda
  • 21,730
  • 17
  • 111
  • 169
cretzel
  • 19,864
  • 19
  • 58
  • 71

5 Answers5

5

This works fine for us. As well as the ID and creation date, we also have a modified date. We also have an intermediate TaggedBaseEntity that implements a Taggable interface, because some of our web application's entities have tags, like questions on Stack Overflow.

Peter Hilton
  • 17,211
  • 6
  • 50
  • 75
5

The one that I use is primarily to implement hashCode() and equals(). I also added a method to pretty print the entity. In response to DR above, most of this can be overridden, but in my implementation you are stuck with an ID of type Long.

public abstract class BaseEntity implements Serializable {

    public abstract Long getId();
    public abstract void setId(Long id);

    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
        return result;
    }

    /**
     * @see java.lang.Object#equals(Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        BaseEntity other = (BaseEntity) obj;
        if (getId() == null) {
            if (other.getId() != null)
                return false;
        } else if (!getId().equals(other.getId()))
            return false;
        return true;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new StringBuilder(getClass().getSimpleName()).append(":").append(getId()).toString();
    }

    /**
     * Prints complete information by calling all public getters on the entity.
     */
    public String print() {

        final String EQUALS = "=";
        final String DELIMITER = ", ";
        final String ENTITY_FORMAT = "(id={0})";

        StringBuffer sb = new StringBuffer("{");

        PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(this);
        PropertyDescriptor property = null;
        int i = 0;
        while ( i < properties.length) {

            property = properties[i];
            sb.append(property.getName());
            sb.append(EQUALS);

            try {
                Object value = PropertyUtils.getProperty(this, property.getName());
                if (value instanceof BaseEntity) {
                    BaseEntity entityValue = (BaseEntity) value;
                    String objectValueString = MessageFormat.format(ENTITY_FORMAT, entityValue.getId());
                    sb.append(objectValueString);
                } else {
                    sb.append(value);
                }
            } catch (IllegalAccessException e) {
                // do nothing
            } catch (InvocationTargetException e) {
                // do nothing
            } catch (NoSuchMethodException e) {
                // do nothing
            }

            i++;
            if (i < properties.length) {
                sb.append(DELIMITER);
            }
        }

        sb.append("}");

        return sb.toString();
    }
}
Matt Sidesinger
  • 2,124
  • 1
  • 22
  • 18
  • Excuse me, but why are you using a while loop for what seems to fit more in a for loop? – Hosam Aly Jan 29 '09 at 15:42
  • Yes, I could have done something like: for (i = 0; i < properties.length; i++) { but if you look at the bottom, after the i++, I use i after it has been incremented. Perhaps there is another way, but this is perfectly readable. – Matt Sidesinger Feb 06 '09 at 02:23
  • It's been a while since this question, but nevertheless, there's a bug in the above equals() method, which can create problems with proxies: instead of getClass() != obj.getClass() use either isAssignableFrom() or !(obj instanceof BaseEntity) – javashlook Mar 18 '09 at 15:39
  • 1
    We've been using the lombok @EqualsAndHashCode annotation on our entities. This has been much less error prone than writing them ourselves. – Casey Watson Jan 09 '13 at 02:59
1

I wouldn't hesitate to use a common base class, after all that's the point of O/R mapping.

I use common base classes, too, but only if the entities share at least some common properties. I won't use it, if the ID is the only common property. Until now I did not encounter any problems.

Daniel Rikowski
  • 71,375
  • 57
  • 251
  • 329
0

It works well for me too.

Notice that you can also in this entity add some event listeners / interceptors like the Hibernate Envers one or any custom one according to your need so that you can: - Track all modifications - Know which user made the last modification - Update automatically the last modification - Set automatically the first insertion date And ther stuff like that...

Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419
-1

You can find some samples here

http://blogsprajeesh.blogspot.com/2010/01/nhibernate-defining-mappings-part-4.html