it-swarm.com.de

@ManyToOne-Beziehung kann nicht auf null gesetzt werden

Ich habe eine Viele-zu-Eins-Beziehung, die ich nullfähig machen möchte:

@ManyToOne(optional = true)
@JoinColumn(name = "customer_id", nullable = true)
private Customer customer;

Leider setzt JPA die Spalte in meiner Datenbank immer auf NOT NULL. Kann jemand das erklären? Gibt es eine Möglichkeit, damit es funktioniert? Beachten Sie, dass ich JBoss 7, JPA 2.0 mit Hibernate als Persistenzanbieter und eine PostgreSQL 9.1-Datenbank verwende.

EDIT:

Ich habe die Ursache meines Problems gefunden. Anscheinend liegt es an der Art und Weise, wie ich den Primärschlüssel in der referenzierten Entität Customer definiert habe:

@Entity
@Table
public class Customer { 
    @Id
    @GeneratedValue
    @Column(columnDefinition="serial")
    private int id;
}

Es scheint, dass die Verwendung von @Column(columnDefinition="serial") für den Primärschlüssel die Fremdschlüssel, die auf ihn verweisen, automatisch auf NOT NULL in der Datenbank setzt. Ist das wirklich das erwartete Verhalten, wenn der Spaltentyp als serial angegeben wird? Gibt es eine Problemumgehung, um in diesem Fall nullfähige Fremdschlüssel zu aktivieren?

Danke im Voraus.

16
vcattin

Ich habe die Lösung für mein Problem gefunden. Die Art und Weise, wie der Primärschlüssel in Entität Customer definiert wird, ist in Ordnung. Das Problem liegt in der Fremdschlüsseldeklaration. Es sollte so erklärt werden:

@ManyToOne
@JoinColumn(columnDefinition="integer", name="customer_id")
private Customer customer;

Wenn das Attribut columnDefinition="integer" nicht angegeben wird, wird der Fremdschlüssel standardmäßig als Quellenspalte festgelegt: eine nicht-null-Seriennummer mit eigener Sequenz. Das ist natürlich nicht das, was wir wollen, da wir nur wollen, dass die automatisch inkrementierte ID referenziert wird und nicht eine neue erstellt wird.

Es scheint außerdem, dass das Attribut name=customer_id auch erforderlich ist, wie ich bei einigen Tests festgestellt habe. Andernfalls wird die Fremdschlüsselspalte weiterhin als Quellenspalte festgelegt. Dies ist meiner Meinung nach ein merkwürdiges Verhalten. Kommentare oder zusätzliche Informationen, um dies zu verdeutlichen, sind willkommen!

Der Vorteil dieser Lösung besteht schließlich darin, dass die ID von der Datenbank (nicht von JPA) generiert wird und wir uns daher keine Gedanken darüber machen müssen, wenn Daten manuell oder mithilfe von Skripts eingefügt werden, was häufig bei der Datenmigration oder -wartung der Fall ist.

11
vcattin

Ich bin auf dieses Problem gestoßen, konnte es aber so lösen:

@ManyToOne
@JoinColumn(nullable = true)
private Customer customer;

Möglicherweise ist das Problem aus der Deklaration von @ManyToOne(optional = true) entstanden.

7
oschlueter

Das ist sehr komisch.

In JPA ist der nullfähige Parameter standardmäßig true. Ich verwende diese Art der Konfiguration immer und es funktioniert gut. Wenn Sie versuchen, das Objekt zu speichern, sollte es erfolgreich sein.

Haben Sie versucht, eine Tabelle zu löschen, die für diese Beziehung erstellt wurde? Vielleicht haben Sie eine ältere Tabelle mit dieser Spalte?

Oder vielleicht sollten Sie versuchen, eine Lösung für andere Codeabschnitte zu finden, da dies die richtige Konfiguration ist.

Hinweis: Ich habe diese Konfiguration auf PostgreSQL mit JPA2 und Hibernate ausprobiert.

EDIT

In diesem Fall können Sie vielleicht eine etwas andere Definition des Primärschlüssels versuchen.

Zum Beispiel können Sie Definition wie folgt verwenden:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column()
private Long id;

und postgresql wird generiert

id bigint NOT NULL
-- with constraint
CONSTRAINT some_table_pkey PRIMARY KEY (id)

Wenn dies gut genug ist, können Sie diese Lösung ausprobieren. 

0
pedjaradenkovic

setzen Sie den Wert der Fremdschlüsselspalte explizit als null in der Transaktion, aber vor dem Sicherungsvorgang. Führen Sie in diesem Ruhezustand keine Auswahlabfragen für diese fremdschlüsselbezogene Tabelle aus und lösen Sie nicht die Ausnahme "Speichern Sie die transiente Instanz vor dem Leeren" ab. Wenn Sie den "Nullwert" bedingt setzen möchten, führen Sie Folgendes aus: 1. Holen Sie den Wert und rufen Sie ihn mit dem Repo-Aufruf "get/find" auf. 2. Überprüfen Sie dann den abgerufenen Wert für die Bedingung und setzen Sie ihn entsprechend auf "null" und fand Arbeit

// Transaktionsstart 
 
 Optional <customer> customerObject = customerRepository.findByCustomerId (customer.getCustomerId ()) 
 
 If (customerObject.isPresent ()) yourEnclosingEntityObject. setCustomer (customerObject)} 
 
 else {yourEnclosingEntityObject.setCustomer (null)} 
 
 yourEnclosingEntityObjectRepository.save (IhrEnclosingEntityObject) 
. // Transaktionsende
0
stackoverflow