Witam,
Piszę program do wystawiania faktur - desktopowa aplikacja w Javie SE, baza w MySQL. Wszystko byłoby dobrze, gdyby nie kwestia mapowania kolekcji zawartych w fakturze - chodzi o listy poszczególnych pozycji i podsumowań dokumentu.
Kolekcje są dodawane do obiektu faktury w metodzie addInvoice():
public void addInvoice() {
Invoice invoice = this.invoiceDataBuilder.build();
this.saveRelatedEntities(invoice);
invoice.setInvoicePositions(this.positions);
invoice.setInvoiceSummations(this.getInvoiceSummations());
this.hibernateManager.save(invoice);
}
która następnie przekazuje fakturę do obiektu zarządzającego Hibernate:
public void save(Invoice invoice) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
session.save(invoice);
transaction.commit();
session.close();
}
Oto fragment pliku Invoice.hbm.xml:
<list name="invoiceSummations" cascade="all" fetch="subselect" inverse="true">
<key column="Invoice_id" not-null="true"/>
<list-index column="Position" base="1"/>
<one-to-many class="account.domain.document.InvoiceSummation"/>
</list>
<list name="invoicePositions" cascade="all" fetch="subselect" inverse="true">
<key column="Invoice_id" not-null="true"/>
<list-index column="Position" base="1"/>
<one-to-many class="account.domain.document.InvoicePosition"/>
</list>
Jest to relacja dwukierunkowa - faktura zawiera kolekcje pozycji (InvoicePosition) i podsumowań (InvoiceSummation), zaś obie te klasy mają zmienną faktury (Invoice), która powinna przechowywać referencję do faktury-rodzica. Niestety, zastosowanie zwykłego kodu settera sprawia, że zmienna invoice w podsumowaniach i pozycjach faktury ma wartość null i protestuje przy zapisie do bazy. Początkowo myślałem, że Hibernate z automatu przydziela tę referencję w momencie wygenerowania id zapisanej w bazie faktury i nie trzeba się o to martwić...
Zwykły setter wewnątrz klasy Invoice.java:
public void setInvoiceSummations(List invoiceSummations) {
this.invoiceSummations = invoiceSummations;
}
Błąd: Exception in thread "AWT-EventQueue-0" org.hibernate.PropertyValueException: not-null property references a null or transient value: account.domain.document.InvoiceSummation.invoice
Dlatego postanowiłem nieco zmodyfikować settera, by za pomocą pętli foreach zmusić elementy kolekcji do przyjęcia referencji parenta.
Zmodyfikowany setter wewnątrz klasy Invoice.java:
public void setInvoiceSummations(List invoiceSummations) {
this.invoiceSummations = invoiceSummations;
for(InvoiceSummation summation : this.invoiceSummations) {
summation.setInvoice(this);
}
}
Wtedy jednak pojawia się inny błąd: SEVERE: Field 'Position' doesn't have a default value Exception in thread "AWT-EventQueue-0" org.hibernate.exception.GenericJDBCException: could not insert: [account.domain.document.InvoiceSummation]
Chodzi o pole będące indeksem listy (<list-index column="Position" base="1" />), które nie dość, że za Chiny Ludowe nie chce się inkrementować, to jeszcze domaga się wartości domyślnej, choć przecież została ona zdefiniowana w pliku mapującym (base). I tu powstaje pytanie - jak obchodzić się z list-index? Czy należy dlań stworzyć osobne pole w klasach InvoiceSummation/InvoicePosition i samemu zająć się jego inkrementacją? Czy potrzebuje ono osobnego węzła w plikach mapujących obydwie klasy?