web-dev-qa-db-fra.com

Impossible de rendre la relation @ManyToOne nullable

J'ai une relation plusieurs-à-un que je veux être nullable:

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

Malheureusement, JPA continue à définir la colonne de ma base sur NOT NULL. Quelqu'un peut-il expliquer cela? Y a-t-il un moyen de le faire fonctionner? Notez que j'utilise JBoss 7, JPA 2.0 avec Hibernate comme fournisseur de persistance et une base de données PostgreSQL 9.1.

MODIFIER:

J'ai trouvé la cause de mon problème. Apparemment, cela est dû à la façon dont j'ai défini la clé primaire dans l'entité référencée Customer:

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

Il semble que l'utilisation de @Column(columnDefinition="serial") pour la clé primaire définisse automatiquement les clés étrangères la référant à NOT NULL dans la base de données. Est-ce vraiment le comportement attendu lorsque vous spécifiez le type de colonne sous la forme serial? Existe-t-il une solution de contournement pour activer les clés étrangères nullables dans ce cas?

Merci d'avance.

16
vcattin

J'ai trouvé la solution à mon problème. La définition de la clé primaire dans l'entité Customer convient, le problème réside dans la déclaration de la clé étrangère. Il devrait être déclaré comme ceci:

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

En effet, si l'attribut columnDefinition="integer" est omis, la clé étrangère sera définie par défaut comme colonne source: une série non nulle avec sa propre séquence. Ce n'est évidemment pas ce que nous souhaitons, car nous souhaitons simplement que l'identifiant auto-incrémenté soit référencé, et non pour en créer un nouveau.

De plus, il semble que l'attribut name=customer_id soit également requis, comme je l'ai observé lors de certains tests. Sinon, la colonne de clé étrangère sera toujours définie comme colonne source. C'est un comportement étrange à mon avis. Des commentaires ou des informations supplémentaires pour clarifier ceci sont les bienvenus!

Enfin, l’avantage de cette solution est que l’ID est généré par la base de données (et non par JPA). Nous n’avons donc pas à nous en préoccuper lors de l’insertion manuelle ou par l’intermédiaire de scripts, ce qui se produit souvent lors de la migration ou de la maintenance des données.

11
vcattin

Je suis tombé sur ce problème mais j'ai pu le résoudre de la manière suivante:

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

Peut-être que le problème est né de la déclaration de @ManyToOne(optional = true)

7
oschlueter

C'est très bizarre.

Dans JPA, le paramètre nullable est true par défaut. J'utilise ce genre de configuration tout le temps et cela fonctionne bien. Si vous essayez de sauvegarder une entité, cela devrait réussir.

Avez-vous essayé de supprimer la table créée pour cette relation? Peut-être que vous avez une table héritée avec cette colonne?

Ou peut-être devriez-vous essayer de trouver une solution sur d'autres morceaux de code, car il s'agit d'une configuration appropriée.

Note: J'ai essayé cette configuration sur PostgreSQL avec JPA2 et Hibernate.

MODIFIER

Dans ce cas, vous pouvez peut-être essayer une définition légèrement différente de la clé primaire.

Par exemple, vous pouvez utiliser une définition comme celle-ci:

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

et postgresql va générer

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

Si cela suffit, vous pouvez essayer cette solution. 

0
pedjaradenkovic

dans la transaction mais avant l'opération de sauvegarde, définissez explicitement la valeur de la colonne de clé étrangère sur null. Par cette veille prolongée, n'effectuez jamais de requêtes de sélection pour cette table liée à la clé étrangère et ne lève pas l'exception "enregistrer l'instance transitoire avant le vidage". si vous souhaitez définir "valeur nulle" de manière conditionnelle, exécutez ensuite 1. l'extraction et définissez la valeur à l'aide de l'appel repo get/find 2. puis vérifiez la valeur extraite pour la condition et définissez-la sur null en conséquence. passez le code ci-dessous qui est testé et trouvé de travail

// Début de la transaction 
 
 Facultatif <Client> customerObject = customerRepository.findByCustomerId (customer.getCustomerId ()) 
 
 If (customerObject.isPresent ()) yourEnclosingEntityObject. setCustomer (customerObject)} 
 
 else {votreEnclosingEntityObject.setCustomer (null)} 
 
 votreEnclosingEntityObjectRepository.save (votreEnclosingEntityObject) 
. // Fin de la transaction
0
stackoverflow