web-dev-qa-db-fra.com

JPA - La persistance d'une relation unidirectionnelle unidirectionnelle échoue avec EclipseLink

J'essaie de conserver une relation unidirectionnelle un à plusieurs très simple, mais EclipseLink (2.3.1) échoue.

Classe de service (parent):

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="service_id")
    public long serviceID;

    @Column(name="name")
    public String name;

    @OneToMany(cascade={CascadeType.ALL})
    @JoinColumn(name="service_id", referencedColumnName="service_id")
    public Set<Parameter> parameters;
}

Classe de paramètre (enfant):
(Il existe bien sûr un champ de clé étrangère "service_id" dans la base de données, qui n'est pas représenté dans la classe, car il s'agit d'une relation unidirectionnelle).

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="param_id")
    public long parameterID;

    @Column(name="name")
    public String name;
}

Et voici le code pour la persistance d'entité:

    Service service = new Service();
    service.parameters = new HashSet<Parameter>();
    service.name = "test";
    Parameter param = new Parameter();
    param.name = "test";
    service.parameters.add(param);
    em.persist(service);
    em.flush();

Je reçois cette exception:

Internal Exception: Java.sql.SQLException: Field 'service_id' doesn't have a default value
Error Code: 1364
Call: INSERT INTO tbl_service_parameters2 (name) VALUES (?)
    bind => [test]

EDIT: Le champ de base de données service_id a (et devrait avoir) une contrainte non nulle, en raison de la nature des données.

Est-ce un bug ou quelque chose ne va pas dans le code? 

23
gamliela

Essayez de supprimer la contrainte non NULL du champ service_id de la table Parameter. Eclipselink met à jour la clé étrangère pour les colonnes de jointure unidirectionnelle 1: m dans une instruction distincte. Vous devez donc désactiver ou retarder le contrôle de contrainte. Le rendre bidirectionnel permettra de mettre à jour le champ fp avec le reste des données de paramètre.

14
Chris

Utilisez nullable = false, sur @JoinColumn:

@JoinColumn(name = "service_id", nullable = false)
28
William Ferreira

J'ai pu le faire fonctionner dans Oracle en utilisant une clé étrangère pouvant être différée.

Exemple:

ALTER TABLE my_table ADD CONSTRAINT my_constraint_name FOREIGN KEY (my_table_column) REFERENCES foreign_key_table  (foreign_key_table_column) DEFERRABLE INITIALLY DEFERRED
1
jc12

Vous pouvez modifier votre persistance pour la version de veille prolongée <4.0 et votre code s'exécutera correctement. 

1
Alexander

Par défaut, nullable est true sur @JoinColumn. Tout en conservant les données dans une relation un à plusieurs, nous devons rendre nullable comme false pour éviter les exceptions de violation de données qui se produisent au moment de l'exécution.

0
SreeNath

Comme je l'ai découvert, dans de tels cas, la clé étrangère est renseignée dans une déclaration distincte. Dans mon exemple, j'ai utilisé une entité Address avec customer_id comme clé étrangère.

2014-07-08T20:51:12.752+0300|FINE: INSERT INTO ADDRESS (address_id, street, city, region) VALUES (?, ?, ?, ?)
    bind => [10, foo, foo, foo]
2014-07-08T20:51:12.753+0300|FINEST: Execute query InsertObjectQuery(ua.test.customer.Address@28cef39d)
2014-07-08T20:51:12.757+0300|FINEST: Execute query DataModifyQuery(sql="UPDATE ADDRESS SET customer_id = ? WHERE (address_id = ?)")
2014-07-08T20:51:12.757+0300|FINE: UPDATE ADDRESS SET customer_id = ? WHERE (address_id = ?)
    bind => [151, 10]

Par conséquent, avoir @JoinColumn avec nullable=true provoque une erreur.

Comme alternative, vous pouvez utiliser @OneToMany (..., orphanRemoval = true, ...).

0
kolobok