Jak lépe na abstract entity v JPA

Absolvoval jsem svůj doposud nejlepší technický pohovor (ne, v Google přijímací pohovor rozhodně lepší nebyl). Nikdo nedělal ramena s asymptotickou složitostí apod. Ba právě naopak to bylo velmi inspirující. Kromě toho, že jsem pochytil takové drobnosti jako unixový příkaz tree, jsem se hlavně přiučil, jak psát lépe abstract entity v JPA. SoftWare Samuraj měl pochopitelně k mému kódu zvídavé otázky a podnětné připomínky, které mě přiměly se na kód znovu podívat a zamyslet se.
Měl jsem abstraktní entitu s abstraktním getterem a setterem pro primární klíč, ale bez samotné deklarace fieldu, protože v každé tabulce se sloupec jmenoval jinak.
import javax.persistence.MappedSuperclass; | |
@MappedSuperclass | |
public abstract class AbstractEntity<PK> { | |
public abstract PK getId(); | |
public abstract void setId(PK id); | |
@Override | |
public int hashCode() { | |
if (getId() != null) { | |
return getId().hashCode(); | |
} | |
return super.hashCode(); | |
} | |
@Override | |
public boolean equals(Object obj) { | |
if (this == obj) { | |
return true; | |
} | |
if (obj == null) { | |
return false; | |
} | |
if (getClass() != obj.getClass()) { | |
return false; | |
} | |
AbstractEntity<?> other = (AbstractEntity<?>) obj; | |
if (getId() == null || other.getId() == null) { | |
return false; | |
} | |
if (!getId().equals(other.getId())) { | |
return false; | |
} | |
return true; | |
} | |
} |
import javax.persistence.*; | |
@Entity | |
public class User extends AbstractEntity<Long> { | |
@Id | |
@SequenceGenerator(name = "userSequence", sequenceName="SQ_USERS") | |
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "userSequence") | |
@Column(name = "USR_ID") | |
private Long id; | |
@Column(name = "USR_NAME", nullable = false) | |
private String name; | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
@Override | |
public Long getId() { | |
return id; | |
} | |
@Override | |
public void setId(Long id) { | |
this.id = id; | |
} | |
} |
Samotné mapování lze překrýt pomocí anotace AttributeOverride. Zbývá ještě dořešit to, že každá entita má vlastní databázovou sekvenci. Ovšem i to lze nastavit.
import javax.persistence.GeneratedValue; | |
import javax.persistence.GenerationType; | |
import javax.persistence.Id; | |
import javax.persistence.MappedSuperclass; | |
import java.io.Serializable; | |
@MappedSuperclass | |
public abstract class AbstractEntity<PK extends Serializable> { | |
public static final String GENERATOR = "customSequence"; | |
@Id | |
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = GENERATOR) | |
private PK id; | |
public PK getId() { | |
return id; | |
} | |
public void setId(PK id) { | |
this.id = id; | |
} | |
@Override | |
public int hashCode() { | |
if (getId() != null) { | |
return getId().hashCode(); | |
} | |
return super.hashCode(); | |
} | |
@Override | |
public boolean equals(Object obj) { | |
if (this == obj) { | |
return true; | |
} | |
if (obj == null) { | |
return false; | |
} | |
if (getClass() != obj.getClass()) { | |
return false; | |
} | |
AbstractEntity<?> other = (AbstractEntity<?>) obj; | |
if (getId() == null || other.getId() == null) { | |
return false; | |
} | |
if (!getId().equals(other.getId())) { | |
return false; | |
} | |
return true; | |
} | |
} |
import javax.persistence.*; | |
@Entity | |
@SequenceGenerator(name = AbstractEntity.GENERATOR, sequenceName="SQ_USERS") | |
@AttributeOverride(name="id", column=@Column(name = "USER_ID")) | |
public class User extends AbstractEntity<Long> { | |
@Column(name = "USR_NAME", nullable = false) | |
private String name; | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
} |
Řekněte sami, není to takhle hezčí?
Ještě doplním, že více AttributOverride je možné sdružit do AttributeOverrides. K překrytí asociací analogicky použijete AssociationOverride a AttributeOverrides.