الميزات الجديدة في JPA 2.2

عطلة سعيدة للجميع!

حدث فجأة أن تزامنت بداية المجموعة الثانية "Java Enterprise Developer" مع اليوم 256 من العام. صدفة؟ لا أعتقد ذلك.

حسنًا ، نحن نشارك الاهتمام قبل الأخير: ما هي الأشياء الجديدة التي جلبتها JPA 2.2 - نتائج التدفق ، وتحويل التاريخ المحسّن ، والتعليقات التوضيحية الجديدة - مجرد أمثلة قليلة على التحسينات المفيدة.

دعنا نذهب!

واجهة برمجة تطبيقات Java Persidence API (JPA) هي أحد مواصفات Java EE الأساسية المستخدمة على نطاق واسع في الصناعة. بغض النظر عما إذا كنت تقوم بالتطوير لمنصة Java EE أو لإطار Java البديل ، فإن JPA هو اختيارك لحفظ البيانات. قام JPA 2.1 بتحسين المواصفات ، مما سمح للمطورين بحل المشكلات مثل الإنشاء التلقائي لمخططات قواعد البيانات والعمل الفعال مع الإجراءات المخزنة في قاعدة البيانات. يعمل الإصدار الأحدث ، JPA 2.2 ، على تحسين المواصفات بناءً على هذه التغييرات.
في هذه المقالة سأتحدث عن وظائف جديدة وسأقدم أمثلة من شأنها أن تساعدك على البدء في ذلك. كعينة ، أستخدم مشروع "Java EE 8 Playground" المتوفر على GitHub . يستند تطبيق العينة إلى مواصفات Java EE 8 ويستخدم JavaServer Faces (JSF) و Enterprise JavaBeans (EJB) وإطارات JPA للاستمرارية. يجب أن تكون على دراية بـ JPA لفهم ما يدور حوله.



باستخدام JPA 2.2

يعد الإصدار 2.2 من JPA جزءًا من النظام الأساسي Java EE 8. تجدر الإشارة إلى أن خوادم التطبيقات المتوافقة مع Java EE 8 فقط توفر مواصفات جاهزة للاستخدام خارج الصندوق. في وقت كتابة هذه السطور (أواخر 2017) ، كان هناك عدد غير قليل من خوادم التطبيقات. ومع ذلك ، فإن استخدام JPA 2.2 مع Java EE7 سهل. تحتاج أولاً إلى تنزيل ملفات JAR المناسبة باستخدام Maven Central وإضافتها إلى المشروع. إذا كنت تستخدم Maven في مشروعك ، فأضف الإحداثيات إلى ملف Maven POM:

<dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> <version>2.2</version> </dependency> 

ثم حدد تنفيذ JPA الذي تريد استخدامه. بدءًا من JPA 2.2 ، يحتوي كل من EclipseLink و Hibernate على تطبيقات متوافقة. كأمثلة في هذه المقالة ، أستخدم EclipseLink بإضافة التبعية التالية:

 <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>eclipselink</artifactId> <version>2.7.0 </version> </dependency> 

إذا كنت تستخدم خادمًا متوافقًا مع Java EE 8 ، مثل GlassFish 5 أو Payara 5 ، فيجب أن تكون قادرًا على تحديد المنطقة "المقدمة" لهذه التبعيات في ملف POM. خلاف ذلك ، حدد منطقة "الترجمة" لتضمينها في تجميع المشروع.

جافا 8 التاريخ والوقت

ربما يكون أحد أكثر الإضافات الإيجابية هو Java 8 Date and Time API. منذ إصدار Java SE 8 في عام 2014 ، استخدم المطورون الحلول لاستخدام واجهة برمجة التطبيقات للوقت والتاريخ مع JPA. على الرغم من أن معظم الحلول واضحة إلى حد ما ، إلا أن الحاجة إلى إضافة دعم أساسي لـ Date and Time API المحدثة طال انتظارها. يتضمن دعم JPA لـ Date and Time API الأنواع التالية:

  • java.time.LocalDate
  • java.time.LocalTime
  • java.time.LocalDateTime
  • java.time.OffsetTime
  • java.time.OffsetDateTime

لفهم أفضل ، سأشرح أولاً كيفية عمل دعم واجهة برمجة التطبيقات للتاريخ والوقت بدون JPA 2.2. يمكن أن يعمل JPA 2.1 فقط مع تراكيب التاريخ الأقدم مثل java.util.Date و java.sql.Timestamp . لذلك ، يجب عليك استخدام محول لتحويل التاريخ المخزن في قاعدة البيانات إلى تصميم قديم يدعمه JPA 2.1 ، ثم تحويله إلى واجهة برمجة تطبيقات Date and Time محدثة للاستخدام في التطبيق. قد يبدو محول التاريخ في JPA 2.1 قادرًا على مثل هذا التحويل شيئًا مثل الإدراج 1. يتم استخدام المحول فيه للتحويل بين LocalDate و java.util.Date .

الإدراج 1

 @Converter(autoApply = true) public class LocalDateTimeConverter implements AttributeConverter<LocalDate, Date> { @Override public Date convertToDatabaseColumn(LocalDate entityValue) { LocalTime time = LocalTime.now(); Instant instant = time.atDate(entityValue) .atZone(ZoneId.systemDefault()) .toInstant(); return Date.from(instant); } @Override public LocalDate convertToEntityAttribute(Date databaseValue){ Instant instant = Instant.ofEpochMilli(databaseValue.getTime()); return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate(); } } 

لم يعد JPA 2.2 بحاجة إلى كتابة مثل هذا المحول ، لأنك تستخدم أنواع الوقت والتاريخ المدعومة. دعم هذه الأنواع مدمج ، لذلك يمكنك ببساطة تحديد النوع المدعوم في حقل فئة الكيان بدون رمز إضافي. يوضح مقتطف الرمز أدناه هذا المفهوم. لاحظ أنه ليست هناك حاجة لإضافة تعليق توضيحي إلى الرمز @Temporal ، لأن تعيين النوع يحدث تلقائيًا.

 public class Job implements Serializable { . . . @Column(name = "WORK_DATE") private LocalDate workDate; . . . } 

نظرًا لأن أنواع الوقت والتاريخ المدعومة هي كائنات من الدرجة الأولى في JPA ، يمكن تحديدها بدون احتفالات إضافية. في JPA @Temporal يجب وصف التعليق التوضيحي في جميع الحقول والخصائص الثابتة java.util.Calendar و java.util.Calendar .

تجدر الإشارة إلى أنه يتم دعم بعض أنواع وقت التاريخ فقط في هذا الإصدار ، ولكن يمكن إنشاء محول السمات بسهولة للعمل مع أنواع أخرى ، على سبيل المثال ، لتحويل LocalDateTime إلى ZonedDateTime . أكبر مشكلة في كتابة هذا المحول هي تحديد أفضل طريقة للتحويل بين الأنواع المختلفة. لتسهيل الأمور ، يمكن الآن تنفيذ محولات السمات. سأقدم مثالا على التنفيذ أدناه.

يوضح LocalDateTime ZonedDateTime 2 كيفية تحويل الوقت من LocalDateTime إلى ZonedDateTime .

قائمة 2

 @Converter public class LocalToZonedConverter implements AttributeConverter<ZonedDateTime, LocalDateTime> { @Override public LocalDateTime convertToDatabaseColumn(ZonedDateTime entityValue) { return entityValue.toLocalDateTime(); } @Override public ZonedDateTime convertToEntityAttribute(LocalDateTime databaseValue) { return ZonedDateTime.of(databaseValue, ZoneId.systemDefault()); } } 

على وجه التحديد ، هذا المثال واضح للغاية لأن ZonedDateTime يحتوي على طرق سهلة التحويل. يحدث التحويل عن طريق استدعاء الأسلوب toLocalDateTime() . يمكن إجراء التحويل العكسي عن طريق استدعاء الأسلوب ZonedDateTimeOf() وتمرير قيمة ZoneId مع ZoneId لاستخدام المنطقة الزمنية.

محولات السمة المضمنة

كانت محولات السمة إضافة لطيفة جدًا إلى JPA 2.1 ، لأنها سمحت لأنواع السمات أن تكون أكثر مرونة. يضيف تحديث JPA 2.2 قدرة مفيدة على جعل محولات السمات قابلة للتنفيذ. هذا يعني أنه يمكنك تضمين السياقات وموارد حقن التبعية (CDI) مباشرة في محول السمة. يتوافق هذا التعديل مع تحسينات CDI الأخرى في مواصفات Java EE 8 ، مثل محولات JSF المتقدمة ، حيث يمكنهم الآن أيضًا استخدام حقن CDI.

للاستفادة من هذه الميزة الجديدة ، ما عليك سوى تضمين موارد CDI في محول السمات ، حسب الحاجة. توفر القائمة 2 مثالًا لمحول السمة ، والآن سأقوم بفصلها ، موضحة جميع التفاصيل المهمة.

يجب أن تقوم فئة المحول بتطبيق واجهة javax.persistence.AttributeConverter ، بتمرير قيم X و Y. تتوافق قيمة X مع نوع البيانات في كائن Java ، ويجب أن تتوافق قيمة Y مع نوع عمود قاعدة البيانات. بعد ذلك ، يجب إضافة تعليقات توضيحية إلى فئة المحول @Converter . وأخيرًا ، يجب أن تتجاوز الفئة convertToDatabaseColumn() و convertToEntityAttribute() . يجب أن يحول التطبيق في كل طريقة من هذه الأساليب القيم من أنواع معينة ثم يعود إليها.

لتطبيق المحول تلقائيًا في كل مرة يتم فيها استخدام نوع البيانات المحدد ، أضف "تلقائي" ، كما في @Converter(autoApply=true) . لتطبيق محول على سمة واحدة ، استخدم التعليق التوضيحيConverter على مستوى السمة ، كما هو موضح هنا:

 @Convert(converter=LocalDateConverter.java) private LocalDate workDate; 

يمكن أيضًا تطبيق المحول على مستوى الفصل:

 @Convert(attributeName="workDate", converter = LocalDateConverter.class) public class Job implements Serializable { . . . 

لنفترض أنني أريد تشفير القيم الواردة في حقل creditLimit لكيان Customer عند حفظه. لتنفيذ هذه العملية ، يجب تشفير القيم قبل حفظها وفك تشفيرها بعد استردادها من قاعدة البيانات. يمكن القيام بذلك عن طريق المحول ، وباستخدام JPA 2.2 ، يمكنني تضمين كائن التشفير في المحول لتحقيق النتيجة المرجوة. القائمة 3 تقدم مثالا.

قائمة 3

 @Converter public class CreditLimitConverter implements AttributeConverter<BigDecimal, BigDecimal> { @Inject CreditLimitEncryptor encryptor; @Override public BigDecimal convertToDatabaseColumn (BigDecimal entityValue) { String encryptedFormat = encryptor.base64encode(entityValue.toString()); return BigDecimal.valueOf(Long.valueOf(encryptedFormat)); } ... } 

في هذا الكود ، يتم تنفيذ العملية عن CreditLimitEncryptor فئة CreditLimitEncryptor في المحول ثم استخدامه للمساعدة في العملية.

تدفق نتائج الاستعلام

الآن يمكنك بسهولة الاستفادة الكاملة من ميزات تدفقات Java SE 8 عند العمل مع نتائج الاستعلام. لا تعمل سلاسل المحادثات على تبسيط القراءة والكتابة والحفاظ على التعليمات البرمجية فحسب ، بل تساعد أيضًا في تحسين أداء الاستعلام في بعض المواقف. تساعد بعض عمليات تنفيذ سلسلة المحادثات أيضًا على تجنب عدد كبير جدًا من الطلبات المتزامنة للبيانات بشكل مفرط ، على الرغم من أن استخدام ترقيم الصفحات ResultSet في بعض الحالات قد يعمل بشكل أفضل من التدفقات.

لتمكين هذه الوظيفة ، تمت إضافة طريقة getResultStream() إلى TypedQuery Query و TypedQuery . يسمح هذا التغيير الطفيف لـ JPA بإرجاع دفق من النتائج بدلاً من قائمة. وبالتالي ، إذا كنت تعمل مع ResultSet كبير ، فمن المنطقي مقارنة الأداء بين تنفيذ مؤشر ترابط جديد و ResultSets للتمرير أو ترقيم الصفحات. والسبب هو أن عمليات التنفيذ لمؤشر الترابط تسترد جميع السجلات مرة واحدة ، وتخزنها في قائمة ، ثم تعيدها. تسترجع ResultSet للتمرير وتقنية ترقيم الصفحات تجزئة البيانات ، والتي قد تكون أفضل لمجموعات البيانات الكبيرة.

قد يقرر مزودو المثابرة تجاوز طريقة getResultStream() الجديدة getResultStream() تحسين التنفيذ. يتضمن السبات بالفعل طريقة stream () التي تستخدم ResultSet للتمرير لتحليل نتائج السجلات بدلاً من إعادتها بالكامل. وهذا يسمح للإسبات بالعمل مع مجموعات البيانات الكبيرة جدًا والقيام بذلك بشكل جيد. من المتوقع أن يتجاوز مقدمو الخدمات الآخرون هذه الطريقة لتوفير ميزات مماثلة مفيدة لـ JPA.

بالإضافة إلى الأداء ، فإن القدرة على تدفق النتائج هي إضافة لطيفة إلى JPA ، والتي توفر طريقة ملائمة للعمل مع البيانات. سوف أعرض بضعة سيناريوهات حيث قد يكون هذا مفيدًا ، لكن الاحتمالات نفسها لا حصر لها. في كلا السيناريوهين ، أستعلم عن كيان Job وأعيد الدفق. أولاً ، انظر إلى التعليمات البرمجية التالية ، حيث أقوم ببساطة getResultStream() تدفق Jobs مقابل Customer معين عن طريق استدعاء أسلوب واجهة Query getResultStream() . ثم أستخدم هذا الخيط لإخراج التفاصيل المتعلقة customer work date Job'a.

 public void findByCustomer(PoolCustomer customer){ Stream<Job> jobList = em.createQuery("select object(o) from Job o " + "where o.customer = :customer") .setParameter("customer", customer) .getResultStream(); jobList.map(j -> j.getCustomerId() + " ordered job " + j.getId() + " - Starting " + j.getWorkDate()) .forEach(jm -> System.out.println(jm)); } 


يمكن تعديل هذه الطريقة بشكل طفيف بحيث تُرجع قائمة النتائج باستخدام طريقة Collectors .toList() كما يلي.

 public List<Job> findByCustomer(PoolCustomer customer){ Stream<Job> jobList = em.createQuery( "select object(o) from Job o " + "where o.customerId = :customer") .setParameter("customer", customer) .getResultStream(); return jobList.collect(Collectors.toList()); } 

في السيناريو التالي ، الموضح أدناه ، أجد List بالمهام المتعلقة بتجمعات لنموذج معين. في هذه الحالة ، أعيد جميع المهام المطابقة للنموذج المقدم كسلسلة. على غرار المثال الأول ، أقوم أولاً بإرجاع دفق من سجلات Jobs . بعد ذلك ، أقوم بتصفية السجلات بناءً على نموذج تجمع العملاء. كما ترى ، فإن الكود الناتج صغير للغاية وسهل القراءة.

 public List<Job> findByCustPoolShape(String poolShape){ Stream<Job> jobstream = em.createQuery( "select object(o) from Job o") .getResultStream(); return jobstream.filter( c -> poolShape.equals(c.getCustomerId().getPoolId().getShape())) .collect(Collectors.toList()); } 

كما ذكرت سابقًا ، من المهم تذكر الأداء في السيناريوهات حيث يتم إرجاع كميات كبيرة من البيانات. هناك حالات يكون فيها مؤشر الترابط أكثر فائدة في الاستعلام عن قواعد البيانات ، ولكن هناك أيضًا الحالات التي يمكن أن تؤدي إلى تدهور الأداء. القاعدة الأساسية الجيدة هي أنه إذا كان يمكن الاستعلام عن البيانات كجزء من استعلام SQL ، فمن المنطقي القيام بذلك. في بعض الأحيان ، لا تفوق مزايا استخدام بناء جملة مؤشر الترابط الأفضل الأداء الذي يمكن تحقيقه باستخدام تصفية SQL القياسية.

دعم التعليقات التوضيحية

عندما تم إصدار Java SE 8 ، أصبحت التعليقات التوضيحية ممكنة ، مما يسمح لك بإعادة استخدام التعليقات التوضيحية في الإعلان. تتطلب بعض المواقف استخدام نفس التعليق التوضيحي في فصل دراسي أو حقل عدة مرات. على سبيل المثال ، قد يكون هناك أكثر من تعليق توضيحي @SqlResultSetMapping لفئة كيان معين. في الحالات التي يتطلب فيها دعم إعادة التعليق التوضيحي ، يجب استخدام التعليق التوضيحي للحاوية. التعليقات التكرارية المكررة لا تقلل فقط من متطلبات التفاف مجموعات من التعليقات التوضيحية المتطابقة في التعليقات التوضيحية للحاوية ، ولكن يمكنها أيضًا تسهيل قراءة التعليمات البرمجية.

يعمل هذا على النحو التالي: يجب وضع علامة على تنفيذ فئة التعليق التوضيحي بـ @Repeatable meta-annotation للإشارة إلى أنه يمكن استخدامه أكثر من مرة. يأخذ التعليق التوضيحي @Repeatable نوع فئة التعليق التوضيحي للحاوية. على سبيل المثال ، NamedQuery الآن NamedQuery التعليق التوضيحي NamedQuery التوضيحي @Repeatable(NamedQueries.class) . في هذه الحالة ، لا يزال التعليق التوضيحي للحاوية قيد الاستخدام ، ولكن لا يتعين عليك التفكير فيه عند استخدام نفس التعليق التوضيحي في الإعلان أو الفصل الدراسي ، لأن @Repeatable هذه التفاصيل.

نعطي مثالا. إذا كنت تريد إضافة أكثر من تعليق توضيحي @NamedQuery إلى فئة كيان في JPA 2.1 ، فأنت بحاجة إلى تغليفها داخل التعليق التوضيحي @NamedQueries ، كما هو موضح في القائمة 4.

قائمة 4

 @Entity @Table(name = "CUSTOMER") @XmlRootElement @NamedQueries({ @NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c") , @NamedQuery(name = "Customer.findByCustomerId", query = "SELECT c FROM Customer c " + "WHERE c.customerId = :customerId") , @NamedQuery(name = "Customer.findByName", query = "SELECT c FROM Customer c " + "WHERE c.name = :name") . . .)}) public class Customer implements Serializable { . . . } 

ومع ذلك ، في JPA 2.2 ، كل شيء مختلف. نظرًا لأن @NamedQuery عبارة عن تعليق توضيحي مكرر ، فيمكن تحديده في فئة الكيان أكثر من مرة ، كما هو موضح في القائمة 5.

قائمة 5

 @Entity @Table(name = "CUSTOMER") @XmlRootElement @NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c") @NamedQuery(name = "Customer.findByCustomerId", query = "SELECT c FROM Customer c " + "WHERE c.customerId = :customerId") @NamedQuery(name = "Customer.findByName", query = "SELECT c FROM Customer c " + "WHERE c.name = :name") . . . public class Customer implements Serializable { . . . } 

قائمة التعليقات التوضيحية المكررة:

  • @AssociationOverride
  • @AttributeOverride
  • @Convert
  • @JoinColumn
  • @MapKeyJoinColumn
  • @NamedEntityGraphy
  • @NamedNativeQuery
  • @NamedQuery
  • @NamedStoredProcedureQuery
  • @PersistenceContext
  • @PersistenceUnit
  • @PrimaryKeyJoinColumn
  • @SecondaryTable
  • @SqlResultSetMapping

الخلاصة

يحتوي الإصدار JPA 2.2 على بعض التغييرات ، ولكن التحسينات المضمنة كبيرة. أخيرًا ، يتم تنسيق JPA مع Java SE 8 ، مما يسمح للمطورين باستخدام ميزات مثل Date and Time API ، وتدفق نتائج الاستعلام ، وتكرار التعليقات التوضيحية. يعمل هذا الإصدار أيضًا على تحسين تناسق CDI عن طريق إضافة القدرة على تضمين موارد CDI في محولات السمات. JPA 2.2 متاح الآن وهو جزء من Java EE 8 ، أعتقد أنك ستحب استخدامه.

النهاية

كما هو الحال دائمًا ، نحن في انتظار الأسئلة والتعليقات.

Source: https://habr.com/ru/post/ar423195/


All Articles