تم تخصيص هذا النص لمناهج مختلفة للتحقق من صحة البيانات: ما هي المآزق التي قد يتعثر فيها المشروع وما الأساليب والتقنيات التي يجب اتباعها عند التحقق من صحة البيانات في تطبيقات Java.

غالبًا ما رأيت المشاريع التي لم يكلف منشئوها عناء اختيار نهج للتحقق من صحة البيانات. عملت الفرق على المشروع تحت ضغط لا يصدق في شكل مواعيد نهائية ومتطلبات غامضة ، ونتيجة لذلك ، لم يكن لديهم ببساطة الوقت للتحقق الدقيق والمتسق. لذلك ، فإن كود التحقق الخاص بهم مبعثر في كل مكان: في مقتطفات جافا سكريبت ، وحدات تحكم الشاشة ، في صناديق منطق الأعمال ، كيانات المجال ، المشغلات وقيود قاعدة البيانات. كان هذا الرمز مليئًا بعبارات if-else ، فقد طرح مجموعة من الاستثناءات ، وحاول معرفة أين يتم التحقق من صحة جزء معين من البيانات هناك ... ونتيجة لذلك ، مع تطور المشروع ، يصبح من الصعب والمكلف الامتثال للمتطلبات (غالبًا ما تكون مربكة للغاية) ، و توحيد نهج التحقق من صحة البيانات.
فهل هناك طريقة بسيطة وأنيقة للتحقق من صحة البيانات؟ هل هناك طريقة تحمينا من خطيئة عدم قابلية القراءة ، طريقة تجمع كل منطق التحقق معًا ، والتي تم إنشاؤها بالفعل لنا من قبل مطوري أطر جافا المشهورة؟
نعم ، هناك مثل هذه الطريقة.
بالنسبة لنا ، مطوري منصة CUBA ، من المهم جدًا استخدام أفضل الممارسات. نعتقد أن رمز التحقق يجب أن:
- تكون قابلة لإعادة الاستخدام واتبع مبدأ DRY ؛
- كن طبيعيًا ومفهومًا ؛
- وضعت في المكان الذي يتوقع المطور رؤيته ؛
- القدرة على التحقق من البيانات من مصادر مختلفة: واجهة المستخدم ، مكالمات SOAP ، REST ، إلخ.
- العمل في بيئة متعددة الخيوط دون مشاكل ؛
- يتم الاتصال بداخل التطبيق تلقائيًا ، دون الحاجة إلى تشغيل الشيكات يدويًا ؛
- لمنح المستخدم رسائل واضحة ومترجمة في مربعات حوار موجزة ؛
- اتبع المعايير.
دعونا نرى كيف يمكن تنفيذ ذلك باستخدام تطبيق مثال مكتوب باستخدام إطار عمل CUBA Platform. ومع ذلك ، نظرًا لأن CUBA يعتمد على Spring و EclipseLink ، فإن معظم التقنيات المستخدمة هنا ستعمل على أي منصة Java أخرى تدعم مواصفات JPA و Bean Validation.
التحقق باستخدام قيود قاعدة البيانات
ربما تكون الطريقة الأكثر شيوعًا وواضحة للتحقق من البيانات هي استخدام القيود على مستوى قاعدة البيانات ، على سبيل المثال ، العلامة المطلوبة (للحقول التي لا يمكن أن تكون قيمتها فارغة) ، وطول السلسلة ، والفهارس الفريدة ، وما إلى ذلك. هذه الطريقة هي الأكثر ملاءمة لتطبيقات المؤسسات ، لأن هذا النوع من البرامج يركز بشكل صارم على معالجة البيانات. ومع ذلك ، حتى هنا غالبًا ما يرتكب المطورون أخطاء عن طريق وضع حدود بشكل منفصل لكل مستوى من مستويات التطبيق. في معظم الأحيان ، يكمن السبب في توزيع المسؤوليات بين المطورين.
ضع في اعتبارك مثالًا يعرفه معظمنا ، حتى البعض من تجربتنا الخاصة ... إذا كانت المواصفات تقول أنه يجب أن يكون هناك 10 أحرف في حقل رقم جواز السفر ، فمن المحتمل جدًا أن يتم التحقق من ذلك من قبل الجميع: مهندس DB في DDL ، مطور الخلفية في الكيان المقابل و خدمات REST ، وأخيرًا ، مطور واجهة المستخدم مباشرة من جانب العميل. ثم يتغير هذا المتطلب ، ويزداد الحقل إلى 15 حرفًا. يغير Devops قيم القيود في قاعدة البيانات ، ولكن لا يتغير شيء للمستخدم ، لأن التقييد هو نفسه من جانب العميل ...
أي مطور يعرف كيف يتجنب هذه المشكلة - يجب أن تكون عملية التحقق مركزية! في CUBA ، تم العثور على هذا التحقق في التعليقات التوضيحية لكيان JPA. استنادًا إلى هذه المعلومات الوصفية ، سينشئ CUBA Studio نص DDL الصحيح ويطبق أدوات التحقق المناسبة من جانب العميل.

إذا تغيرت التعليقات التوضيحية ، فسيقوم CUBA بتحديث البرامج النصية لـ DDL وإنشاء البرامج النصية للترحيل ، لذلك في المرة القادمة التي تقوم فيها بنشر المشروع ، ستدخل القيود الجديدة المستندة إلى JPA حيز التنفيذ في كل من الواجهة وقاعدة بيانات التطبيق.
على الرغم من البساطة والتنفيذ على مستوى قاعدة البيانات ، مما يعطي موثوقية مطلقة لهذه الطريقة ، فإن نطاق التعليقات التوضيحية لـ JPA يقتصر على أبسط الحالات التي يمكن التعبير عنها في معيار DDL ولا تتضمن مشغلات قاعدة البيانات أو الإجراءات المخزنة. لذلك ، يمكن للقيود المستندة إلى JPA أن تجعل حقل الكيان فريدًا أو إلزاميًا أو أن تحدد أقصى طول للعمود. يمكنك أيضًا تعيين قيود فريدة على مجموعة الأعمدة باستخدام التعليق التوضيحي @UniqueConstraint
. ولكن هذا كل شيء على الأرجح.
مع ذلك ، في الحالات التي تتطلب منطق تحقق أكثر تعقيدًا ، مثل التحقق من حقل للحصول على قيمة الحد الأدنى / الأقصى ، أو التحقق باستخدام تعبير عادي ، أو إجراء فحص مخصص خاص بتطبيقك فقط ، يتم تطبيق النهج المعروف باسم "التحقق من فول" .
التحقق من فول
يعلم الجميع أنه من الممارسات الجيدة اتباع المعايير التي لها دورة حياة طويلة ، والتي أثبتت فعاليتها في آلاف المشاريع. Java Bean Validation هو نهج موثق في JSR 380 و 349 و 303 وتطبيقاتها: Hibernate Validator و Apache BVal .
على الرغم من أن هذا النهج مألوف للعديد من المطورين ، إلا أنه غالبًا ما يتم التقليل من قيمته. هذه طريقة سهلة لتضمين التحقق من صحة البيانات حتى في المشاريع القديمة ، والتي تسمح لك ببناء عمليات التحقق بشكل واضح وبسيط وموثوق وقريب من منطق الأعمال قدر الإمكان.
يمنح استخدام التحقق من فول بين المشروع العديد من المزايا:
- يقع منطق التحقق بجوار مجال الموضوع: تعريف القيود على حقول وأساليب الحاوية يحدث بطريقة طبيعية وموجهة نحو الكائن.
- يمنحنا معيار Bean Validation العشرات من التعليقات التوضيحية للتحقق من الصندوق ، على سبيل المثال:
@NotNull
و @Size
و @Min
و @Max
و @Pattern
و @Email
و @Past
وليس قياسيًا تمامًا @URL
و @Length
وأقوى @ScriptAssert
وغيرها الكثير . - لا يقتصر المعيار على التعليقات التوضيحية الجاهزة ويسمح لنا بإنشاء الخاصة بنا. يمكننا أيضًا إنشاء تعليق توضيحي جديد من خلال دمج العديد من التعليقات الأخرى ، أو تعريفها باستخدام فئة Java منفصلة كمدقق.
على سبيل المثال ، في المثال أعلاه ، يمكننا ضبط التعليق التوضيحي @ValidPassportNumber
فئة @ValidPassportNumber
للتحقق من أن رقم جواز السفر يطابق التنسيق اعتمادًا على قيمة حقل country
. - يمكن تعيين القيود ليس فقط على الحقول أو الفئات ، ولكن أيضًا على الأساليب ومعلماتها. يسمى هذا النهج "التحقق من الصحة بموجب عقد" وسيتم مناقشته بعد قليل.
عندما يرسل المستخدم المعلومات التي تم إدخالها ، تبدأ منصة CUBA (مثل بعض الأطر الأخرى) التحقق من Bean تلقائيًا ، لذا فإنها تعرض على الفور رسالة خطأ إذا فشل التحقق ، ولا نحتاج إلى تشغيل أداة التحقق من سلة المحذوفات يدويًا.
دعونا نعود إلى المثال برقم جواز السفر ، ولكن هذه المرة سنكمله بالعديد من القيود على كيان الشخص:
- يجب أن يتكون حقل
name
من حرفين أو أكثر ويجب أن يكون صالحًا. (كما ترى ، regexp ليس بسيطًا ، لكن "Charles Ogier de Batz de Castelmore Comte d'Artagnan" سيجتاز الاختبار ، ولكن "R2D2" لن ينجح) ؛ - يجب أن يكون
height
(الارتفاع) في الفترة التالية: 0 < height <= 300
سم ؛ - يجب أن يحتوي حقل
email
على سلسلة تطابق تنسيق البريد الإلكتروني الصحيح.
مع كل هذه الاختبارات ، ستبدو فئة الشخص كما يلي:
@Listeners("passportnumber_PersonEntityListener") @NamePattern("%s|name") @Table(name = "PASSPORTNUMBER_PERSON") @Entity(name = "passportnumber$Person") @ValidPassportNumber(groups = {Default.class, UiCrossFieldChecks.class}) @FraudDetectionFlag public class Person extends StandardEntity { private static final long serialVersionUID = -9150857881422152651L; @Pattern(message = "Bad formed person name: ${validatedValue}", regexp = "^[AZ][az]*(\\s(([az]{1,3})|(([az]+\\')?[AZ][az]*)))*$") @Length(min = 2) @NotNull @Column(name = "NAME", nullable = false) protected String name; @Email(message = "Email address has invalid format: ${validatedValue}", regexp = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") @Column(name = "EMAIL", length = 120) protected String email; @DecimalMax(message = "Person height can not exceed 300 centimeters", value = "300") @DecimalMin(message = "Person height should be positive", value = "0", inclusive = false) @Column(name = "HEIGHT") protected BigDecimal height; @NotNull @Column(name = "COUNTRY", nullable = false) protected Integer country; @NotNull @Column(name = "PASSPORT_NUMBER", nullable = false, length = 15) protected String passportNumber; ... }
Person.java
أعتقد أن استخدام التعليقات التوضيحية مثل @NotNull
و @DecimalMin
@Length
و @Pattern
و @Pattern
وما شابه ذلك واضح تمامًا ولا يتطلب أي تعليقات. دعنا نلقي نظرة فاحصة على تنفيذ التعليق التوضيحي @ValidPassportNumber
.
يتحقق علامتنا التجارية الجديدة @ValidPassportNumber
أن Person#passportNumber
يتطابق مع نمط regexp لكل بلد محدد في حقل Person#country
.
أولاً ، دعونا نلقي نظرة على الوثائق (كتيبات CUBA أو Hibernate جيدة) ، وفقًا لها ، نحتاج إلى وضع علامة على صفنا بهذا التعليق التوضيحي الجديد وتمرير معلمة groups
إليه ، حيث تعني UiCrossFieldChecks.class
أنه يجب تشغيل هذا التحقق في التحقق من الصحة - بعد التحقق من جميع الحقول الفردية ، ويحفظ Default.class
التقييد في مجموعة التحقق من الصحة الافتراضية.
يبدو وصف التعليق التوضيحي كما يلي:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = ValidPassportNumberValidator.class) public @interface ValidPassportNumber { String message() default "Passport number is not valid"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
ValidPassportNumber.java
هنا @Target(ElementType.TYPE)
أن الغرض من التعليق التوضيحي لوقت التشغيل هذا هو الفئة ، و @Constraint(validatedBy = … )
يحدد أن التحقق يتم من خلال فئة ValidPassportNumberValidator
التي تطبق واجهة ConstraintValidator<...>
. رمز التحقق نفسه في طريقة isValid(...)
، التي تقوم بالتحقق الفعلي بطريقة مباشرة إلى حد ما:
public class ValidPassportNumberValidator implements ConstraintValidator<ValidPassportNumber, Person> { public void initialize(ValidPassportNumber constraint) { } public boolean isValid(Person person, ConstraintValidatorContext context) { if (person == null) return false; if (person.country == null || person.passportNumber == null) return false; return doPassportNumberFormatCheck(person.getCountry(), person.getPassportNumber()); } private boolean doPassportNumberFormatCheck(CountryCode country, String passportNumber) { ... } }
ValidPassportNumberValidator.java
هذا كل شيء. باستخدام CUBA Platform ، لا نحتاج إلى كتابة أي شيء سوى سطر من الكود البرمجي الذي سيعمل على التحقق من الصحة المخصص لدينا ويعطي رسائل خطأ المستخدم.
لا شيء معقد ، أليس كذلك؟
الآن دعونا نرى كيف يعمل كل شيء. هنا لدى CUBA nishtyaki أخرى: فهي لا تُظهر للمستخدم رسالة خطأ فحسب ، بل تُبرز أيضًا في الحقول الحمراء التي لم تجتاز التحقق من الفول:

أليس هذا حلًا أنيقًا؟ يمكنك الحصول على عرض ملائم لأخطاء التحقق في واجهة المستخدم عن طريق إضافة بضع تعليقات توضيحية من Java إلى كيانات مجال الموضوع.
لتلخيص القسم ، دعونا نذكر بإيجاز مزايا التحقق من فول للكيانات مرة أخرى:
- أنها مفهومة وقابلة للقراءة.
- يتيح لك تحديد قيود القيمة مباشرة في فئات الكيانات ؛
- يمكن تخصيصها واستكمالها ؛
- مدمجة في ORMs الشائعة ، ويتم تشغيل عمليات الفحص تلقائيًا قبل حفظ التغييرات في قاعدة البيانات ؛
- تقوم بعض الأطر أيضًا بتشغيل التحقق من الفول تلقائيًا عندما يرسل المستخدم البيانات إلى واجهة المستخدم (وإذا لم يكن الأمر كذلك ، فمن السهل استدعاء واجهة
Validator
يدويًا) ؛ - التحقق من فول هو معيار معترف به ومليء بالوثائق على الإنترنت.
ولكن ماذا لو كنت بحاجة إلى تعيين قيود على طريقة أو منشئ أو عنوان REST للتحقق من صحة البيانات الواردة من نظام خارجي؟ أو إذا كنت بحاجة إلى التحقق من قيم معلمات الطريقة بشكل توضيحي ، دون كتابة شفرة مملة مع العديد من شروط if-else في كل طريقة يتم اختبارها؟
الجواب بسيط: ينطبق التحقق من الفول على الأساليب أيضًا!
المصادقة عن طريق العقد
في بعض الأحيان تحتاج إلى تجاوز التحقق من حالة نموذج البيانات. قد تستفيد العديد من الطرق من التحقق التلقائي من المعلمات وقيمة الإرجاع. قد يكون هذا ضروريًا ليس فقط للتحقق من البيانات التي تتجه إلى عناوين REST أو SOAP ، ولكن أيضًا في تلك الحالات عندما نريد كتابة الشروط المسبقة والشروط اللاحقة لاستدعاءات الطريقة للتأكد من أن البيانات التي تم إدخالها تم التحقق منها قبل تنفيذ نص الطريقة ، أو أن القيمة المرتجعة في النطاق المتوقع ، أو على سبيل المثال ، نحتاج فقط إلى وصف نطاقات قيم معلمات الإدخال بشكل تعريفي لتحسين إمكانية قراءة التعليمات البرمجية.
باستخدام التحقق من الفول ، يمكن تطبيق القيود على معلمات الإدخال وقيم الإرجاع للطرق والمنشئات للتحقق من الشروط المسبقة والظروف اللاحقة لمكالماتهم في أي فئة Java. يحتوي هذا المسار على مزايا عديدة مقارنة بالطرق التقليدية للتحقق من صحة المعلمات وقيم الإرجاع:
- ليس من الضروري إجراء عمليات فحص يدويًا بأسلوب إلزامي (على سبيل المثال ، من خلال إلقاء
IllegalArgumentException
وما شابه). يمكنك تحديد القيود بشكل توضيحي ، وجعل الشفرة أكثر فهمًا وتعبيرًا ؛ - يمكن تكوين القيود وإعادة استخدامها وتكوينها: لا تحتاج إلى كتابة منطق التحقق لكل عملية تحقق. رمز أقل يعني أخطاء أقل.
- إذا تم وضع علامة على الفئة أو القيمة
@Validated
للطريقة أو المعلمة الخاصة بها @Validated
التوضيحي @Validated
، فسيتم إجراء عمليات الفحص تلقائيًا بواسطة النظام الأساسي في كل مرة يتم فيها استدعاء الطريقة. - إذا تم تمييز الملف القابل للتنفيذ
@Documented
التوضيحي @Documented
، فسيتم تضمين الشروط المسبقة واللاحقة في JavaDoc الذي تم إنشاؤه.
باستخدام "التحقق من صحة العقد" ، نحصل على كود واضح ومدمج وصيانته بسهولة.
على سبيل المثال ، دعنا نلقي نظرة على واجهة وحدة تحكم REST لتطبيق CUBA. تتيح لك واجهة PersonApiService
الحصول على قائمة بالأشخاص من قاعدة البيانات باستخدام طريقة getPersons()
وإضافة شخص جديد باستخدام addNewPerson(...)
.
ولا تنس أن التحقق من الفاصوليا موروث! بمعنى آخر ، إذا وضعنا تعليقًا توضيحيًا على فئة أو حقل أو طريقة معينة ، فستخضع جميع الفئات التي ترث هذه الفئة أو تنفذ هذه الواجهة إلى نفس التعليق التوضيحي للتحقق.
@Validated public interface PersonApiService { String NAME = "passportnumber_PersonApiService"; @NotNull @Valid @RequiredView("_local") List<Person> getPersons(); void addNewPerson( @NotNull @Length(min = 2, max = 255) @Pattern(message = "Bad formed person name: ${validatedValue}", regexp = "^[AZ][az]*(\\s(([az]{1,3})|(([az]+\\')?[AZ][az]*)))*$") String name, @DecimalMax(message = "Person height can not exceed 300 cm", value = "300") @DecimalMin(message = "Person height should be positive", value = "0", inclusive = false) BigDecimal height, @NotNull CountryCode country, @NotNull String passportNumber ); }
PersonApiService.java
هل هذه الشفرة واضحة بما فيه الكفاية؟
_ (باستثناء التعليق التوضيحي @RequiredView(“_local”)
، الخاص @RequiredView(“_local”)
CUBA والتحقق من أن كائن Person
الذي تم إرجاعه يحتوي على كافة الحقول من جدول PASSPORTNUMBER_PERSON
).
يحدد @Valid
أنه يجب أيضًا التحقق من كل كائن مجموعة تم إرجاعه بواسطة أسلوب getPersons()
مقابل قيود فئة Person
.
في تطبيق CUBA ، تتوفر هذه الطرق على العناوين التالية:
- / app / rest / v2 / services / passportnumber_PersonApiService / getPersons
- / app / rest / v2 / services / passportnumber_PersonApiService / addNewPerson
دعنا نفتح تطبيق Postman ونتأكد من أن التحقق من الصحة يعمل كما ينبغي:

كما لاحظت ، لم يتم التحقق من صحة رقم جواز السفر في المثال أعلاه. وذلك لأن هذا الحقل يتطلب addNewPerson
طريقة addNewPerson
، نظرًا لأن اختيار قالب تعبير عادي للتحقق من صحة passportNumber
يعتمد على قيمة حقل country
. هذا التحقق المتقاطع هو تناظرية كاملة لقيود الكيان على مستوى الفئة!
يدعم JSR 349 و 380 التحقق من صحة المعلمات. يمكنك قراءة وثائق السبات لمعرفة كيفية تطبيق التحقق المتبادل الخاص بك من أساليب الطبقة / الواجهة.
التحقق من الفاصوليا الخارجية
لا يوجد كمال في العالم ، لذا فإن التحقق من الفول له عيوبه وقيوده:
- في بعض الأحيان نحتاج فقط إلى التحقق من حالة رسم بياني معقد للكائنات قبل حفظ التغييرات في قاعدة البيانات. على سبيل المثال ، تحتاج إلى التأكد من وضع جميع عناصر طلب العميل في حزمة واحدة. هذه عملية صعبة نوعًا ما ، وإن تنفيذها في كل مرة يضيف فيها المستخدم عناصر جديدة إلى الطلب ليست فكرة جيدة. لذلك ، قد تكون هناك حاجة لمثل هذا الفحص مرة واحدة فقط: قبل حفظ كائن
Order
OrderItem
الفرعية OrderItem
في قاعدة البيانات. - يجب إجراء بعض الشيكات داخل المعاملة. على سبيل المثال ، يجب أن يتحقق نظام المخزن الإلكتروني من وجود نسخ كافية من البضائع من أجل تنفيذ الطلب قبل إلزامه بقاعدة البيانات. لا يمكن إجراء مثل هذا الشيك إلا داخل المعاملة ، لأنه النظام متعدد الخيوط وقد تتغير كمية البضائع الموجودة في المخزون في أي وقت.
تقدم منصة CUBA آليتين للتحقق من البيانات قبل الالتزام تسمى مستمعي الكيانات ومستمعي المعاملات . دعونا ننظر فيها بمزيد من التفصيل.
قوائم كيانات
إن مستمعي الكيانات في CUBA PreInsertEvent
إلى حد كبير PreInsertEvent
PreUpdateEvent
و PredDeleteEvent
و PredDeleteEvent
التي تقدمها JPA للمطور. تسمح لك كلتا الآليتين بفحص كائنات الكيان قبل وبعد تخزينها في قاعدة البيانات.
في CUBA ، من السهل إنشاء وتوصيل مستمع كيان ، لهذا تحتاج إلى شيئين:
- قم بإنشاء وحدة تحكم مُدارة تقوم بتنفيذ إحدى واجهات مستمع الكيان. 3 واجهات مهمة للتحقق من الصحة:
BeforeDeleteEntityListener<T>
،
BeforeInsertEntityListener<T>
،
BeforeUpdateEntityListener<T>
- قم بإضافة التعليق التوضيحي
@Listeners
إلى كائن الكيان الذي تخطط لتتبعه.
هذا كل ما في الأمر.
مقارنة بمعيار JPA (JSR 338 ، القسم 3.5) ، يتم كتابة واجهات مستمع منصة CUBA ، لذلك لا تحتاج إلى إرسال وسيطة من نوع Object
إلى نوع كيان لبدء العمل معه. تضيف منصة CUBA الكيانات ذات الصلة أو المتصلين EntityManager القدرة على تحميل وتعديل الكيانات الأخرى. كل هذه التغييرات ستستدعي أيضًا مستمع الكيان المقابل.
تدعم منصة CUBA أيضًا "الحذف البسيط" ، وهو نهج يتم فيه بدلاً من حذف السجلات من قاعدة البيانات فعلاً ، أن يتم وضع علامة عليها على أنها محذوفة فقط ولا يمكن الوصول إليها للاستخدام العادي. لذلك ، للحذف PreUpdate
، تستدعي المنصة المستمعين AfterDeleteEntityListener
/ AfterDeleteEntityListener
، بينما تستدعي PreUpdate
PostUpdate
/ PostUpdate
.
دعونا نلقي نظرة على مثال. هنا ، تتصل وحدة @Listeners
المستمع للحدث بفئة الكيان من خلال سطر واحد فقط من التعليمات البرمجية: التعليق التوضيحي @Listeners
، الذي يأخذ اسم فئة المستمع:
@Listeners("passportnumber_PersonEntityListener") @NamePattern("%s|name") @Table(name = "PASSPORTNUMBER_PERSON") @Entity(name = "passportnumber$Person") @ValidPassportNumber(groups = {Default.class, UiCrossFieldChecks.class}) @FraudDetectionFlag public class Person extends StandardEntity { ... }
Person.java
يبدو تطبيق المستمع نفسه كما يلي:
@Component("passportnumber_PersonEntityListener") public class PersonEntityListener implements BeforeDeleteEntityListener<Person>, BeforeInsertEntityListener<Person>, BeforeUpdateEntityListener<Person> { @Override public void onBeforeDelete(Person person, EntityManager entityManager) { if (!checkPassportIsUnique(person.getPassportNumber(), person.getCountry(), entityManager)) { throw new ValidationException( "Passport and country code combination isn't unique"); } } @Override public void onBeforeInsert(Person person, EntityManager entityManager) {
PersonEntityListener.java
مستمعي الكيانات خيار رائع إذا:
- من الضروري التحقق من البيانات داخل المعاملة قبل تخزين كائن الكيان في قاعدة البيانات ؛
- من الضروري التحقق من البيانات في قاعدة البيانات أثناء عملية التحقق ، على سبيل المثال ، للتحقق من وجود منتج كافٍ في المخزون لقبول الطلب ؛
- تحتاج إلى النظر ليس فقط في كائن الكيان ، مثل
Order
، ولكن أيضًا في الكيانات ذات الصلة ، على سبيل المثال ، OrderItems
Order
لكيان Order
؛ - نريد تتبع عمليات الإدراج أو التحديث أو الحذف لفئات كيانات معينة فقط ، على سبيل المثال ، فقط
OrderItem
Order
و OrderItem
، ولا نحتاج إلى التحقق من التغييرات في فئات الكيانات الأخرى أثناء المعاملة.
مستمعي المعاملات
يعمل مستمعو المعاملات لـ CUBA أيضًا في سياق المعاملات ، ولكن بالمقارنة مع مستمعي الكيانات ، يتم استدعاؤهم لكل معاملة قاعدة بيانات.
وهذا يمنحهم قوة خارقة:
- لا شيء يمكن أن يهرب من انتباههم.
ولكن يتم تحديد الشيء نفسه من خلال أوجه القصور الخاصة بهم:
- هم أكثر صعوبة في الكتابة ؛
- يمكنهم تقليل الأداء بشكل كبير ؛
- يجب كتابتها بعناية فائقة: يمكن أن يعيق خلل في مستمع المعاملات التحميل الأولي للتطبيق.
لذا ، يعد مستمعو المعاملات حلاً جيدًا عندما تحتاج إلى فحص أنواع مختلفة من الكيانات باستخدام نفس الخوارزمية ، على سبيل المثال ، فحص جميع البيانات بحثًا عن الاحتيال عبر الإنترنت من خلال خدمة واحدة تخدم جميع أهداف عملك.

ألق نظرة على عينة تتحقق لمعرفة ما إذا كان الكيان يحتوي على تعليق توضيحي @FraudDetectionFlag
، ويبدأ ، إن وجد ، في الكشف عن الاحتيال. أكرر: ضع في اعتبارك أن هذه الطريقة يتم استدعاؤها على النظام قبل تنفيذ كل معاملة في قاعدة البيانات ، لذا يجب أن يحاول الرمز التحقق من أقل عدد ممكن من الكائنات.
@Component("passportnumber_ApplicationTransactionListener") public class ApplicationTransactionListener implements BeforeCommitTransactionListener { private Logger log = LoggerFactory.getLogger(ApplicationTransactionListener.class); @Override public void beforeCommit(EntityManager entityManager, Collection<Entity> managedEntities) { for (Entity entity : managedEntities) { if (entity instanceof StandardEntity && !((StandardEntity) entity).isDeleted() && entity.getClass().isAnnotationPresent(FraudDetectionFlag.class) && !fraudDetectorFeedAndFastCheck(entity)) { logFraudDetectionFailure(log, entity); String msg = String.format( "Fraud detection failure in '%s' with id = '%s'", entity.getClass().getSimpleName(), entity.getId()); throw new ValidationException(msg); } } } ... }
ApplicationTransactionListener.java
لتصبح مستمعًا للمعاملات ، يجب على الحبة المُدارة تنفيذ واجهة BeforeCommitTransactionListener
وطريقة beforeCommit
. يتم ربط مستمعي المعاملات تلقائيًا عند بدء التطبيق. يسجل CUBA جميع الفئات التي تطبق BeforeCommitTransactionListener
أو AfterCompleteTransactionListener
للمعاملات.
الخلاصة
التحقق من فول (JPA 303 ، 349 و 980) هو نهج يمكن أن يكون بمثابة أساس موثوق لـ 95 ٪ من حالات التحقق من صحة البيانات التي تمت مواجهتها في مشروع الشركة. الميزة الرئيسية لهذا النهج هي أن معظم منطق التحقق من الصحة يتركز مباشرة في فئات نموذج المجال. لذلك ، من السهل العثور عليها ، وسهلة القراءة وسهلة الصيانة. يدعم Spring و CUBA والعديد من المكتبات الأخرى هذه المعايير ويقوم بإجراء عمليات التحقق من الصحة تلقائيًا عند تلقي البيانات على طبقة واجهة المستخدم أو استدعاء طرق التحقق من الصحة أو تخزين البيانات من خلال ORM ، لذلك غالبًا ما تبدو عملية التحقق من الفول سحرية من وجهة نظر المطور.
يرى بعض مطوري البرامج أن التحقق على مستوى فئات نموذج الموضوع غير طبيعي ومعقد للغاية ، ويقولون إن التحقق من صحة البيانات على مستوى واجهة المستخدم هو استراتيجية فعالة إلى حد ما. ومع ذلك ، أعتقد أن العديد من نقاط التحقق في المكونات ووحدات تحكم واجهة المستخدم ليست هي النهج الأكثر عقلانية. , , , , , listener' .
, , :
- JPA , , DDL.
- Bean Validation — , , , . , .
- bean validation, . , , REST.
- Entity listeners: , Bean Validation, . , . Hibernate .
- Transaction listeners — , , . , , .
PS: , Java, , , .
روابط مفيدة