JavaRush /مدونة جافا /Random-AR /تطبيق السبات الأول الخاص بك

تطبيق السبات الأول الخاص بك

نشرت في المجموعة
في هذه المقالة، ستتعرف على أحد أطر عمل المؤسسات الأكثر شيوعًا لـ Java وستنشئ تطبيقك الأول باستخدام Hibernate. لم تسمع عن السبات؟ ربما سمعت عنه ولكنك لم تستخدمه؟ أو حاولت البدء ولم تنجح؟ في جميع الحالات الثلاث، مرحبًا بك في القص :) أول تطبيق للإسبات - 1مرحبًا بالجميع! في هذه المقالة، سأتحدث عن الميزات الرئيسية لإطار عمل Hibernate وسأساعدك في كتابة تطبيقك المصغر الأول. لهذا نحتاج:
  1. Intellij Idea الإصدار النهائي؛
    قم بالتنزيل من الموقع الرسمي وقم بتنشيط الإصدار التجريبي لمدة 30 يومًا.
  2. يعد PostgeSQL أحد أشهر أنظمة إدارة قواعد البيانات الحديثة (DBMS)؛
  3. Maven (مدمج بالفعل في IDEA)؛
  4. القليل من الصبر.
تستهدف المقالة في المقام الأول أولئك الذين لم يسبق لهم التعامل مع هذه التكنولوجيا من قبل، لذلك تم تقليل كمية التعليمات البرمجية قدر الإمكان. هيا بنا نبدأ!

ما هو السبات؟

يعد هذا أحد أكثر تطبيقات نموذج ORM شيوعًا. يصف النموذج العلائقي للكائن العلاقات بين كائنات البرنامج والسجلات في قاعدة البيانات. بالطبع، وظائف السبات واسعة جدًا، لكننا سنركز على أبسط الوظائف. هدفنا: إنشاء تطبيق CRUD (إنشاء، قراءة، تحديث، حذف) والذي سيكون قادرًا على:
  1. إنشاء مستخدمين (User)، وكذلك البحث عنهم في قاعدة البيانات عن طريق الهوية، وتحديث بياناتهم في قاعدة البيانات، وكذلك حذفهم من قاعدة البيانات.
  2. تعيين كائنات السيارة (تلقائي) للمستخدمين. إنشاء وتحرير والعثور على وحذف السيارات من قاعدة البيانات.
  3. بالإضافة إلى ذلك، يجب أن يقوم التطبيق تلقائيًا بإزالة السيارات "اليتيمة" من قاعدة البيانات. أولئك. عند حذف مستخدم، يجب أيضًا حذف جميع السيارات التابعة له من قاعدة البيانات.
سيكون هيكل مشروعنا على النحو التالي: أول تطبيق للإسبات - 2كما ترون، لا يوجد شيء معقد. 6 فئات + ملف واحد مع التكوينات. أولاً، لنقم بإنشاء مشروع مخضرم جديد في Intellij Idea. ملف -> مشروع جديد. من أنواع المشاريع المقترحة، حدد Maven وانتقل إلى الأمام. أول تطبيق للإسبات - 3Apache Maven هو إطار عمل لأتمتة تجميع المشاريع بناءً على وصف بنيتها في ملفات بلغة POM. سيتم وصف البنية الكاملة لمشروعك في ملف pom.xml، الذي ستقوم IDEA بإنشائه في جذر مشروعك. في إعدادات المشروع، ستحتاج إلى تحديد معلمات Maven - groupId وartifactId. عادةً ما يكون معرف المجموعة في المشاريع هو اسم المؤسسة أو القسم، ويتم كتابة اسم المجال الخاص بالمؤسسة أو موقع المشروع هناك. وفي المقابل، artifactId هو اسم المشروع. بالنسبة إلى groupdId، يمكنك تحديد ذلك com.вашНикнейм.javarush، وهذا لن يؤثر على تشغيل التطبيق بأي شكل من الأشكال. بالنسبة إلى artifactId، حدد أي اسم مشروع تريده. يمكنك أيضًا ترك الإصدار دون تغيير. أول تطبيق للإسبات - 4في الشاشة الأخيرة، ما عليك سوى تأكيد البيانات التي أدخلتها مسبقًا. أول تطبيق للإسبات - 5لذلك، أنشأنا المشروع، كل ما تبقى هو كتابة الكود وتشغيله :) أولا وقبل كل شيء، إذا أردنا إنشاء تطبيق يعمل مع قاعدة بيانات، فلا يمكننا بالتأكيد الاستغناء عن قاعدة البيانات! قم بتنزيل PostgreSQL من هنا (أستخدم الإصدار 9). يحتوي PostgreSQL على المستخدم الافتراضي "postgres"، وسوف تحتاج إلى إنشاء كلمة مرور له أثناء التثبيت. لا تنس كلمة المرور الخاصة بك، سنحتاجها لاحقًا! (بشكل عام، يعد استخدام قاعدة البيانات الافتراضية في التطبيقات ممارسة سيئة، ولكن من أجل تقليل كمية البواسير، سنكتفي بإنشاء قاعدة البيانات الخاصة بنا). إذا لم تكن مرتاحًا لسطر الأوامر واستعلامات SQL، فهناك أخبار جيدة. يوفر Intellij IDEA واجهة مستخدم مناسبة تمامًا للعمل مع قاعدة البيانات. يبدو كما يلي: أول تطبيق للإسبات - 6(الموجود على الشريط الجانبي الأيمن لـ IDEA، علامة التبويب قاعدة البيانات) لإنشاء اتصال، انقر فوق "+"، حدد المزود الخاص بنا (PostgeSQL). املأ الحقول بالمستخدم واسم قاعدة البيانات (كلاهما postgres) وأدخل كلمة المرور التي قمت بتعيينها عند تثبيت PostgreSQL. إذا لزم الأمر، قم بتنزيل برنامج تشغيل Postgres، ويمكن القيام بذلك على نفس الصفحة. انقر فوق "اختبار الاتصال" للتأكد من إنشاء الاتصال بقاعدة البيانات. إذا رأيت النقش "ناجح"، فإننا ننتقل. الآن لنقم بإنشاء الجداول التي نحتاجها. سيكون هناك اثنان منهم - المستخدمون والسيارات. معلمات جدول المستخدمين: تطبيقك الأول على السبات - 7يرجى ملاحظة أن المعرف هو المفتاح الأساسي. إذا كنت لا تعرف ما هو المفتاح الأساسي في SQL، فابحث عنه في Google، فهو مهم. الإعداد لجدول السيارات: تطبيقك الأول على السبات - 8بالنسبة للسيارات، تحتاج إلى تكوين المفتاح الخارجي - المفتاح الخارجي. وسوف تربط جداولنا. أنصحك بقراءة المزيد عنه؛ بكل بساطة، فهو يشير إلى جدول خارجي، في حالتنا، المستخدمين. إذا كانت السيارة مملوكة لمستخدم بالمعرف = 1، ففي حقل user_id بجدول autos، سيكون الرقم 1. وهذه هي الطريقة التي نربط بها المستخدمين بسياراتهم في تطبيقنا. في جدول السيارات الخاص بنا، سيكون حقل user_id بمثابة مفتاح خارجي. سيشير إلى حقل المعرف الخاص بجدول المستخدمين. أول تطبيق للإسبات - 9وهكذا قمنا بإنشاء قاعدة بيانات تحتوي على جدولين. يبقى أن نفهم كيفية إدارتها من كود Java. سنبدأ بملف pom.xml، حيث نحتاج إلى تضمين المكتبات الضرورية (في لغة Maven تسمى التبعيات). يتم تخزين جميع المكتبات في مستودع Maven المركزي. تلك التي تحددها في pom.xml، يمكنك استخدامها في المشروع. يجب أن يبدو ملف pom.xml الخاص بك بهذا الشكل: أول تطبيق للإسبات - 10لا يوجد شيء معقد كما ترى. أضفنا تبعيتين فقط - لاستخدام PostgreSQL وHbernate. الآن دعنا ننتقل إلى كود جافا. إنشاء جميع الحزم والفئات اللازمة للمشروع. في البداية، سنحتاج إلى نماذج البيانات - الفئات Userو Auto.
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 not specify Column name if it matches the name of the column 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 not specify Column name if it matches the name of the column 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;
    }
}
كما ترون، تم تجهيز الفصول الدراسية بمجموعة من التعليقات التوضيحية غير الواضحة. لنبدأ بالتعامل معهم. التعليق التوضيحي الرئيسي بالنسبة لنا هو @Entity. اقرأ عنها في ويكيبيديا وتذكر كل شيء، هذا هو أساس الأساسيات. يسمح هذا التعليق التوضيحي بربط كائنات Java الخاصة بفصلك بقاعدة البيانات. لكي تكون الفئة كيانًا، يجب أن تستوفي المتطلبات التالية:
  • يجب أن يكون لديك مُنشئ فارغ ( public​​أو protected);
  • لا يمكن أن تكون متداخلة أو واجهة أو enum؛
  • لا يمكن أن finalيحتوي على final-fields/properties؛
  • يجب أن يحتوي على حقل @Id واحد على الأقل.
تحقق من فئات الكيانات الخاصة بك، فهي مكان شائع جدًا لإطلاق النار على قدمك. من السهل جدًا أن تنسى شيئًا ما. في هذه الحالة، يمكن للجهة:
  • تحتوي على منشئات غير فارغة؛
  • أن تكون موروثة وأن تكون موروثة؛
  • تحتوي على طرق أخرى وتنفيذ واجهات.
كما ترون، فإن الفصل Userمشابه جدًا لجدول المستخدمين. هناك حقول id, name. ageلا تحتاج التعليقات التوضيحية الموجودة فوقها إلى الكثير من الشرح: فمن الواضح بالفعل أن @Id هو إشارة إلى أن الحقل هو معرف كائنات من هذه الفئة. يحدد التعليق التوضيحي @Table الموجود أعلى الفئة اسم الجدول الذي سيتم كتابة الكائنات فيه. انتبه إلى التعليق الموجود أعلى حقل العمر: إذا كان اسم الحقل في الفصل الدراسي والجدول متماثلين، فلن تحتاج إلى إضافة تعليق توضيحي @Column، فسيعمل بهذه الطريقة. فيما يتعلق بـ "الاستراتيجية = GenerationType.IDENTITY" المشار إليها بين قوسين: هناك العديد من استراتيجيات إنشاء المعرفات. يمكنك البحث عنه في جوجل، ولكن في إطار تطبيقنا لا داعي للقلق. الشيء الرئيسي هو أنه سيتم إنشاء معرفات كائناتنا تلقائيًا، لذلك لا يوجد محدد للمعرف، ولا نحدده في المُنشئ أيضًا. Userومع ذلك، لا يزال الفصل متميزًا في بعض النواحي . لديه قائمة السيارات! يظهر التعليق التوضيحي @OneToMany أعلى القائمة. وهذا يعني أن كائنًا واحدًا من فئة المستخدم يمكن أن يتوافق مع عدة أجهزة. يشير الإعداد "mappedBY" إلى حقل المستخدم الخاص بالفئة Auto. وبهذه الطريقة، ترتبط الأجهزة والمستخدمون ببعضهم البعض. يُترجم إعداد إزالة الأيتام جيدًا من اللغة الإنجليزية - "إزالة الأيتام". إذا قمنا بحذف مستخدم من قاعدة البيانات، فسيتم أيضًا حذف جميع السيارات المرتبطة به. Autoفي المقابل، سترى في الفصل حقل المستخدم مع التعليق التوضيحي @ManyToOne (يمكن أن تتوافق العديد من السيارات مع مستخدم واحد) والتعليق التوضيحي @JoinColumn. يشير من خلاله إلى أي عمود في جدول autos يتم الاتصال بجدول المستخدمين (نفس المفتاح الخارجي الذي تحدثنا عنه سابقًا). بعد إنشاء نموذج بيانات، حان الوقت لتعليم برنامجنا كيفية إجراء العمليات على هذه البيانات في قاعدة البيانات. لنبدأ بفئة الأداة المساعدة HibernateSessionFactoryUtil. لها مهمة واحدة فقط - إنشاء مصنع جلسة لتطبيقنا للعمل مع قاعدة البيانات (مرحبًا، نمط "المصنع!"). لا يستطيع أن يفعل أي شيء آخر.
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("Exception!" + e);
            }
        }
        return sessionFactory;
    }
}
في هذه الفئة، نقوم بإنشاء كائن تكوين جديد، التكوين، ونمرر إليه الفئات التي يجب أن ينظر إليها على أنها كيانات - Userو Auto. انتبه إلى الطريقة configuration.getProperties(). ما هي الخصائص الأخرى؟ أين؟ الخصائص هي معلمات لكيفية عمل السبات، وهي محددة في ملف خاص hibernate.cfg.xml. أول تطبيق للإسبات - 11تتم قراءة Hibernate.cfg.xml هنا: new Configuration().configure(); كما ترون، لا يوجد شيء خاص فيه - معلمات للاتصال بقاعدة البيانات، ومعلمة خاصة show_sql. إنه ضروري حتى يتم إخراج جميع استعلامات SQL التي سيتم تنفيذها في وضع السبات على قاعدة البيانات الخاصة بنا إلى وحدة التحكم. بهذه الطريقة، سترى بالضبط ما يفعله Hibernate في كل لحظة من الزمن وتتخلص من التأثير "السحري". التالي نحن بحاجة إلى الطبقة UserDAO. (بطريقة جيدة، تحتاج إلى البرمجة من خلال الواجهات - قم بإنشاء واجهة UserDAOوتنفيذها بشكل منفصل UserDAOImpl، ولكن لتقليل كمية التعليمات البرمجية سأحذف هذا. لا تفعل هذا في المشاريع الحقيقية!). يعد DAO (كائن الوصول إلى البيانات) أحد أنماط التصميم الأكثر شيوعًا، "الوصول إلى البيانات". معناها بسيط - إنشاء طبقة في التطبيق تكون مسؤولة فقط عن الوصول إلى البيانات، ولا شيء غير ذلك. احصل على البيانات من قاعدة البيانات، وقم بتحديث البيانات، وحذف البيانات - وهذا كل شيء. اقرأ المزيد عن DAOs؛ سوف تستخدمها باستمرار في عملك. ماذا يمكن لفصلنا أن يفعل UserDao؟ في الواقع، مثل جميع المنظمات اللامركزية المستقلة، يمكنها العمل فقط مع البيانات. ابحث عن مستخدم حسب هويته، أو قم بتحديث بياناته، أو احذفه، أو اسحب قائمة بجميع المستخدمين من قاعدة البيانات أو احفظ مستخدمًا جديدًا في قاعدة البيانات - هذه هي كل وظائفه.
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;
    }
}
الأساليب UserDaoمتشابهة مع بعضها البعض. في معظمها، نتلقى كائن جلسة (جلسة متصلة بقاعدة البيانات الخاصة بنا) باستخدام مصنع الجلسة الخاص بنا، وننشئ معاملة واحدة خلال هذه الجلسة، وننفذ تحويلات البيانات اللازمة، ونحفظ نتيجة المعاملة في قاعدة البيانات ونغلق الجلسة. الأساليب نفسها، كما ترون، بسيطة للغاية. DAO هو "قلب" تطبيقنا. ومع ذلك، لن نقوم بإنشاء DAO مباشرةً واستدعاء أساليبه في ملف main(). سيتم نقل كل المنطق إلى ملف UserService.
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);
    }


}
الخدمة عبارة عن طبقة بيانات في التطبيق مسؤولة عن تنفيذ منطق الأعمال. إذا كان برنامجك يحتاج إلى تنفيذ بعض منطق الأعمال، فإنه يفعل ذلك من خلال الخدمات. تحتوي الخدمة على نفسها UserDaoوتستدعي أساليب DAO في أساليبها. قد يبدو هذا بمثابة تكرار للوظائف بالنسبة لك (لماذا لا نستدعي الأساليب من كائن داو فقط)، ولكن مع وجود عدد كبير من الكائنات والمنطق المعقد، فإن تقسيم التطبيق إلى طبقات له فوائد هائلة (هذه ممارسة جيدة، تذكر هذه المعلومات لـ المستقبل واقرأ عن "طبقات التطبيق" "). المنطق في خدمتنا بسيط، لكن في المشاريع الحقيقية، ستحتوي أساليب الخدمة على أكثر من سطر واحد من التعليمات البرمجية :) الآن لدينا كل ما نحتاجه حتى يعمل التطبيق! لنقم بإنشاء main()مستخدم وأجهزة له في الطريقة، وربطها ببعضها البعض وحفظها في قاعدة البيانات.
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("Masha",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);
    }
}
كما ترون، يحتوي جدول المستخدمين على إدخال خاص به، كما أن جدول autos له إدخال خاص به. أول تطبيق للإسبات - 13أول تطبيق للإسبات - 14دعونا نحاول إعادة تسمية مستخدمنا. دعونا نمسح جدول المستخدمين ونقوم بتشغيل الكود
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("Masha",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("Sasha");
        userService.updateUser(user);
    }
}
يعمل! أول تطبيق للإسبات - 15ماذا لو قمت بحذف مستخدم؟ لنقم بمسح جدول المستخدمين (سوف تقوم السيارات بمسح نفسها) وتنفيذ التعليمات البرمجية
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("Masha",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("Sasha");
        userService.updateUser(user);
        userService.deleteUser(user);
    }
}
وجداولنا فارغة تماما (انتبه إلى وحدة التحكم، سيتم عرض جميع الاستعلامات التي نفذها السبات هناك). يمكنك تجربة التطبيق وتجربة جميع ميزاته. على سبيل المثال، قم بإنشاء مستخدم لديه أجهزة، واحفظه في قاعدة البيانات، واطلع على المعرف المخصص له، وحاول استخدام الطريقة main()"لسحب" المستخدم من قاعدة البيانات بواسطة هذا المعرف وعرض قائمة بأجهزته في وحدة التحكم . وبطبيعة الحال، لم نر سوى جزء صغير من وظائف السبات. إمكانياتها واسعة جدًا، وكانت منذ فترة طويلة أحد معايير الصناعة لتطوير Java. إذا كنت ترغب في دراستها بالتفصيل، يمكنني أن أوصي بكتاب “Java Persistence API and Hibernate” الذي قمت بمراجعته في إحدى مقالاتي السابقة. آمل أن تكون هذه المقالة مفيدة للقراء. إذا كان لديك أي أسئلة، اطرحها في التعليقات، وسأكون سعيدًا بالإجابة عليها :) ولا تنس أيضًا دعم المؤلف في المسابقة عن طريق "الإعجاب" به. أو الأفضل من ذلك - "أنا أحب ذلك كثيرًا" :) حظًا سعيدًا في دراستك!
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION