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.