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?
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.
Utilisez nullable = false
, sur @JoinColumn
:
@JoinColumn(name = "service_id", nullable = false)
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
Vous pouvez modifier votre persistance pour la version de veille prolongée <4.0 et votre code s'exécutera correctement.
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.
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, ...)
.