CodeGym /Java-Blog /Random-DE /Ihre erste Hibernate-Anwendung
John Squirrels
Level 41
San Francisco

Ihre erste Hibernate-Anwendung

Veröffentlicht in der Gruppe Random-DE
In diesem Artikel lernen Sie eines der beliebtesten Enterprise-Frameworks für Java kennen und erstellen Ihre erste Hibernate-Anwendung. Noch nie von Hibernate gehört? Oder vielleicht haben Sie davon gehört, es aber noch nicht genutzt? Oder haben Sie vielleicht versucht, es zu verwenden, sind aber gescheitert? In allen drei Fällen – willkommen unter dem Schnitt :) Ihre erste Hibernate-Anwendung – 1 Hallo zusammen! In diesem Artikel werde ich über die Hauptfunktionen des Hibernate-Frameworks sprechen und Ihnen beim Schreiben Ihrer ersten Minianwendung helfen. Dazu benötigen wir:
  1. IntelliJ IDEA Ultimate Edition
    Laden Sie es von der offiziellen Website herunter und aktivieren Sie die 30-Tage-Testversion.
  2. PostgreSQL – eines der beliebtesten modernen Datenbankverwaltungssysteme (DBMS)
  3. Maven (bereits mit IDEA verkabelt)
  4. Ein bisschen Geduld.
Für alle Fälle habe ich den Anwendungscode auf GitHub gepostet. Der Artikel richtet sich in erster Linie an diejenigen, die noch nie zuvor mit dieser Technologie gearbeitet haben, daher habe ich den Codeumfang minimiert. Lass uns anfangen!

Was ist Ruhezustand?

Es handelt sich um eine der beliebtesten ORM-Implementierungen (Object-Relational Mapping). Eine objektrelationale Zuordnung definiert die Beziehung zwischen Softwareobjekten und Datenbankdatensätzen. Natürlich verfügt Hibernate über einen sehr breiten Funktionsumfang, wir konzentrieren uns jedoch auf die einfachsten Funktionen. Unser Ziel ist es, eine CRUD-Anwendung (Erstellen, Lesen, Aktualisieren, Löschen) zu erstellen, die Folgendes kann:
  1. Erstellen Sie Benutzer (Benutzer), suchen Sie sie in der Datenbank anhand ihrer ID, aktualisieren Sie ihre Daten in der Datenbank und löschen Sie sie aus der Datenbank.
  2. Weisen Sie Benutzern Autoobjekte (Auto) zu. Erstellen, aktualisieren, suchen und löschen Sie Autos aus der Datenbank.
  3. Darüber hinaus sollte die Anwendung „besitzerlose“ Autos automatisch aus der Datenbank entfernen. Mit anderen Worten: Wenn ein Benutzer gelöscht wird, müssen auch alle zu diesem Benutzer gehörenden Autos aus der Datenbank gelöscht werden.
Unser Projekt wird wie folgt aufgebaut sein: Ihre erste Hibernate-Anwendung – 2Wie Sie sehen, nichts Kompliziertes. 6 Klassen + 1 Datei mit Konfigurationen. Erstellen Sie zunächst ein neues Maven-Projekt in IntelliJ IDEA. Datei -> Neues Projekt. Wählen Sie Maven aus den vorgeschlagenen Projekttypen aus und fahren Sie mit dem nächsten Schritt fort. Ihre erste Hibernate-Anwendung – 3Apache Maven ist ein Framework zum automatischen Erstellen von Projekten basierend auf einer Beschreibung ihrer Struktur in POM-Dateien. Die gesamte Struktur Ihres Projekts wird in pom.xml beschrieben, einer Datei, die IDEA selbst im Stammverzeichnis Ihres Projekts erstellt. In den Projekteinstellungen müssen Sie die folgenden Maven-Einstellungen angeben: GroupId und ArtefaktId. In Projekten ist die Gruppen-ID normalerweise eine Beschreibung des Unternehmens oder der Geschäftseinheit. Hier kann der Domainname des Unternehmens oder der Website stehen. Die Artefakt-ID wiederum ist der Name des Projekts. Für groupdId können Sie eingeben com.yourNickname.codegym. Dies hat keine Auswirkungen auf die Anwendung. Wählen Sie als Artefakt-ID einen beliebigen Projektnamen aus. Die Version kann unverändert bleiben. Ihre erste Hibernate-Anwendung – 4Auf dem letzten Bildschirm bestätigen Sie einfach die zuvor eingegebenen Daten.Ihre erste Hibernate-Anwendung – 5Also haben wir das Projekt erstellt. Jetzt müssen wir nur noch etwas Code schreiben und ihn zum Laufen bringen :) Das Wichtigste zuerst: Wenn wir eine Anwendung erstellen wollen, die mit einer Datenbank funktioniert, kommen wir definitiv nicht ohne eine Datenbank aus! Laden Sie PostgreSQL hier herunter (ich verwende Version 9). PostgreSQL hat den Standardbenutzer „postgres“ – Sie müssen sich bei der Installation ein Passwort dafür ausdenken. Vergessen Sie das Passwort nicht. Wir werden es später brauchen! (Im Allgemeinen ist die Verwendung der Standarddatenbank in Anwendungen eine schlechte Praxis, aber wir werden dies tun, um die Anzahl der verursachten Geschwüre zu reduzieren, indem wir eine eigene Datenbank erstellen.) Wenn Sie mit der Befehlszeile und SQL-Abfragen nicht vertraut sind, gibt es gute Nachrichten. IntelliJ IDEA bietet eine rundum passende Benutzeroberfläche für die Arbeit mit der Datenbank. Ihre erste Hibernate-Anwendung – 6(befindet sich im rechten Bereich von IDEA, auf der Registerkarte „Datenbank“). Um eine Verbindung herzustellen, klicken Sie auf „+“ und wählen Sie unsere Datenquelle (PostgeSQL) aus. Füllen Sie die Felder für Benutzer und Datenbank („postgres“ für beide) aus und geben Sie das Passwort ein, das bei der Installation von PostgreSQL festgelegt wurde. Laden Sie bei Bedarf den Postgres-Treiber herunter. Sie können dies auf derselben Seite tun. Klicken Sie auf „Verbindung testen“, um zu überprüfen, ob die Datenbankverbindung hergestellt ist. Wenn Sie „Erfolgreich“ sehen, fahren Sie fort. Jetzt erstellen wir die Tabellen, die wir brauchen. Es wird insgesamt zwei geben: Benutzer und Autos. Parameter für die Benutzertabelle: Ihre erste Hibernate-Anwendung – 7Beachten Sie, dass id der Primärschlüssel ist. Wenn Sie den Primärschlüssel in SQL nicht kennen, googeln Sie ihn. Das ist wichtig. Einstellungen für die Autos-Tabelle: Ihre erste Hibernate-Anwendung – 8Für die Autos-Tabelle müssen Sie einen Fremdschlüssel konfigurieren. Es dient der Verknüpfung unserer Tabellen. Ich empfehle Ihnen, mehr darüber zu lesen. Vereinfacht ausgedrückt verweist es auf eine externe Tabelle, in unserem Fall auf Benutzer. Wenn ein Auto dem Benutzer mit der ID = 1 gehört, ist das Feld „user_id“ der Autos gleich 1. Auf diese Weise verknüpfen wir Benutzer in unserer Anwendung mit ihren Autos. In unserer Autos-Tabelle fungiert das Feld user_id als Fremdschlüssel. Es bezieht sich auf das ID-Feld der Benutzertabelle. Ihre erste Hibernate-Anwendung – 9Also haben wir eine Datenbank mit zwei Tabellen erstellt. Was bleibt, ist zu verstehen, wie man es über Java-Code verwaltet. Wir beginnen mit der Datei pom.xml, in die wir die erforderlichen Bibliotheken einbinden müssen (in Maven werden sie Abhängigkeiten genannt). Alle Bibliotheken werden im zentralen Maven-Repository gespeichert. Die von Ihnen in pom.xml angegebenen Bibliotheken stehen Ihnen zur Verwendung im Projekt zur Verfügung. Ihre pom.xml sollte so aussehen: Ihre erste Hibernate-Anwendung – 10Nichts Kompliziertes, wie Sie sehen können. Wir haben nur zwei Abhängigkeiten hinzugefügt – für die Verwendung von PostgreSQL und Hibernate. Kommen wir nun zum Java-Code. Erstellen Sie alle erforderlichen Pakete und Klassen im Projekt. Zunächst benötigen wir ein Datenmodell: die Userund- AutoKlassen.

 package models;
 
 import javax.persistence.*;
 import java.util.ArrayList;
 import java.util.List;
 
 @Entity
 @Table (name = "users")
 public class User {
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private int id;
     @Column(name = "name")
     private String name;
     // You can omit the Column attribute if the name property matches the column name in the table
     private int age;
 
     @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
     private List<Auto> autos;
 
     public User() {
     }
 
     public User(String name, int age) {
         this.name = name;
         this.age = age;
         autos = new ArrayList<>();
     }
 
     public void addAuto(Auto auto) {
         auto.setUser(this);
         autos.add(auto);
     }
 
     public void removeAuto(Auto auto) {
         autos.remove(auto);
     }
 
     public int getId() {
         return id;
     }
 
     public String getName() {
         return name;
     }
 
     public void setName(String name) {
         this.name = name;
     }
 
     public int getAge() {
         return age;
     }
 
     public void setAge(int age) {
         this.age = age;
     }
 
     public List<Auto> getAutos() {
         return autos;
     }
 
     public void setAutos(List<Auto> autos) {
         this.autos = autos;
     }
 
     @Override
     public String toString() {
         return "models.User{" +
                 "id=" + id +
                 ", name='" + name + '\'' +
                 ", age=" + age +
                 '}';
     }
 }
 
 

 package models;
 
 import javax.persistence.*;
 
 @Entity
 @Table(name = "autos")
 public class Auto {
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private int id;
 
     @Column (name = "model")
     private String model;
 
     // You can omit the Column attribute if the name property matches the column name in the table
     private String color;
 
 
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "user_id")
     private User user;
 
     public Auto() {
     }
 
     public Auto(String model, String color) {
         this.model = model;
         this.color = color;
     }
 
     public int getId() {
         return id;
     }
 
     public String getModel() {
         return model;
     }
 
     public void setModel(String model) {
         this.model = model;
     }
 
     public String getColor() {
         return color;
     }
 
     public void setColor(String color) {
         this.color = color;
     }
 
     public User getUser() {
         return user;
     }
 
     public void setUser(User user) {
         this.user = user;
     }
 
     @Override
     public String toString() {
         return color + " " + model;
     }
 }
 
 
Wie Sie sehen, enthalten Klassen eine Reihe undurchsichtiger Anmerkungen. Fangen wir an, uns mit ihnen auseinanderzusetzen. Für uns ist die Hauptannotation @Entity. Lesen Sie darüber auf Wikipedia und lernen Sie alles auswendig. Dies ist das Fundament der Stiftung. Mit dieser Annotation können Objekte Ihrer Java-Klasse einer Datenbank zugeordnet werden. Damit eine Klasse eine Entität ist, muss sie die folgenden Anforderungen erfüllen:
  • Es muss einen leeren Konstruktor ( publicoder protected) haben.
  • Es kann nicht verschachtelt werden, eine Schnittstelle oder einenum
  • Es kann und darf keine Felder/Eigenschaften finalhabenfinal
  • Es muss mindestens ein @Id-Feld enthalten.
Überprüfen Sie Ihre Entitätsklassen: Sie sind sehr beliebte Orte, an denen man sich selbst ins Bein schießen kann. Es ist sehr leicht, etwas zu vergessen. Darüber hinaus kann ein Unternehmen Folgendes tun:
  • Es kann nicht leere Konstruktoren haben
  • Es kann erben und vererbt werden
  • Es kann andere Methoden haben und Schnittstellen implementieren.
Wie Sie sehen, Userist die Klasse der Benutzertabelle sehr ähnlich. Es hat id, nameundageFelder. Die darüber befindlichen Anmerkungen bedürfen keiner besonderen Erklärung: Es ist klar, dass @Id angibt, dass das Feld ein Bezeichner von Objekten dieser Klasse ist. Die Annotation @Table über der Klasse gibt den Namen der Tabelle an, in die die Objekte geschrieben werden. Beachten Sie den Kommentar über dem Altersfeld: Wenn der Name des Felds in der Klasse mit dem Namen der Tabelle übereinstimmt, können Sie die Annotation @Column weglassen und es wird funktionieren. Was den in den geschweiften Klammern angegebenen Teil betrifft („strategy = GenerationType.IDENTITY“): Es gibt mehrere Strategien zur Generierung von IDs. Sie können sie googeln, aber für unsere Anwendung brauchen Sie sich nicht die Mühe zu machen. Die Hauptsache ist, dass für unsere Objekte der Wert von id automatisch generiert wird. Dementsprechend gibt es keinen Setter für id und wir setzen ihn auch nicht im Konstruktor. Jedoch,UserKlasse sticht hervor. Es gibt eine Liste von Autos! Ihre erste Hibernate-Anwendung – 11Die Annotation @OneToMany hängt über der Liste. Dies bedeutet, dass mehrere Autos demselben Objekt der Benutzerklasse entsprechen können. Das Element „mappedBy“ verweist auf das Benutzerfeld der AutoKlasse. Somit sind Autos und Benutzer miteinander verbunden. Das orphanRemoval-Element gibt an, ob der Entfernungsvorgang auf Entitäten angewendet werden soll, die keine Beziehung mehr haben. Wenn wir einen Benutzer aus der Datenbank löschen, werden auch alle damit verbundenen Autos gelöscht. Im Gegenzug in derAutoKlasse sehen Sie das Benutzerfeld mit der Annotation @ManyToOne (ein Benutzer kann vielen Autos entsprechen) und der Annotation @JoinColumn. Es gibt an, welche Spalte in der Autos-Tabelle als Referenz auf die Benutzertabelle verwendet wird (dh der Fremdschlüssel, über den wir zuvor gesprochen haben). Nachdem Sie das Datenmodell erstellt haben, ist es an der Zeit, unserem Programm beizubringen, Operationen mit den Daten in der Datenbank durchzuführen. Beginnen wir mit der Dienstprogrammklasse HibernateSessionFactoryUtil. Es hat nur eine Aufgabe – eine Sitzungsfabrik zu erstellen, damit unsere Anwendung mit der Datenbank arbeiten kann (begrüßen Sie das Factory-Entwurfsmuster!). Es weiß nicht, wie es etwas anderes machen soll.

 package utils;
 
 import models.Auto;
 import models.User;
 import org.hibernate.SessionFactory;
 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
 import org.hibernate.cfg.Configuration;
 
 public class HibernateSessionFactoryUtil {
     private static SessionFactory sessionFactory;
 
     private HibernateSessionFactoryUtil() {}
 
     public static SessionFactory getSessionFactory() {
         if (sessionFactory == null) {
             try {
                 Configuration configuration = new Configuration().configure();
                 configuration.addAnnotatedClass(User.class);
                 configuration.addAnnotatedClass(Auto.class);
                 StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
                 sessionFactory = configuration.buildSessionFactory(builder.build());
 
             } catch (Exception e) {
                 System.out.println("Исключение!" + e);
             }
         }
         return sessionFactory;
     }
 }
 
In dieser Klasse erstellen wir ein neues Konfigurationsobjekt und übergeben ihm die Klassen, die es als Entitäten behandeln soll: Userund Auto. Achten Sie auf die configuration.getProperties()Methode. Welche weiteren Eigenschaften gibt es? Woher kommen sie? Eigenschaften sind die Ruhezustandseinstellungen, die in der speziellen Datei hibernate.cfg.xml angegeben sind. Ihre erste Hibernate-Anwendung – 12Hibernate.cfg.xml ist hier zu lesen: new Configuration().configure(); Wie Sie sehen, ist daran nichts Besonderes: Es enthält die Parameter für die Verbindung zur Datenbank sowie den Parameter show_sql. Dies ist erforderlich, damit alle von Hibernate ausgeführten SQL-Abfragen auf der Konsole angezeigt werden. Auf diese Weise können Sie genau sehen, was Hibernate zu einem bestimmten Zeitpunkt tut, und jegliches Gefühl von „Magie“ eliminieren. Als nächstes brauchen wir dasUserDAOKlasse. Die beste Vorgehensweise besteht darin, über die Schnittstellen zu programmieren – erstellen Sie eine separate UserDAOSchnittstelle und UserDAOImplImplementierung, aber ich werde dies überspringen, um die Codemenge zu reduzieren. Tun Sie dies nicht in echten Projekten! Das DAO-Entwurfsmuster (Data Access Object) ist eines der gebräuchlichsten. Die Idee ist einfach: Erstellen Sie eine Anwendungsschicht, die nur für den Zugriff auf Daten verantwortlich ist, nichts weiter. Daten aus der Datenbank abrufen, Daten aktualisieren, Daten löschen – fertig. Erfahren Sie mehr über DAO. Sie werden bei Ihrer Arbeit ständig Datenzugriffsobjekte verwenden. Was kann unsere UserDaoKlasse tun? Nun, wie alle DAOs kann es nur mit Daten arbeiten. Suchen Sie einen Benutzer anhand seiner ID, aktualisieren Sie seine Daten, löschen Sie ihn, rufen Sie eine Liste aller Benutzer aus der Datenbank ab oder speichern Sie einen neuen Benutzer in der Datenbank – das ist die Gesamtheit seiner Funktionen.

 package dao;
 
 import models.Auto;
 import models.User;
 import org.hibernate.Session;
 import org.hibernate.Transaction;
 import utils.HibernateSessionFactoryUtil;
 import java.util.List;
 
 public class UserDao {
 
     public User findById(int id) {
         return HibernateSessionFactoryUtil.getSessionFactory().openSession().get(User.class, id);
     }
 
     public void save(User user) {
         Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
         Transaction tx1 = session.beginTransaction();
         session.save(user);
         tx1.commit();
         session.close();
     }
 
     public void update(User user) {
         Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
         Transaction tx1 = session.beginTransaction();
         session.update(user);
         tx1.commit();
         session.close();
     }
 
     public void delete(User user) {
         Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
         Transaction tx1 = session.beginTransaction();
         session.delete(user);
         tx1.commit();
         session.close();
     }
 
     public Auto findAutoById(int id) {
         return HibernateSessionFactoryUtil.getSessionFactory().openSession().get(Auto.class, id);
     }
 
     public List<User> findAll() {
         List<User> users = (List<User>) HibernateSessionFactoryUtil.getSessionFactory().openSession().createQuery("From User").list();
         return users;
     }
 }
 
UserDaoDie Methoden sind einander ähnlich. In den meisten Fällen erhalten wir mithilfe unserer Session Factory ein Sitzungsobjekt (Datenbankverbindungssitzung), erstellen eine einzelne Transaktion innerhalb dieser Sitzung, führen die erforderlichen Datenmanipulationen durch, speichern das Ergebnis der Transaktion in der Datenbank und schließen dann die Sitzung. Die Methoden selbst sind, wie Sie sehen, recht einfach. DAO ist das „Herz“ unserer Anwendung. Wir werden jedoch kein DAO direkt erstellen und seine Methoden in unserer main()Methode aufrufen. Die gesamte Logik wird in die UserServiceKlasse verschoben.

 package services;
 
 import dao.UserDao;
 import models.Auto;
 import models.User;
 
 import java.util.List;
 
 public class UserService {
 
     private UserDao usersDao = new UserDao();
 
     public UserService() {
     }
 
     public User findUser(int id) {
         return usersDao.findById(id);
     }
 
     public void saveUser(User user) {
         usersDao.save(user);
     }
 
     public void deleteUser(User user) {
         usersDao.delete(user);
     }
 
     public void updateUser(User user) {
         usersDao.update(user);
     }
 
     public List<User> findAllUsers() {
         return usersDao.findAll();
     }
 
     public Auto findAutoById(int id) {
         return usersDao.findAutoById(id);
     }
 
 
 }
 
 
Ein Dienst ist eine Anwendungsdatenschicht, die für die Ausführung der Geschäftslogik verantwortlich ist. Wenn Ihr Programm eine Geschäftslogik ausführen muss, erfolgt dies über Dienste. Ein Dienst enthält eine UserDaound ruft in seinen Methoden DAO-Methoden auf. Es mag den Anschein haben, als würden wir hier Funktionen duplizieren (warum rufen wir die Methoden nicht einfach von einem DAO-Objekt aus auf?), aber bei vielen Objekten und komplexer Logik bietet die Schichtung der Anwendung enorme Vorteile (dies ist eine gute Vorgehensweise – denken Sie in Zukunft daran). und lesen Sie mehr über „Anwendungsschichten“). Unser Dienst verfügt über eine einfache Logik, aber Dienstmethoden in realen Projekten enthalten viel mehr als eine Codezeile :) Jetzt haben wir alles, was Sie zum Ausführen der Anwendung benötigen! In der main()Methode erstellen wir einen Benutzer und sein Auto, verknüpfen sie miteinander und speichern sie in der Datenbank.

 import models.Auto;
 import models.User;
 import services.UserService;
 
 import java.sql.SQLException;
 
 public class Main {
     public static void main(String[] args) throws SQLException {
 
         UserService userService = new UserService();
         User user = new User ("Jenny", 26);
         userService.saveUser(user);
         Auto ferrari = new Auto("Ferrari", "red");
         ferrari.setUser(user);
         user.addAuto(ferrari);
         Auto ford = new Auto("Ford", "black");
         ford.setUser(user);
         user.addAuto(ford);
         userService.updateUser(user);
     }
 }
 
 
Wie Sie sehen können, verfügt die Benutzertabelle über einen eigenen Datensatz und die Autos-Tabelle über einen eigenen Datensatz. Ihre erste Hibernate-Anwendung – 13Ihre erste Hibernate-Anwendung – 14Versuchen wir, unseren Benutzer umzubenennen. Löschen Sie die Benutzertabelle und führen Sie den Code aus

 import models.Auto;
 import models.User;
 import services.UserService;
 
 import java.sql.SQLException;
 
 public class Main {
     public static void main(String[] args) throws SQLException {
 
         UserService userService = new UserService();
         User user = new User ("Jenny", 26);
         userService.saveUser(user);
         Auto ferrari = new Auto("Ferrari", "red");
         user.addAuto(ferrari);
         Auto ford = new Auto("Ford", "black");
         ford.setUser(user);
         user.addAuto(ford);
         userService.updateUser(user);
         user.setName ("Benny");
         userService.updateUser(user);
     }
 }
 
 
Es klappt! Ihre erste Hibernate-Anwendung – 15Was passiert, wenn Sie den Benutzer löschen? Löschen Sie die Benutzertabelle (autos löscht sich selbst) und führen Sie den Code aus

 import models.Auto;
 import models.User;
 import services.UserService;
 
 import java.sql.SQLException;
 
 public class Main {
     public static void main(String[] args) throws SQLException {
 
         UserService userService = new UserService();
         User user = new User ("Jenny", 26);
         userService.saveUser(user);
         Auto ferrari = new Auto("Ferrari", "red");
         user.addAuto(ferrari);
         Auto ford = new Auto("Ford", "black");
         ford.setUser(user);
         user.addAuto(ford);
         userService.updateUser(user);
         user.setName ("Benny");
         userService.updateUser(user);
         userService.deleteUser(user);
     }
 }
 
 
Und unsere Tabellen sind komplett leer (achten Sie auf die Konsole – dort werden alle von Hibernate ausgeführten Anfragen angezeigt). Sie können mit der Anwendung herumspielen und alle Funktionen ausprobieren. Erstellen Sie beispielsweise einen Benutzer mit Autos, speichern Sie ihn in der Datenbank, sehen Sie sich die dem Benutzer zugewiesene ID an und versuchen Sie, diese ID im zu verwendenmain()Methode, um den Benutzer aus der Datenbank abzurufen und eine Liste seiner Autos auf der Konsole anzuzeigen. Natürlich haben wir nur einen kleinen Teil der Funktionalität von Hibernate gesehen. Seine Fähigkeiten sind sehr breit gefächert und es ist seit langem ein Standard-Branchentool für die Java-Entwicklung. Wer sich ausführlich damit beschäftigen möchte, dem kann ich das Buch „Java Persistence API and Hibernate“ empfehlen. Ich habe es in einem früheren Artikel rezensiert. Ich hoffe, dieser Artikel war für die Leser hilfreich. Wenn Sie Fragen haben, stellen Sie diese in den Kommentaren. Ich antworte gerne :) Vergessen Sie auch nicht, den Autor beim Wettbewerb zu unterstützen, indem Sie ein „Gefällt mir“ posten. Oder noch besser: „Ich liebe es“ :) Viel Glück beim Lernen!
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION