مرحبًا بك في درس مثال Hibernate Second Level Cache. اليوم سنتناول Hibernate EHCache وهو أحد أشهر موفري Hibernate Second Level Cache.
Hibernate Second Level Cache
إحدى الفوائد الرئيسية لاستخدام Hibernate في تطبيقات كبيرة هي دعمه للتخزين المؤقت، مما يقلل من استعلامات قاعدة البيانات ويحسن الأداء. في المثال السابق، قمنا بالتطرق إلى Hibernate First Level Cache واليوم سنتناول Hibernate Second Level Cache باستخدام Hibernate EHCache. موفري ذاكرة التخزين المؤقت من Hibernate الخاصة بالمستوى الثاني تشمل EHCache و Infinispan، ولكن EHCache هو الأكثر شيوعًا وسنستخدمه في مشروعنا التوضيحي. ومع ذلك، قبل أن ننتقل إلى مشروعنا، يجب أن نعرف استراتيجيات مختلفة لتخزين مؤشر.
- Read Only: يجب استخدام استراتيجية التخزين المؤقت هذه لكائنات الدومين الدائمة التي سيتم قراءتها دائمًا ولكن لن يتم تحديثها أبدًا. إنها مفيدة لقراءة وتخزين تكوين التطبيق وبيانات أخرى ثابتة لا يتم تحديثها أبدًا. إن هذه هي أبسط استراتيجية مع أفضل أداء لأنه لا يوجد هناك أي فوق تحميل للتحقق مما إذا كان الكائن قد تم تحديثه في قاعدة البيانات أم لا.
- قراءة الكتابة: مناسب للكائنات الثابتة التي يمكن تحديثها بواسطة تطبيق Hibernate. ومع ذلك، إذا تم تحديث البيانات إما من خلال النظام الخلفي أو تطبيقات أخرى، فلن يكون هناك طريقة لـ Hibernate للعلم بذلك وقد تكون البيانات قديمة. لذا أثناء استخدام هذه الاستراتيجية، تأكد من استخدام واجهة برمجة التطبيقات Hibernate لتحديث البيانات.
- قراءة الكتابة غير المقيدة: إذا كان التطبيق بحاجة إلى تحديث البيانات بشكل متقطع فقط ولا يتطلب عزل صارم للمعاملات، فقد يكون استخدام ذاكرة التخزين المؤقت للقراءة غير المقيدة مناسبًا.
- معاملي: توفر استراتيجية ذاكرة التخزين المؤقت المعاملي دعمًا لموفري ذاكرة التخزين المؤقت الكاملة للمعاملات مثل JBoss TreeCache. يمكن استخدام مثل هذه الذاكرة المؤقتة فقط في بيئة JTA ويجب عليك تحديد hibernate.transaction.manager_lookup_class.
تخزين مؤقت EHCache
نظرًا لأن EHCache يدعم جميع استراتيجيات التخزين المؤقت المذكورة أعلاه، فإنها الخيار الأفضل عندما تبحث عن تخزين مؤقت من المستوى الثاني في Hibernate. لن أدخل في تفاصيل كثيرة حول EHCache، سيكون تركيزي الرئيسي هو جعله يعمل لتطبيق Hibernate. قم بإنشاء مشروع Maven في Eclipse أو IDE المفضل لديك، وسيبدو التنفيذ النهائي كما في الصورة أدناه. دعنا نلقي نظرة على كل مكون من مكونات التطبيق واحد تلو الآخر.
الاعتماديات في مافن لـ Hibernate EHCache
لكي نستخدم الخزن المؤقت من المستوى الثاني في Hibernate، نحتاج إلى إضافة الاعتماديات ehcache-core و hibernate-ehcache في تطبيقنا. يستخدم EHCache slf4j لتسجيل الأحداث، لذا قمت أيضًا بإضافة slf4j-simple لأغراض التسجيل. أنا استخدم أحدث إصدارات جميع هذه الواجهات البرمجية، هناك فرصة طفيفة لعدم توافق واجهات برمجة التطبيقات لـ hibernate-ehcache مع واجهة برمجة التطبيقات لـ ehcache-core، في هذه الحالة ستحتاج إلى التحقق من ملف pom.xml الخاص بـ hibernate-ehcache لمعرفة الإصدار الصحيح لاستخدامه. يبدو ملف pom.xml النهائي لدينا كما يلي.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.hibernate</groupId>
<artifactId>HibernateEHCacheExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>Hibernate Secondary Level Cache Example using EHCache implementation</description>
<dependencies>
<!-- Hibernate Core API -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.5.Final</version>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
</dependency>
<!-- EHCache Core APIs -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.9</version>
</dependency>
<!-- Hibernate EHCache API -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.3.5.Final</version>
</dependency>
<!-- EHCache uses slf4j for logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
</dependencies>
</project>
تكوين Hibernate EHCache – الخزن المؤقت من المستوى الثاني لـ Hibernate
الخزن المؤقت من المستوى الثاني في Hibernate معطل افتراضيًا، لذا سنحتاج إلى تمكينه وإضافة بعض التكوينات لجعله يعمل. يبدو ملف hibernate.cfg.xml الخاص بنا كما يلي.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration SYSTEM "classpath://org/hibernate/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">pankaj123</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
<property name="hibernate.connection.username">pankaj</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<!-- For singleton factory -->
<!-- <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>
-->
<!-- enable second level cache and query cache -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="net.sf.ehcache.configurationResourceName">/myehcache.xml</property>
<mapping class="com.journaldev.hibernate.model.Employee" />
<mapping class="com.journaldev.hibernate.model.Address" />
</session-factory>
</hibernate-configuration>
بعض النقاط المهمة حول تكوينات الخزن المؤقت من المستوى الثاني في Hibernate هي:
- hibernate.cache.region.factory_class يُستخدم لتحديد فئة المصنع للتخزين المؤقت من المستوى الثاني، أنا أستخدم
org.hibernate.cache.ehcache.EhCacheRegionFactory
لهذا الغرض. إذا كنت ترغب في أن تكون فئة المصنع نمطية، يجب عليك استخدام فئةorg.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
. إذا كنت تستخدم Hibernate 3، ستكون الفئات المقابلةnet.sf.ehcache.hibernate.EhCacheRegionFactory
وnet.sf.ehcache.hibernate.SingletonEhCacheRegionFactory
. - hibernate.cache.use_second_level_cache يُستخدم لتمكين التخزين المؤقت من المستوى الثاني.
- hibernate.cache.use_query_cache يُستخدم لتمكين تخزين الاستعلامات، دونه، نتائج استعلامات HQL لن تتم تخزينها.
- net.sf.ehcache.configurationResourceName يُستخدم لتحديد موقع ملف تكوين EHCache، إنه معلم اختياري وإذا لم يكن موجودًا، سيحاول EHCache تحديد ملف ehcache.xml في مسار الفئة التطبيقية.
ملف تكوين Hibernate EHCache
يبدو ملف تكوين EHCache الخاص بنا myehcache.xml كما يلي.
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="java.io.tmpdir/ehcache" />
<defaultCache maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" statistics="true">
<persistence strategy="localTempSwap" />
</defaultCache>
<cache name="employee" maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="5" timeToLiveSeconds="10">
<persistence strategy="localTempSwap" />
</cache>
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
<persistence strategy="localTempSwap" />
</cache>
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
maxEntriesLocalHeap="5000" eternal="true">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
يوفر Hibernate EHCache العديد من الخيارات، لن أدخل في التفاصيل كثيرًا ولكن بعض التكوينات المهمة أعلاه هي:
- diskStore: يقوم EHCache بتخزين البيانات في الذاكرة ولكن عندما يبدأ في التجاوز، يبدأ في كتابة البيانات إلى نظام الملفات. نستخدم هذه الخاصية لتحديد الموقع الذي سيكتب فيه EHCache البيانات التي تم تجاوزها.
- defaultCache: إنها تكوين إلزامي، يُستخدم عندما يحتاج كائن إلى التخزين المؤقت ولا توجد مناطق تخزين مؤقت محددة لذلك.
- cache name=“employee”: نستخدم عنصر الذاكرة المؤقتة لتحديد المنطقة وتكويناتها. يمكننا تحديد مناطق متعددة وخصائصها، بينما نقوم بتحديد خصائص ذاكرة النموذج، يمكننا أيضًا تحديد المنطقة مع استراتيجيات التخزين المؤقت. تعتبر خصائص الذاكرة المؤقتة سهلة الفهم وواضحة بالاسم.
- تم تعريف مناطق الذاكرة المؤقتة
org.hibernate.cache.internal.StandardQueryCache
وorg.hibernate.cache.spi.UpdateTimestampsCache
لأن EHCache كان يعطي تحذيرًا بشأن ذلك.
Hibernate Second Level Cache – Model Bean Caching Strategy
نستخدم تعليقة org.hibernate.annotations.Cache
لتوفير تكوين التخزين المؤقت. يتم استخدام org.hibernate.annotations.CacheConcurrencyStrategy
لتعريف استراتيجية التخزين المؤقت ويمكننا أيضًا تحديد منطقة التخزين المؤقت التي سيتم استخدامها للنماذج.
package com.journaldev.hibernate.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
@Entity
@Table(name = "ADDRESS")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Address {
@Id
@Column(name = "emp_id", unique = true, nullable = false)
@GeneratedValue(generator = "gen")
@GenericGenerator(name = "gen", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "employee") })
private long id;
@Column(name = "address_line1")
private String addressLine1;
@Column(name = "zipcode")
private String zipcode;
@Column(name = "city")
private String city;
@OneToOne
@PrimaryKeyJoinColumn
private Employee employee;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
}
package com.journaldev.hibernate.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;
@Entity
@Table(name = "EMPLOYEE")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private long id;
@Column(name = "emp_name")
private String name;
@Column(name = "emp_salary")
private double salary;
@OneToOne(mappedBy = "employee")
@Cascade(value = org.hibernate.annotations.CascadeType.ALL)
private Address address;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
ملاحظة أنني استخدم نفس إعداد قاعدة البيانات كما في مثال HQL، قد ترغب في التحقق من ذلك لإنشاء جداول قاعدة البيانات وتحميل بيانات عينة.
فئة الأداة لمصنع الجلسة في Hibernate
لدينا فئة أداة بسيطة لتكوين هايبرنيت والحصول على مثيل الفئة الوحيدة SessionFactory
.
package com.journaldev.hibernate.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static SessionFactory buildSessionFactory() {
try {
// إنشاء مصنع الجلسة من hibernate.cfg.xml
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");
System.out.println("Hibernate Configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null) sessionFactory = buildSessionFactory();
return sessionFactory;
}
}
مشروع ذاكرة التخزين المؤقت من مستوى الثاني لـ Hibernate باستخدام Hibernate EHCache جاهز، دعنا نكتب برنامج بسيط لاختباره.
برنامج اختبار Hibernate EHCache
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.stat.Statistics;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateEHCacheMain {
public static void main(String[] args) {
System.out.println("Temp Dir:"+System.getProperty("java.io.tmpdir"));
// تهيئة الجلسات
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Statistics stats = sessionFactory.getStatistics();
System.out.println("Stats enabled="+stats.isStatisticsEnabled());
stats.setStatisticsEnabled(true);
System.out.println("Stats enabled="+stats.isStatisticsEnabled());
Session session = sessionFactory.openSession();
Session otherSession = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Transaction otherTransaction = otherSession.beginTransaction();
printStats(stats, 0);
Employee emp = (Employee) session.load(Employee.class, 1L);
printData(emp, stats, 1);
emp = (Employee) session.load(Employee.class, 1L);
printData(emp, stats, 2);
// مسح ذاكرة التخزين المؤقت من المستوى الأول أولاً، بحيث يتم استخدام ذاكرة التخزين المؤقت من المستوى الثاني
session.evict(emp);
emp = (Employee) session.load(Employee.class, 1L);
printData(emp, stats, 3);
emp = (Employee) session.load(Employee.class, 3L);
printData(emp, stats, 4);
emp = (Employee) otherSession.load(Employee.class, 1L);
printData(emp, stats, 5);
// إطلاق الموارد
transaction.commit();
otherTransaction.commit();
sessionFactory.close();
}
private static void printStats(Statistics stats, int i) {
System.out.println("***** " + i + " *****");
System.out.println("Fetch Count="
+ stats.getEntityFetchCount());
System.out.println("Second Level Hit Count="
+ stats.getSecondLevelCacheHitCount());
System.out
.println("Second Level Miss Count="
+ stats
.getSecondLevelCacheMissCount());
System.out.println("Second Level Put Count="
+ stats.getSecondLevelCachePutCount());
}
private static void printData(Employee emp, Statistics stats, int count) {
System.out.println(count+":: Name="+emp.getName()+", Zipcode="+emp.getAddress().getZipcode());
printStats(stats, count);
}
}
org.hibernate.stat.Statistics
توفر إحصائيات لمصنع Hibernate SessionFactory، نحن نستخدمها لطباعة عدد الاسترجاع وضربات ذاكرة التخزين المؤقت من المستوى الثاني، والفقدان وعدد الإضافة. الإحصائيات معطلة افتراضيًا لتحسين الأداء، ولهذا السبب أقوم بتمكينها في بداية البرنامج. عند تشغيل البرنامج أعلاه، نحصل على الكثير من الإخراج الذي تم إنشاؤه بواسطة APIs Hibernate و EHCache، ولكننا مهتمون بالبيانات التي نقوم بطباعتها. تقوم عملية التشغيل العيني بطباعة الإخراج التالي.
Temp Dir:/var/folders/h4/q73jjy0902g51wkw0w69c0600000gn/T/
Hibernate Configuration loaded
Hibernate serviceRegistry created
Stats enabled=false
Stats enabled=true
***** 0 *****
Fetch Count=0
Second Level Hit Count=0
Second Level Miss Count=0
Second Level Put Count=0
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
1:: Name=Pankaj, Zipcode=95129
***** 1 *****
Fetch Count=1
Second Level Hit Count=0
Second Level Miss Count=1
Second Level Put Count=2
2:: Name=Pankaj, Zipcode=95129
***** 2 *****
Fetch Count=1
Second Level Hit Count=0
Second Level Miss Count=1
Second Level Put Count=2
3:: Name=Pankaj, Zipcode=95129
***** 3 *****
Fetch Count=1
Second Level Hit Count=2
Second Level Miss Count=1
Second Level Put Count=2
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
4:: Name=Lisa, Zipcode=560100
***** 4 *****
Fetch Count=2
Second Level Hit Count=2
Second Level Miss Count=2
Second Level Put Count=4
5:: Name=Pankaj, Zipcode=95129
***** 5 *****
Fetch Count=2
Second Level Hit Count=4
Second Level Miss Count=2
Second Level Put Count=4
كما يمكنك رؤية من الإخراج، تم تعطيل الإحصائيات في البداية ولكننا قمنا بتمكينها لفحص ذاكرة التخزين المؤقت من المستوى الثاني لدينا. شرح الإخراج خطوة بخطوة هو كما يلي:
- قبل تحميل أي بيانات في تطبيقنا، كل الإحصائيات تكون 0 كما هو متوقع.
- عندما نقوم بتحميل الموظف ذو الهوية = 1 للمرة الأولى، يتم البحث أولاً في ذاكرة التخزين المؤقت من المستوى الأول ثم في ذاكرة التخزين المؤقت من المستوى الثاني. إذا لم يتم العثور عليه في الذاكرة المؤقتة، يتم تنفيذ استعلام قاعدة البيانات وبالتالي يصبح عدد الاستحصال 1. بمجرد تحميل الكائن، يتم حفظه في ذاكرة التخزين المؤقت من المستوى الأول وذاكرة التخزين المؤقت من المستوى الثاني على حد سواء. لذا يظل عدد الضربات الثانوية 0 ويصبح عدد الفقدان 1. لاحظ أن عدد الإضافة هو 2، وذلك لأن كائن الموظف يتكون أيضًا من عنوان، لذا يتم حفظ كل من الكائنين في ذاكرة التخزين المؤقت من المستوى الثاني ويتم زيادة العدد إلى 2.
- بعد ذلك، نقوم مرة أخرى بتحميل الموظف ذو الهوية = 1، وهذه المرة يكون موجودًا في ذاكرة التخزين المؤقت من المستوى الأول. لذا لا ترى أي استعلام قاعدة بيانات وتظل جميع إحصائيات ذاكرة التخزين المؤقت من المستوى الثاني الأخرى كما هي.
- بعد ذلك نستخدم طريقة
evict()
لإزالة كائن الموظف من ذاكرة التخزين المؤقت من المستوى الأول، الآن عند محاولة تحميله، يجده هايبرنيت في ذاكرة التخزين المؤقت من المستوى الثاني. ولهذا السبب لا يتم تنفيذ أي استعلام قاعدة بيانات ويبقى عدد جلب البيانات هو 1. لاحظ أن عدد الوصولات يتغير من 0 إلى 2 لأن كل من كائن الموظف وكائن العنوان يتم قراءتهما من ذاكرة التخزين المؤقت من المستوى الثاني. عدد الوصولات الفاشلة وعدد الإضافات يبقى عند القيمة السابقة. - بعد ذلك نقوم بتحميل موظف برقم تعريفي يساوي 3، يتم تنفيذ استعلام قاعدة البيانات ويزداد عدد جلب البيانات إلى 2، ويزداد عدد الوصولات الفاشلة من 1 إلى 2، ويزداد عدد الإضافات من 2 إلى 4.
- بعد ذلك نحاول تحميل موظف برقم تعريفي يساوي 1 في جلسة أخرى، حيث أن ذاكرة التخزين المؤقت من المستوى الثاني في هايبرنيت مشتركة عبر الجلسات، يتم العثور عليها في ذاكرة التخزين المؤقت من المستوى الثاني ولا يتم تنفيذ أي استعلام قاعدة بيانات. يبقى عدد جلب البيانات وعدد الوصولات الفاشلة وعدد الإضافات هو نفسه في حين يزداد عدد الوصولات من 2 إلى 4.
لذلك من الواضح أن ذاكرة التخزين المؤقت من المستوى الثاني في هايبرنيت؛ هايبرنيت EHCache؛ تعمل بشكل جيد. إحصائيات هايبرنيت مفيدة في العثور على نقاط الضعف في النظام وتحسينها لتقليل عدد جلب البيانات وتحميل المزيد من البيانات من ذاكرة التخزين المؤقت. هذا كل شيء بالنسبة لمثال هايبرنيت EHCache، أتمنى أن يساعدك في تكوين EHCache في تطبيقاتك هايبرنيت والحصول على أداء أفضل من خلال ذاكرة التخزين المؤقت من المستوى الثاني في هايبرنيت. يمكنك تنزيل المشروع العيني من الرابط أدناه واستخدام بيانات الإحصائيات الأخرى للتعلم أكثر.
Source:
https://www.digitalocean.com/community/tutorials/hibernate-ehcache-hibernate-second-level-cache