في هذه المقالة، سوف تتعرف على أحد أطر عمل المؤسسات الأكثر شيوعًا لـ Java وستنشئ أول تطبيق Hibernate خاص بك. لم تسمع عن السبات؟ أو ربما سمعت عنه ولم تستخدمه؟ أو ربما حاولت استخدامه ولكنك فشلت؟ في جميع الحالات الثلاث - مرحبًا بكم في الجزء السفلي :) مرحبًا بالجميع! في هذه المقالة، سأتحدث عن الميزات الرئيسية لإطار عمل السبات وأساعدك على كتابة تطبيقك المصغر الأول. ولهذا نحتاج إلى:
- IntelliJ IDEA Ultimate Edition
قم بتنزيله من الموقع الرسمي وتفعيل النسخة التجريبية لمدة 30 يومًا. - PostgreSQL - أحد أشهر أنظمة إدارة قواعد البيانات الحديثة (DBMS)
- Maven (متصل بالفعل بـ IDEA)
- القليل من الصبر.
ما هو السبات؟
إنها واحدة من أكثر تطبيقات رسم الخرائط الارتباطية للكائنات (ORM) شيوعًا. يحدد التعيين العلائقي للكائنات العلاقة بين كائنات البرنامج وسجلات قاعدة البيانات. بالطبع، يتمتع Hibernate بوظائف واسعة جدًا، لكننا سنركز على أبسط الوظائف. هدفنا هو إنشاء تطبيق CRUD (الإنشاء والقراءة والتحديث والحذف) الذي سيكون قادرًا على:- إنشاء مستخدمين (User) والبحث عنهم في قاعدة البيانات عن طريق الهوية وتحديث بياناتهم في قاعدة البيانات وحذفهم من قاعدة البيانات.
- تعيين كائنات السيارة (تلقائي) للمستخدمين. إنشاء وتحديث والعثور على وحذف السيارات من قاعدة البيانات.
- بالإضافة إلى ذلك، يجب أن يقوم التطبيق تلقائيًا بإزالة السيارات "غير المالكة" من قاعدة البيانات. بمعنى آخر، عند حذف مستخدم، يجب أيضًا حذف جميع السيارات التابعة لذلك المستخدم من قاعدة البيانات.
com.yourNickname.codegym
. وهذا لن يكون له أي تأثير على التطبيق. بالنسبة إلى artifactId، اختر أي اسم مشروع تريده. يمكن ترك النسخة دون تغيير. في الشاشة الأخيرة، ما عليك سوى تأكيد البيانات التي تم إدخالها مسبقًا. لذلك أنشأنا المشروع. الآن كل ما تبقى علينا فعله هو كتابة بعض التعليمات البرمجية وتشغيلها :) أول الأشياء أولاً: إذا أردنا إنشاء تطبيق يعمل مع قاعدة بيانات، فلا يمكننا بالتأكيد الاستغناء عن قاعدة البيانات! قم بتنزيل PostgreSQL من هنا
(أنا أستخدم الإصدار 9). يحتوي PostgreSQL على المستخدم الافتراضي "postgres" - ستحتاج إلى التفكير في كلمة مرور له عند التثبيت. لا تنسى كلمة المرور. سنحتاجها لاحقًا! (بشكل عام، يعد استخدام قاعدة البيانات الافتراضية في التطبيقات ممارسة سيئة، ولكننا سنفعل ذلك لتقليل عدد أسباب القرحة عن طريق إنشاء قاعدة البيانات الخاصة بك). إذا لم تكن صديقًا لسطر الأوامر واستعلامات SQL، فهناك أخبار جيدة. يوفر IntelliJ IDEA واجهة مستخدم مناسبة تمامًا للعمل مع قاعدة البيانات. يبدو الأمر كما يلي: (موجود في الجزء الأيمن من IDEA، علامة التبويب قاعدة البيانات). لإنشاء اتصال، انقر فوق "+" وحدد مصدر بياناتنا (PostgeSQL). املأ الحقول الخاصة بالمستخدم وقاعدة البيانات ("postgres" لكليهما) وأدخل كلمة المرور التي تم تعيينها أثناء تثبيت PostgreSQL. إذا لزم الأمر، قم بتنزيل برنامج تشغيل Postgres. يمكنك القيام بذلك على نفس الصفحة. انقر فوق "اختبار الاتصال" للتحقق من إنشاء اتصال قاعدة البيانات. إذا رأيت "ناجح"، فانتقل. الآن سنقوم بإنشاء الجداول التي نحتاجها. سيكون هناك إجمالي اثنين: المستخدمين والسيارات. معلمات جدول المستخدمين: لاحظ أن المعرف هو المفتاح الأساسي. إذا كنت لا تعرف ما هو المفتاح الأساسي في SQL، فابحث عنه في Google. هذا مهم. إعدادات جدول السيارات: بالنسبة لجدول السيارات، تحتاج إلى تكوين مفتاح خارجي. سيكون بمثابة ربط جداولنا. أنصحك بقراءة المزيد عنها. ببساطة، فهو يشير إلى جدول خارجي، في حالتنا، المستخدمين. إذا كانت السيارة مملوكة للمستخدم بالمعرف = 1، فسيكون حقل user_id الخاص بالسيارات مساويًا لـ 1. وهذه هي الطريقة التي نربط بها المستخدمين بسياراتهم في تطبيقنا. في جدول السيارات الخاص بنا، سيكون حقل user_id بمثابة المفتاح الخارجي. سيشير إلى حقل المعرف الخاص بجدول المستخدمين. لذلك، قمنا بإنشاء قاعدة بيانات تحتوي على جدولين. ما تبقى هو فهم كيفية إدارتها من كود Java. سنبدأ بملف pom.xml، حيث نحتاج إلى تضمين المكتبات الضرورية (في Maven تسمى التبعيات). يتم تخزين جميع المكتبات في مستودع Maven المركزي. المكتبات التي تحددها في pom.xml متاحة لك لاستخدامها في المشروع. يجب أن يبدو ملف pom.xml الخاص بك بهذا الشكل: لا شيء معقد، كما ترون. أضفنا تبعيتين فقط — لاستخدام PostgreSQL وHbernate. الآن دعنا ننتقل إلى كود جافا. إنشاء كافة الحزم والفئات اللازمة في المشروع. للبدء، نحتاج إلى نموذج بيانات: the User
و Auto
classes.
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;
}
}
كما ترون، تحتوي الفصول الدراسية على مجموعة من التعليقات التوضيحية الغامضة. دعونا نبدأ في الحفر فيها. بالنسبة لنا، التعليق التوضيحي الرئيسي هو @Entity. اقرأ عنها على ويكيبيديا واحفظها عن ظهر قلب. هذا هو أساس الأساس. يتيح هذا التعليق التوضيحي إمكانية تعيين كائنات فئة Java الخاصة بك إلى قاعدة بيانات. لكي تكون الفئة كيانًا، يجب أن تستوفي المتطلبات التالية:
- يجب أن يحتوي على مُنشئ فارغ (
public
أوprotected
) - لا يمكن أن تكون متداخلة، واجهة أو
enum
- لا يمكن أن يكون
final
ولا يمكن أن يحتوي علىfinal
حقول/خصائص - يجب أن يحتوي على حقل @Id واحد على الأقل.
- يمكن أن تحتوي على مُنشئات غير فارغة
- يمكن أن ترث وتكون موروثة
- يمكن أن يكون لها طرق أخرى وتنفيذ واجهات.
User
الفصل مشابه جدًا لجدول المستخدمين. لديها id
, name
, والحقول age
. لا تحتاج التعليقات التوضيحية الموجودة فوقها إلى أي شرح خاص: من الواضح أن @Id يشير إلى أن الحقل عبارة عن معرف لكائنات من هذه الفئة. يشير التعليق التوضيحي @Table الموجود أعلى الفئة إلى اسم الجدول الذي تمت كتابة الكائنات فيه. لاحظ التعليق الموجود أعلى حقل العمر: إذا كان اسم الحقل الموجود في الفصل هو نفس اسم الجدول، فيمكنك حذف التعليق التوضيحي @Column وسيعمل. أما بالنسبة للجزء المشار إليه بين الأقواس ("الاستراتيجية = GenerationType.IDENTITY"): هناك عدة استراتيجيات لتوليد المعرفات. يمكنك البحث عنها عبر جوجل، لكن بالنسبة لتطبيقنا، لا داعي للقلق. الشيء الرئيسي هو أنه بالنسبة لكائناتنا، سيتم إنشاء قيمة المعرف تلقائيًا. وفقًا لذلك، لا يوجد محدد للمعرف، ولا نقوم بتعيينه في المُنشئ أيضًا. ومع ذلك، هناك بعض الطرق التي User
يبرز بها الفصل. لديها قائمة من السيارات!
التعليق التوضيحي @OneToMany معلق أعلى القائمة. هذا يعني أن عدة سيارات يمكن أن تتوافق مع نفس كائن فئة المستخدم. يشير العنصر "mappedBy" إلى حقل المستخدم الخاص بالفئة Auto
. وهكذا، ترتبط السيارات والمستخدمين. يشير العنصر orphanRemoval إلى ما إذا كان سيتم تطبيق عملية الإزالة على الكيانات التي لم تعد لها علاقة. إذا قمنا بحذف مستخدم من قاعدة البيانات، فسيتم أيضًا حذف جميع السيارات المرتبطة به. في المقابل، في 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 الخاص. تتم قراءة Hibernate.cfg.xml هنا: new Configuration().configure();
كما ترون، لا يوجد شيء خاص به: فهو يحتوي على معلمات للاتصال بقاعدة البيانات، بالإضافة إلى المعلمة show_sql. يعد هذا مطلوبًا حتى يتم عرض جميع استعلامات SQL التي يتم تنفيذها بواسطة Hibernate على وحدة التحكم. بهذه الطريقة سترى بالضبط ما يفعله Hibernate في أي لحظة، مما يزيل أي إحساس بـ "السحر". بعد ذلك نحتاج إلى UserDAO
الفصل. أفضل الممارسات هي البرمجة من خلال الواجهات — إنشاء UserDAO
واجهة منفصلة UserDAOImpl
وتنفيذها، لكنني سأتخطى ذلك لتقليل كمية التعليمات البرمجية. لا تفعل هذا في المشاريع الحقيقية! يعد نمط تصميم DAO (كائن الوصول إلى البيانات) أحد أكثر الأنماط شيوعًا. الفكرة بسيطة – قم بإنشاء طبقة تطبيق مسؤولة فقط عن الوصول إلى البيانات، لا أكثر. جلب البيانات من قاعدة البيانات، وتحديث البيانات، وحذف البيانات - هذا كل شيء. دراسة المزيد عن DAO. ستستخدم كائنات الوصول إلى البيانات باستمرار في عملك. ماذا يمكن 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 وتستدعيها في أساليبها. قد يبدو أننا نكرر الوظائف هنا (لماذا لا نستدعي الأساليب من كائن 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 ("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);
}
}
كما ترون، جدول المستخدمين له سجله الخاص، وجدول السيارات له سجله الخاص. دعونا نحاول إعادة تسمية مستخدمنا. امسح جدول المستخدمين وقم بتنفيذ التعليمات البرمجية
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);
}
}
إنها تعمل! ماذا لو قمت بحذف المستخدم؟ امسح جدول المستخدمين (سوف تقوم السيارات بمسح نفسها) وتنفيذ التعليمات البرمجية
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);
}
}
وجداولنا فارغة تمامًا (انتبه إلى وحدة التحكم - سيتم عرض جميع الطلبات التي يؤديها Hibernate هناك). يمكنك تجربة التطبيق وتجربة جميع وظائفه. على سبيل المثال، قم بإنشاء مستخدم بسيارات، واحفظه في قاعدة البيانات، وانظر إلى المعرف المخصص للمستخدم، وحاول استخدام هذا المعرف في طريقة main()
جلب المستخدم من قاعدة البيانات وعرض قائمة بسياراته على وحدة التحكم . بالطبع، لم نر سوى جزء صغير من وظائف السبات. إمكانياتها واسعة جدًا، وكانت منذ فترة طويلة أداة صناعية قياسية لتطوير Java. إذا كنت ترغب في دراستها بالتفصيل، يمكنني أن أوصي بكتاب "Java Persistence API and Hibernate". لقد راجعت في مقال سابق. آمل أن تكون هذه المقالة مفيدة للقراء. إذا كان لديك أسئلة، اطرحها في التعليقات. سأكون سعيدًا بالإجابة :) ولا تنس أيضًا دعم المؤلف في المسابقة بنشر "أعجبني". أو الأفضل من ذلك - "أحبها" :) حظًا سعيدًا في دراستك!
GO TO FULL VERSION