Entités JPA et relations DB
Bonne journée, chers collègues !Qu’est-ce que l’entité ?
Une entité est un objet de la vie réelle (par exemple, une voiture) qui possède des attributs (portes, ROUES , moteur). Entité DB : Dans ce cas, notre entité est stockée dans une DB, tout est simple. Pourquoi et comment nous mettons la voiture dans la base de données - nous y reviendrons plus tard.Qu’est-ce que les relations DB ?
Il y a bien longtemps, dans un royaume lointain , une base de données relationnelle a été créée . Dans cette base de données, les données étaient présentées sous forme de tableaux. Mais il était également clair pour l'âne de Shrek qu'il était nécessaire de créer un mécanisme pour relier ces tables entre elles. En conséquence, 4 relations DB sont apparues : Si vous voyez tout cela pour la première fois, je vous préviens encore, ça va empirer : pensez à aller vous promener. Nous analyserons toutes ces relations à l'aide d'un exemple et comprendrons la différence entre elles.Exemple d'horreur
Nous aurons un projet qui aura 5 branches : master, où il y aura une description du projet, et 1 branche pour chaque relation DB. Chaque branche contiendra des scripts SQL pour créer une base de données et la remplir de données de test, ainsi qu'une classe Entity avec mappage d'annotations. Il y aura également un fichier de configuration Hibernate pour chaque branche. J'utiliserai la base de données intégrée H2 pour le projet afin de ne pas être distrait par les aspects individuels de la base de données cloud ou de la base de données externe. En suivant le lien, installez H2 DB sur votre aspirateur. Je décrirai chaque étape dans 1 branche, le reste ne sont que les points clés. À la fin, nous résumerons. Aller. Ceci est un lien vers la branche principale de mon projet.Relation individuelle
Lien vers la succursale ici .-
Nous devons connecter H2 DB à notre projet. Ici, nous devons souligner que nous avons besoin d'Ultimate IDEA pour travailler confortablement avec DB et d'autres choses. Si vous l'avez déjà, accédez directement à la connexion DB. Accédez à l'onglet Base de données et procédez comme dans la capture d'écran :
Ensuite, nous passons aux paramètres de la base de données. Vous pouvez saisir vos données, et même votre SGBD ; je le répète, j'utilise H2 DB par simplicité.
Ensuite, configurons le circuit. Cette étape est facultative mais recommandée si vous avez plusieurs schémas dans la base de données.
Appliquez les paramètres, et au final nous devrions obtenir quelque chose comme ceci :
-
Nous avons créé la base de données et configuré l'accès à celle-ci depuis IDEA. Vous devez maintenant y créer des tableaux et le remplir avec des données. Par exemple, je prendrai deux entités : Auteur et Livre. Un livre peut avoir un auteur, plusieurs auteurs ou ne pas en avoir. Dans cet exemple, nous allons créer tous les types de connexions. Mais à ce stade, c'est une relation individuelle. Créons le script correspondant qui crée les tables DB :
DROP TABLE IF EXISTS PUBLIC.BOOK; CREATE TABLE PUBLIC.BOOK ( ID INTEGER NOT NULL AUTO_INCREMENT, NAME VARCHAR(255) NOT NULL, PRINT_YEAR INTEGER(4) NOT NULL, CONSTRAINT BOOK_PRIMARY_KEY PRIMARY KEY (ID) ); DROP TABLE IF EXISTS PUBLIC.AUTHOR; CREATE TABLE PUBLIC.AUTHOR ( ID INTEGER NOT NULL AUTO_INCREMENT, FIRST_NAME VARCHAR(255) NOT NULL, SECOND_NAME VARCHAR(255) NOT NULL, BOOK_ID INTEGER NOT NULL UNIQUE, CONSTRAINT AUTHOR_PRIMARY_KEY PRIMARY KEY (ID), CONSTRAINT BOOK_FOREIGN_KEY FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID) );
Et exécutons-le :
Résultat de l'exécution dans la console :
Résultat dans la base de données :
-
Regardons un schéma de nos tables. Pour ce faire, RMB sur notre DB :
Résultat:
Sur le diagramme UML nous pouvons voir toutes les clés primaires et étrangères, nous voyons également la connexion entre nos tables.
-
Écrivons un script qui remplit notre base de données avec des données de test :
INSERT INTO PUBLIC.BOOK (NAME, PRINT_YEAR) VALUES ('First book', 2010), ('Second book', 2011), ('Third book', 2012); INSERT INTO PUBLIC.AUTHOR (FIRST_NAME, SECOND_NAME, BOOK_ID) VALUES ('Pablo', 'Lambado', 1), ('Pazo', 'Zopa', 2), ('Lika', 'Vika', 3);
Je veux dire, que se passe-t-il ? Une relation un-à-un est nécessaire lorsque l'entité d'une table est liée à l'entité d'une autre (ou pas du tout liée si NOT NULL est supprimé de BOOK_ID). Dans notre exemple, un livre DOIT avoir un seul auteur. Pas d'autre chemin.
-
Maintenant, la chose la plus intéressante est de savoir comment connecter une classe Java avec des entités DB ? Très simple. Créons deux classes Livre et Auteur. A l'aide d'un exemple, j'analyserai la classe 1 et les domaines clés de la communication. Prenons comme exemple la classe Author :
@Data @Entity @DynamicInsert @DynamicUpdate @Table(name = "AUTHOR") public class Author { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID", nullable = false) private Long id; @Column(name = "FIRST_NAME", nullable = false) private String firstName; @Column(name = "SECOND_NAME", nullable = false) private String secondName; @OneToOne @JoinColumn(name = "BOOK_ID", unique = true, nullable = false) private Book book; }
- Tous les champs de la classe répètent les attributs de l'entité DB.
- @Data (de Lombok ) indique que pour chaque champ, un getter et un setter seront créés, égaux, le hashcode sera remplacé et une méthode toString sera générée.
- @Entity indique que la classe donnée est une entité et est associée à une entité DB.
- @DynamicInsert et @DynamicUpdate indiquent que les insertions et mises à jour dynamiques seront effectuées dans la base de données. Ce sont des paramètres Hibernate plus profonds qui vous seront utiles afin que vous ayez le batch CORRECT.
- @Table (name = "AUTHOR") lie la classe Book à la table DB AUTHOR.
- @Id dit que ce champ est la clé primaire.
- @GeneratedValue (stratégie = GenerationType.IDENTITY) – stratégie de génération de clé primaire.
- @Column (name = "ID", nullable = false) associe un champ à un attribut DB et indique également que le champ DB donné ne peut pas être nul. Ceci est également utile lors de la génération de tables à partir d'entités. Le processus inverse de la façon dont nous créons maintenant notre projet, ceci est nécessaire dans les bases de données de test pour les tests unitaires.
- @OneToOne indique que le champ donné est un champ de relation un-à-un.
- @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - une colonne BOOK_ID sera créée, qui est unique et non nulle.
-
Configurons maintenant Hibernate. Pour ce faire, créez un fichier hibernate.cfg.xml :
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property> <property name="hibernate.connection.driver_class">org.h2.Driver</property> <property name="hibernate.connection.url">jdbc:h2:~/db/onetoone</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password"/> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.use_sql_comments">true</property> <property name="hibernate.generate_statistics">true</property> <property name="hibernate.jdbc.batch_size">50</property> <property name="hibernate.jdbc.fetch_size">50</property> <property name="hibernate.order_inserts">true</property> <property name="hibernate.order_updates">true</property> <property name="hibernate.jdbc.batch_versioned_data">true</property> <mapping class="com.qthegamep.forjavarushpublication2.entity.Book"/> <mapping class="com.qthegamep.forjavarushpublication2.entity.Author"/> </session-factory> </hibernate-configuration>
- hibernate.dialect est le dialecte du SGBD que nous avons choisi.
- hibernate.connection.driver_class — Classe de pilote de notre base de données.
- hibernate.connection.url - utl de notre base de données. Vous pouvez le prendre à partir du premier point où nous avons configuré DB.
- hibernate.connection.username - Nom d'utilisateur de la base de données.
- hibernate.connection.password — Mot de passe de l'utilisateur de base de données.
- hibernate.hbm2ddl.auto - configuration de la génération de tables. S'il est mis à jour, il ne génère pas s'il a déjà été créé, mais le met uniquement à jour.
- hibernate.show_sql - s'il faut afficher les requêtes DB.
- hibernate.format_sql - s'il faut formater les requêtes de base de données. Sinon, ils seront tous sur une seule ligne. Je recommande de l'allumer.
- hibernate.use_sql_comments - commente les requêtes de base de données. S'il s'agit d'un Insert, alors un commentaire est écrit au-dessus de la requête indiquant que la requête est de type Insert.
- hibernate.generate_statistics - génère des journaux. Je recommande et recommande de configurer la journalisation au maximum. La lecture des journaux augmentera vos chances de travailler correctement avec l'ORM.
- hibernate.jdbc.batch_size — Taille maximale du lot.
- hibernate.jdbc.fetch_size — Taille maximale de récupération.
- hibernate.order_inserts - autorise les insertions dynamiques.
- hibernate.order_updates - Permet les mises à jour dynamiques.
- hibernate.jdbc.batch_versioned_data - permet le traitement par lots. Regardez votre SGBD : tout le monde ne le supporte pas.
- classe de mappage - classes qui sont nos entités. Tout doit être répertorié.
-
Maintenant, notre essence doit être déterminée. Nous pouvons vérifier cela dans l'onglet persistance :
Résultat:
-
Nous devons également configurer l'attribution des données :
Résultats : Nous avons réalisé une cartographie individuelle. Le matériel est à titre informatif uniquement, les détails sont dans les références.
Relation un-à-plusieurs
Lien vers la succursale ici . Je ne posterai plus le code dans l'article, car il est déjà trop long. Nous examinons tout le code sur GitHub.-
Suite à l'exécution du script d'initialisation, nous obtenons ce qui suit :
Sentez-vous la différence avec le tableau précédent ?
-
Diagramme:
Relation un-à-plusieurs – un auteur peut avoir plusieurs livres. L'entité de gauche correspond à une ou plusieurs entités de droite.
-
La différence de mappage résidera dans les annotations et les champs :
Un champ apparaît dans la classe Auteur :
@OneToMany(fetch = FetchType.LAZY, mappedBy = "author") private Set<Book> books;
C'est déjà un ensemble, puisqu'on peut avoir plusieurs livres. @OneToMany parle de type d'attitude. FetchType.Lazy dit que nous n'avons pas besoin de charger la liste complète des livres si cela n'est pas spécifié dans la demande. Il faut aussi dire que ce champ NE PEUT PAS être ajouté à toString, sinon nous commencerons à fumer une StackOverflowError. Mon Lombok bien-aimé s'occupe de ça :
@ToString(exclude = "books")
Dans la classe Livre , nous effectuons des commentaires à plusieurs :
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "AUTHOR_ID", nullable = false) private Author author;
Nous concluons ici que One-to-Many est l’image miroir de Many-to-One et vice versa. Il convient de souligner qu'Hibernate ne connaît rien aux communications bidirectionnelles. Pour lui, ce sont deux connexions différentes : l’une dans un sens, l’autre dans le sens opposé.
-
Rien n'a beaucoup changé dans hibernate.cfg.xml .
-
Persistance:
Relation plusieurs-à-un
Puisque Many-to-One est une image miroir de One-to-Many, il y aura peu de différences. Lien vers la succursale ici .-
Suite à l'exécution du script d'initialisation, nous obtenons le résultat suivant :
-
Diagramme:
-
La différence de mappage résidera dans les annotations et les champs :
Il n'y a plus d'ensemble dans la classe Author , puisqu'il a été déplacé vers la classe Book .
-
Persistance:
Relation plusieurs-à-plusieurs
Passons à la relation la plus intéressante. Cette relation, selon toutes les règles de la décence et de l'indécence, se crée à travers un tableau supplémentaire. Mais cette table n'est pas une entité. Intéressant, non ? Jetons un coup d'oeil à cette merde. Lien vers la succursale ici .-
Regardez le script d'initialisation , une table HAS supplémentaire apparaît ici. Nous obtenons quelque chose comme l'auteur a un livre.
À la suite de l'exécution du script, nous obtiendrons les tableaux suivants :
-
Diagramme:
Dans notre exemple, il s'avère qu'un livre peut avoir plusieurs auteurs, et qu'un auteur peut avoir plusieurs livres. Ils peuvent se chevaucher.
-
Les classes de mappage auront des ensembles au sein des classes. Mais comme je l'ai dit, la table HAS n'est pas une entité.
Classe d'auteur :
@ManyToMany @JoinTable(name = "HAS", joinColumns = @JoinColumn(name = "AUTHOR_ID", referencedColumnName = "ID"), inverseJoinColumns = @JoinColumn(name = "BOOK_ID", referencedColumnName = "ID") ) private Set<Book> books;
@ManyToMany est un type de relation.
@JoinTable - c'est exactement ce qui connectera l'attribut à une table HAS supplémentaire. Nous y spécifions deux attributs qui pointeront vers les clés primaires de deux entités.
Cours de livre :
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "books") private Set<Author> authors;
Ici, nous indiquons FetchType et le champ que nous utiliserons pour cartographier.
-
Notre hibernate.cfg.xml est encore une fois resté inchangé (je ne prends pas en compte le fait que nous avons créé une nouvelle base de données pour chaque branche).
-
Persistance:
Compte rendu
Nous avons donc examiné superficiellement les types de relations DB et compris comment les implémenter dans le modèle ORM. Nous avons écrit un projet de test qui démontre toutes les connexions et compris comment configurer hibernate / jpa. Phew.Liens utiles
- En fait, le projet lui-même
- Branche individuelle
- Branche un-à-plusieurs
- Branche plusieurs-à-un
- Branche plusieurs à plusieurs
- Lis le
- Et lis-le
GO TO FULL VERSION