Introduction
Les relations super complexes vont nous permettre de rendre notre BDD plus complète. On va pouvoir ajouter des roues, un volant… à notre camion. JPA va vous simplifier la vie, vous manipulerez des objets Java, que vous pourrez enregistrer votre BDD SQL. Pour toutes les classes que vous faites, vous pouvez créer un repository, mais ce n’est pas nécessaire. Dans nos exemples aujourd’hui, nous ne ferrons pas nécessairement un repository. Le fait d’enregistrer l’objet Camion, enregistrera les objets liés. D’où la nécessité notamment d’avoir un DTO et un modèle, on peut alors filtrer les objets que l’on souhaite modifier ou non.
OneToOne
Ici nous allons voir comment mettre en place une relation OneToOne. La relation OneToOne va vous permettre d’avoir un volant lié à votre camion. On donnera des caractéristiques à ce volant, qui seront indépendantes du camion. Le volant pourra être enlevé de ce camion et mis sur un autre camion. Voici les classes TruckModel et WheelSteeringModel (dont j’ai enlevé les getters et setters) :
package com.raphlys.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
@Entity(name = "truck")
public class TruckModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String brand;
@Column(unique = true)
private String name;
@OneToOne( optional = false)
@JoinColumn(name = "wheel_id")
private WheelSteeringModel wheelSteering;
...
}
package com.raphlys.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
@Entity()
public class WheelSteeringModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String brand;
@Column(unique = true)
private String size;
@OneToOne(mappedBy = "wheelSteering")
private TruckModel truck;
...
}
Voilà ce qui nous intéresse :
@OneToOne( optional = false)
@JoinColumn(name = "wheel_id")
private WheelSteeringModel wheelSteering;
L’annotation @OneToOne( optional = false) indique que l’on a une relation un pour un. Donc pour un camion, il y a un volant, on indique aussi avec optional = false
que l’on a forcément un volant lié au camion.
L’annotation @JoinColumn(name = « wheel_id ») permet d’indiquer la colonne qui permet de faire la liaison entre les deux tables. Ici en l’occurrence wheel_id
est la colonne de la table truck
, elle contient l’id de la table wheel_sterring_model
. Bien entendu les informations de la table wheel_sterring_model
seront mises dans l’attribut wheelSteering
.
@OneToOne(mappedBy = "wheelSteering")
private TruckModel truck;
Ici nous n’avons que l’annotation @OneToOne(mappedBy = "wheelSteering")
, car nous avons décidé de laisser la clé étrangère dans l’autre table. Cette annotation indique que nous avons une relation un pour un et qu’elle fait référence à l’attribut wheelSteering
de l’autre classe.
Ces lignes ne sont pas obligatoires, elles permettent d’accéder au camion à partir du volant. Mais si ce n’est pas nécessaire pour votre application vous pouvez ne pas les mettre.
ManyToOne et OneToMany
À l’instart de la relation OneToOne qui peut être mise dans les deux classes concernées, si l’on met une relation ManyToOne à une classe, on peut mettre une relation OneToMany dans l’autre classe. Pour notre exemple, il y aura les deux. Voici les classes concernées :
package com.raphlys.model;
import java.util.List;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
@Entity(name = "truck")
public class TruckModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String brand;
@Column(unique = true)
private String name;
@OneToOne( optional = false)
@JoinColumn(name = "wheel_id")
private WheelSteeringModel wheelSteering;
@OneToMany(mappedBy = "truck")
private List<WheelModel> wheels;
...
}
package com.raphlys.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
@Entity()
public class WheelModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String brand;
@Column(unique = true)
private String size;
@ManyToOne
@JoinColumn(name = "truck_id")
private TruckModel truck;
...
}
On s’attarde sur les lignes de code suivantes :
@OneToMany(mappedBy = "truck")
private List<WheelModel> wheels;
Donc l’annocation @OneToMany(mappedBy = « truck »), indique que pour un élément de la classe dans laquelle on est (TruckModel), on a plusieurs élèment de l’autre classe (WheelModel). Avec mappedBy, on indique que l’attribut dans l’autre classe est « truck ».
Bien entendu on met une liste (ou une collection) de WheelModel et pas juste un attribut.
@ManyToOne
@JoinColumn(name = "truck_id")
private TruckModel truck;
@ManyToOne indique qu’il y a plusieurs objet de la classe concernée (WheelModel) pour un objet de la classe ciblée (TruckModel).
@JoinColumn(name = « truck_id ») est forcément avec ManyToOne et pas OneToMany, j’imagine, j’espère que vous comprennez que si l’on mettez de l’autre coté la colonne on ne pourrait pas mettre plusieurs clés étrangères. Comme tout à l’heure « truck_id » indique le nom de la clonne dans la table.
Vous pouvez maintenant faire des relations ManyToOne et OneToMany. Si vous ne mettez pas un coté où l’autre, cela fonctionnera aussi, mais vous n’aurez pas le contrôle du nom de la colonne dans table wheel_model.
ManyToMany
C’est la relation la plus complexe que vous puissiez avoir ici. Elle va vous permettre d’associé plusieurs éléments à plusieurs éléments d’une autre classe. Cela oblige JPA a créé une table de jointure contenant les deux clés étrangères. Passons directement au code :
package com.raphlys.model;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
@Entity
public class DriverModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@ManyToMany
@JoinTable(
name = "driver_truck",
joinColumns = @JoinColumn(name = "driver_id"),
inverseJoinColumns = @JoinColumn(name = "truck_id")
)
private List<TruckModel> trucks;
package com.raphlys.model;
import java.util.List;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
@Entity(name = "truck")
public class TruckModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String brand;
@Column(unique = true)
private String name;
@OneToOne(optional = false)
@JoinColumn(name = "wheel_id")
private WheelSteeringModel wheelSteering;
@OneToMany(mappedBy = "truck")
private List<WheelModel> wheels;
@ManyToMany(mappedBy = "trucks")
private List<DriverModel> drivers;
Comme tout à l’heure on s’attarde sur les lignes qui nous intéressent :
@ManyToMany(mappedBy = "trucks")
private List<DriverModel> drivers;
Ici on a exactement le même raisonnement qu’avec le OneToMany, @ManyToMany indique notre relation est de plusieurs à plusieurs, mappedBy permet d’indiquer l’attribut dans l’autre classe. Et enfin met une List, car il y aura plusieurs élèments.
@ManyToMany
@JoinTable(
name = "driver_truck",
joinColumns = @JoinColumn(name = "driver_id"),
inverseJoinColumns = @JoinColumn(name = "truck_id")
)
private List<TruckModel> trucks;
Là, ça devient plus intéressant. @ManyToMany vous savez maintenant à quoi cela sert.
@JoinTable vous invite à configurer la table de jointure. On donne le nom de la table grâce à l’attribut name (ici => driver_truck). On fournis le nom des colonnes pour les clés étrangères avec joinColumns = @JoinColumn(name = « driver_id »), pour la clé étrangère de la table représentant la classe dans laquelle nous nous trouvons et inverseJoinColumns = @JoinColumn(name = « truck_id ») pour la clé étrangère de l’autre table. On aura donc une clé étrangère comme celle ci :
----------------
| driver_truck |
----------------
| driver_id fk |
| truck_id fk |
----------------
Ensuite, vous pouvez transposer ces modifications dans votre Dto. Je vous laisse faire les classes Dtos je vous présente juste la modification que j’ai faites sur ma classe TruckModel.
package com.raphlys.dto;
import java.util.List;
public class TruckDto {
// Identifiant unique du camion
private Long id;
// Marque du camion
private String brand;
// Nom du camion
private String name;
private WheelSteeringDto wheelSteering;
private List<WheelDto> wheels;
private List<DriverDto> drivers;
...
}
Vous pouvez maintenant essayer de mettre vos modèles dans vos Dtos. Nous, nous verrons ça la semaine prochaine. Ça sera l’occasion de vous proposer les services spécialisés dans la conversion, avec quelques possiblités d’optimisation en rendant vos classes plus générique…
Vous n’avez pas finis de Pousser JPA dans ses retranchements
Laisser un commentaire