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?