Аннотация @MappedSuperclass

Аннотация @MappedSuperclass позволяет реализовать наследование Entity классов только в модели предметной области без отражения ее в схеме базы данных. @MappedSuperclass определяет класс - предок для Entity классов, описанные и аннотированные свойства и методы которого будут применены в классах-наследниках. Описанный супер класс не отображается на отдельную таблицу.

Данная аннотация используется для классов, которые не описывают сущности как таковые. Задача состоит в следующем. Сущности (Entity) нашего проекта должны обладать какими-то общими свойствами и методами. В то же время, если исходить из понимания объектов самой базы данных, эти объекты не должны быть связаны между собой. Для модели базы это будут совершенно разные объекты ни связанные между собой.

Для примера создадим класс, который является предком (суперклассом) для всех сущностей в проекте и аннотируем его @MappedSuperclass:

package info.deskbook.hibernate.entity;

import java.io.Serializable;
import javax.persistence.MappedSuperclass;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Column;

@MappedSuperclass
public abstract class BaseEntity implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name="ID")
	private Long id;
	
	public BaseEntity() {
		super();
	}
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@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 (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	@Override  
	public String toString() {
		String result;
		if (id == null) {
			result = this.getClass().getName() + " - [ NEW ]";
		}
		else {
			result = this.getClass().getName() + " - [ID=" + id + "]";
		}
        return result;  
    }  	
}

Создание такого класса-предка зачастую оправдано тем, что мы заранее определяем ряд свойств и методов, которые должны быть определены в сущностях (Entity-классах). В данном конкретном случае, в BaseEntity реализован необходимый минимум свойств и методов для любого Entity-класса:

  • Определен идентификатор класса id и реализованы методы getId() и setId() для работы с идентификатором.
  • Переопределены методы equals() и hashCode(), которые необходимо в любом случае переопределять для любого класса Entity. В классах-наследниках нет необходимости их переопределять без особой необходимости.
  • Переопределен метод toString(), для вывода информации об объекте.

Для класса BaseEntity не будет создана собственная таблица. Также нельзя создать и экземпляр класса, так как класс является абстрактным.

Создадим два класса, описывающие совершенно различные сущности:

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Entity;

@Entity
public class Person extends BaseEntity implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	private String lastName;
	private String firstName;
	private Date birthday;
	
	public Person() {
		super();
	}

	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
}

 

import java.io.Serializable;
import javax.persistence.Entity;

@Entity
public class Currency extends BaseEntity implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String name;

	public Currency() {
		super();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

В результате созданы совершенно разные сущности (Entity-классы), каждая из которых будет сопоставлена со своей собственной таблицей в базе данных. Поле ID будет создано в каждой таблице и иметь совершенно независимые значения друг от друга. Переопределенные методы в классе BaseEntity нет необходимости переопределять в классах-наследниках. Кроме того, использование такого подхода позволило сократить количество кода.