JavaRush /Blog Java /Random-FR /Entités JPA et relations DB
Nikita Koliadin
Niveau 40
Днепр

Entités JPA et relations DB

Publié dans le groupe Random-FR

Entités JPA et relations DB

Bonne journée, chers collègues !
Entités JPA et relations DB - 1
Ce matériel est destiné à ceux qui ont déjà une compréhension de l'organisation des bases de données (ci-après simplement DB - "Base de données"), une connaissance minimale du fonctionnement du mappage objet-relationnel (ci-après simplement ORM ) et de ses implémentations, telles que Hibernate / JPA. . Si cela ne vous est pas familier, je vous conseille de commencer par JDBC , puis de passer ensuite au modèle ORM. Je vous ai prévenu, et je n'assume aucune responsabilité pour votre psychisme après avoir lu cet article sans préparation adéquate ! :) Commençons par tout traiter dans l'ordre. Tout d’abord, nous approfondirons un peu la théorie, juste un petit peu. Deuxièmement, nous découvrirons comment faire cette merde dans Java préféré de tous. Nous rédigerons également avec vous une aide-mémoire de projet, qui consolidera notre compréhension du sujet et servira de modèle pour COMMENT la cartographie doit être effectuée . Alors faisons-le!

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 :
  1. Un par un
  2. Un à plusieurs
  3. Plusieurs-à-Un
  4. Plusieurs à plusieurs
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 .
  1. 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 :

    Entités JPA et relations DB - 2

    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é.

    Entités JPA et relations DB - 3

    Ensuite, configurons le circuit. Cette étape est facultative mais recommandée si vous avez plusieurs schémas dans la base de données.

    Entités JPA et relations DB - 4

    Appliquez les paramètres, et au final nous devrions obtenir quelque chose comme ceci :

    Entités JPA et relations DB - 5
  2. 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 :

    Entités JPA et relations DB - 6

    Résultat de l'exécution dans la console :

    Entités JPA et relations DB - 7

    Résultat dans la base de données :

    Entités JPA et relations DB - 8
  3. Regardons un schéma de nos tables. Pour ce faire, RMB sur notre DB :

    Entités JPA et relations DB - 9

    Résultat:

    Entités JPA et relations DB - 10

    Sur le diagramme UML nous pouvons voir toutes les clés primaires et étrangères, nous voyons également la connexion entre nos tables.

  4. É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.

  5. 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;
    }
Voyons cela dans l'ordre :
  1. Tous les champs de la classe répètent les attributs de l'entité DB.
  2. @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.
  3. @Entity indique que la classe donnée est une entité et est associée à une entité DB.
  4. @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.
  5. @Table (name = "AUTHOR") lie la classe Book à la table DB AUTHOR.
  6. @Id dit que ce champ est la clé primaire.
  7. @GeneratedValue (stratégie = GenerationType.IDENTITY) – stratégie de génération de clé primaire.
  8. @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.
  9. @OneToOne indique que le champ donné est un champ de relation un-à-un.
  10. @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - une colonne BOOK_ID sera créée, qui est unique et non nulle.
Au verso (dans la classe Book ), nous devons également établir une connexion One-to-One et indiquer le champ sur lequel le mappage a lieu. @OneToOne(mappedBy = "book") - dans cet exemple, il s'agit du champ book de la classe Author. JPA les reliera lui-même. À première vue, il peut sembler qu'il y ait un fouillis d'annotations, mais en fait c'est très pratique et avec l'expérience vous les ajouterez automatiquement sans même y penser.
  1. 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>
Description des propriétés :
  1. hibernate.dialect est le dialecte du SGBD que nous avons choisi.
  2. hibernate.connection.driver_class — Classe de pilote de notre base de données.
  3. hibernate.connection.url - utl de notre base de données. Vous pouvez le prendre à partir du premier point où nous avons configuré DB.
  4. hibernate.connection.username - Nom d'utilisateur de la base de données.
  5. hibernate.connection.password — Mot de passe de l'utilisateur de base de données.
  6. 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.
  7. hibernate.show_sql - s'il faut afficher les requêtes DB.
  8. 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.
  9. 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.
  10. 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.
  11. hibernate.jdbc.batch_size — Taille maximale du lot.
  12. hibernate.jdbc.fetch_size — Taille maximale de récupération.
  13. hibernate.order_inserts - autorise les insertions dynamiques.
  14. hibernate.order_updates - Permet les mises à jour dynamiques.
  15. hibernate.jdbc.batch_versioned_data - permet le traitement par lots. Regardez votre SGBD : tout le monde ne le supporte pas.
  16. classe de mappage - classes qui sont nos entités. Tout doit être répertorié.
  1. Maintenant, notre essence doit être déterminée. Nous pouvons vérifier cela dans l'onglet persistance :

    Entités JPA et relations DB - 11

    Résultat:

    Entités JPA et relations DB - 12
  2. Nous devons également configurer l'attribution des données :

    Entités JPA et relations DB - 13 Entités JPA et relations DB - 14

    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.
  1. Suite à l'exécution du script d'initialisation, nous obtenons ce qui suit :

    Entités JPA et relations DB - 15

    Sentez-vous la différence avec le tableau précédent ?

  2. Diagramme:

    Entités JPA et relations DB - 16

    Relation un-à-plusieurs – un auteur peut avoir plusieurs livres. L'entité de gauche correspond à une ou plusieurs entités de droite.

  3. 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é.

  4. Rien n'a beaucoup changé dans hibernate.cfg.xml .

  5. Persistance:

    Entités JPA et relations DB - 17

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 .
  1. Suite à l'exécution du script d'initialisation, nous obtenons le résultat suivant :

    Entités JPA et relations DB - 18
  2. Diagramme:

    Entités JPA et relations DB - 19
  3. 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 .

  4. mise en veille prolongée.cfg.xml

  5. Persistance:

    Entités JPA et relations DB - 20

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 .
  1. 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 :

    Entités JPA et relations DB - 21
  2. Diagramme:

    Entités JPA et relations DB - 22

    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.

  3. 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.

  4. 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).

  5. Persistance:

    Entités JPA et relations DB - 23

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

Mes articles précédents : PS Il peut y avoir des erreurs et des lacunes dans le texte. PPS L'auteur fumait quelque chose d'étrange en écrivant cet article. Merci pour votre attention!
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION