Zuordnen einer einzelnen Entität zu mehreren Tabellen
mittels JPA Secondary Table Annotation
In diesem Blogeintrag soll mittels eines kleinen Beispiel gezeigt werden, wie einfach es ist die JPA Secondary Table Annontation zu verwenden, um Daten, die in mehreren Tabellen gespeichert sind, zu einer einzelnen Enität zu mappen.
Dafür habe ich eine kleine Gradle-basiertes Projekt mit Sprint-Boot erstellt, das eine H2 In-Memory Datenbank verwendet. Der ganze Code ist unter GitHub ersichtlich.
Wofür verwendet man die Secondary Table Annoation?
Wie bereits erwähnt um um Daten, die in mehreren Tabellen gespeichert sind, zu einer einzelnen Enität zu mappen. Die Anwendungsfälle sind vielfältig, jedoch ein oft vorkommender Anwendungsfall ist, wenn man es mit einer Legacy Datenbank zu tun hat.
JPA Secondary Table Annoation anhand eines Beispiels Daten Modell
Für dieses Beispiel gehen wir von folgendem Daten-Modell aus:
In der Tabelle customer werden nur die Namen der Kunden gespeichert und in der Tabelle customer_details befinden sich die zusätzliche Details. Um diese zusätzlichen Information zu finden, benötigt man die id des Kunden, weil sie in der Tabelle customer_details als Primary Key customer_id gespeichert wurde.
Zuerst definieren in der @SecondaryTable mit name von welcher Tabelle wir diese Informationen bekommen und mit @PrimaryKeyJoinColumn spezifizieren wir einen PrimaryKey Column, die als Foreign Key verwendet wird, um mit der anderen Tabelle zu joinen. Zusätzlich definieren wir in der @Column Annotation bei den aus der Tabelle customer_details übernommenen Felder die Tabelle.
Repository, RestController, Service
Zusätzlich implementieren wir ein CustomerRepo, ein CustomerRestController und ein CustomerService.
Im CustomerRestController.java haben wir zwei RestHandler erstellt:
Beim Save-RestHandler übergeben wir der einfachheitshalber alles als RequestParam anstatt diese Daten mittels eines RequestBodyzu schicken.
Wie man im CustomerService verwenden wir nur die Customer Enität für die GET- sowie POST-Requests:
@Service
public class CustomerService {
@Autowired
private CustomerRepo customerRepo;
public List<Customer> getAll() {
return customerRepo.findAll();
}
public Customer saveCustomer(String firstName, String lastName, String customerNumber) {
Customer cust = new Customer();
cust.setFirstName(firstName);
cust.setLastName(lastName);
cust.setCustomerNumber(customerNumber);
cust.setJoinDate(Instant.now());
return customerRepo.save(cust);
}
}
Probieren wir es aus!
Nachdem wir alles implementiert haben, testen wir die Request und schauen, ob alles funktioniert wie gehofft:
GET-Request
Zuerst probieren wir den GET-Request aus. Dafür befüllen wir die Datenbank mit TestDaten (insert_data.sql).
SELECT * FROM CUSTOMER;
SELECT * FROM CUSTOMER_DETAILS;
Nachdem die Tabellen befüllt sind, können wir den GET-Request ausführen: http://localhost:8080/rest/customer/getAll
Wie wir sehen, bekommen wir wirklich Daten aus beiden Tabellen, obwohl wir dafür nur eine Enität verwenden.
POST-Request
Da der GET-Request funktioniert hat, probieren wir gleich den POST-Request aus: http://localhost:8080/rest/customer/save?firstName=Magda&lastName=Ciit&customerNumber=Ciit-Blog-1
Wir bekommen das folgende JSON zurück:
Jetzt schauen wir uns die Tabellen an, ob diese richtig befüllt worden sind:
SELECT * FROM CUSTOMER;
SELECT * FROM CUSTOMER_DETAILS;
Conslusio
Es ist möglich mit der JPA Secondary Table Annotation mehrere Tabellen zu einer einzigen Enität zu mappen und das ganz einfach. Zusätzlich sorgt diese Anotation dafür, dass in den beiden Tabellen für jeden Kunden ein Eintrag vorhanden ist.
Um die Annotation zu verwenden, ist es jedoch wichtig, dass die Tabellen die gleichen ids haben!