Training, Workshops, Softwareentwicklung

Hibernate Tutorial

Unidirektionales OneToMany

Bei einer unidirektionalen OneToMany Beziehung bildet man nur im Customer die Beziehung zu den Invoices ab. Gleich zu Anfang: Das ist nicht optimal, denn Hibernate kann das nicht 100% perfekt abbilden. Wir werden sehen, dass die bidirektionale Abbildung besser funktioniert.

Trotzdem ist es das einfachere Mapping und steht deswegen hier am Anfang.

Das Datenbankschema ist in der Einleitung zu diesem Kapitel beschrieben.

Das unidirektionale Mapping in den Entities sieht so aus:

Mapping
@Entity
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "cust_gen")
    @SequenceGenerator(name = "cust_gen", sequenceName = "customer_seq", allocationSize = 1)
    private Long id;

    // getter/setter omitted for brevity

    @OneToMany(cascade = CascadeType.ALL) (2)
    @JoinColumn(name = "customer_id") (3)
    private Set<Invoice> invoices = new HashSet<>(); (1)
1 Wir arbeiten mit einem java.util.Set, das kommt der Datenbankbeziehung am nächsten und funktioniert auch wesentlich performanter als eine List
2 @OneToMany bestimmt das Mapping. cascade steuert welche Operationen vom Customer auf die Invoice kaskadiert werden sollen.
3 Bei einer unidirektionalen Beziehung müssen wir den Namen der Foreign-Key Spalte auf der Gegenseite hier mit @JoinColumn festlegen.

Der Nachteil am unidirektionalen Mapping zeit sich beim Speichern von Objekten:

Test
doInHibernate(em -> {

    Customer customer = new Customer();
    customer.setFirstname("Buck");
    customer.setLastname("Rogers");

    Invoice invoice = new Invoice();
    invoice.setInvoiceNo("1234");

    customer.getInvoices().add(invoice);

    withTransaction(em, () ->
            em.persist(customer)
    );

    withTransaction(em, () ->
            em.remove(customer)
    );
});


assertSelectCount(0);
assertInsertCount(2);
assertUpdateCount(2); (1)
assertDeleteCount(2);
1 Zusätzlich zu den Inserts benötigt Hibernate zwei Updates um die Beziehung zu speichern bzw. wieder zu löschen.
Query:["insert into Customer (firstname, lastname, id) values (?, ?, ?)"], Params:[(Buck,Rogers,5)]
Query:["insert into Invoice (invoiceNo, id) values (?, ?)"], Params:[(1234,5)]
Query:["update Invoice set customer_id=? where id=?"], Params:[(5,5)]

Beim unidirektionalen Mapping schafft Hibernate es nicht, den Foreign-Key direkt beim Insert mit zu speichern. Hier schafft leider nur das bidirektionale Mapping Abhilfe.