Angenommen, Sie müssen Bestellung in einer SQL-Datenbank ablegen. Eine Bestellung enthält Kopfdaten (Adresse, Datum , Nummer etc.) sowie eine variable Anzahl Zeilen (Menge, Artikel, Preis, Steuer, …). Das Ganze soll in zwei Tabellen abgelegt werden.

Table Order (
        orId Integer,
        [...]
)    

Table OrderLine (
        olId Integer,
        olOrderId Integer,
        [...]
)  

Um nicht mit SQL direkt arbeiten zu müssen, setzen Sie JPA ein. Zwei Entitätsklassen, die mit den passenden Annotationen versehen sind.

@Entity
@Table(name = "Order")
public class Order implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "orId")
    private Integer _id;
    [...]
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "olOrderId", referencedColumnName="olId")
    private List<OrderFeature> _features;

@Entity
@Table(name = "OrderLine")
public class OrderLine implements Serializable{
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "olId")
    private Integer _id;
    [...]

Eine Bestellung muss ihre Zeilen kennen, eine Zeile aber nicht unbedingt ihre Bestellung. In der Klasse Order wird daher eine Liste der Zeilen vorgehalten, die Zeilen besitzen keinen Verweis auf die Bestellung. Eine klassische unidirektionale OneToMany-Beziehung. In der Datenbank wird dies genau anders herum abgelegt: Hier erhält die Zeile einen Fremdschlüssel, der auf die Bestellung verweist. So weit so gut, und im Prinzip in diversen Büchern zu JPA2 oder JEE6 nachlesbar. Und wenn Sie dann JPA die Datenbank anlegen lassen, funktioniert es wunderbar.

Die hier angegebenen Spaltennamen deuten aber an: Es handelt sich um eine bestehende Datenbank, die einem speziellen Namensschema folgt, vorgegeben von DB-Admin, der auch die DB in gewohnter Manier angelegt hat (ok, in diesem Beispiel hab ich es selbst gemacht). Und nichts geht. Warum nur?

Eine Bestellzeile kann nicht ohne Bestellung existieren. Also muss olOrderId immer auf ein orId verweisen. Und wie es sich gehört, ist dieses Feld in der bestehenden DB als NOT NULL deklariert. JPA aber speichert die Zeilen mit zwei Zugriffen: Erst wird die Bestellung mit allen Zeilen gespeichert. Dann holt das System die neu vergebene Id und aktualisiert damit die Zeilen. Bei Erzeugen der Zeilen ist die Id noch unbekannt und bleibt somit NULL.

Lösung: Den DBAdmin überzeugen, dass olOrderID den Wert NULL aufnehmen darf.