1. Bộ nhớ đệm là gì?
Bộ nhớ đệm cơ sở dữ liệu là dữ liệu nằm giữa ứng dụng và nguồn dữ liệu vật lý. Nó sao chép dữ liệu từ nguồn dữ liệu vật lý vào bộ nhớ đệm. Với bộ nhớ đệm, chúng ta có thể giảm tần suất truy cập ứng dụng vào nguồn dữ liệu vật lý, từ đó cải thiện hiệu suất. Phương tiện bộ nhớ đệm thường là bộ nhớ, nhưng cũng có thể là ổ cứng.
Hibernate có ba loại bộ nhớ đệm: bộ nhớ đệm cấp một, bộ nhớ đệm cấp hai và bộ nhớ đệm truy vấn.
2. Bộ nhớ đệm cấp một
Bộ nhớ đệm cấp một là bộ nhớ đệm Session, được quản lý tự động bởi Session mà không cần sự can thiệp của chương trình. Bộ nhớ đệm cấp một tải và lưu trữ đối tượng dựa trên ID của đối tượng. Xem ví dụ mã dưới đây:
@Override
public void kiemTraBoNhoDem() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
MonHoc monHoc = (MonHoc) session.get(MonHoc.class, 1);
System.out.println("Tên: " + monHoc.getTen());
monHoc = (MonHoc) session.get(MonHoc.class, 1);
System.out.println("Tên: " + monHoc.getTen());
tx.commit();
session.close();
}
Kết quả chạy:
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Tên: Nguyên lý máy tính
Tên: Nguyên lý máy tính
Lần truy vấn đầu tiên tạo ra câu lệnh SQL và lưu đối tượng truy vấn vào bộ nhớ đệm cấp một. Lần truy vấn thứ hai, đối tượng được tìm thấy trực tiếp trong bộ nhớ đệm cấp một, không cần tạo lại câu lệnh SQL.
Xem thêm một ví dụ:
@Override
public void kiemTraBoNhoDem() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
MonHoc monHoc = (MonHoc) session.get(MonHoc.class, 1);
System.out.println("Tên: " + monHoc.getTen());
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
monHoc = (MonHoc) session.get(MonHoc.class, 1);
System.out.println("Tên: " + monHoc.getTen());
tx.commit();
session.close();
}
Vì bộ nhớ đệm cấp một là bộ nhớ đệm ở cấp Session, nên khi Session đóng, bộ nhớ đệm cấp một cũng không còn tồn tại. Lần truy vấn thứ hai cũng cần tạo câu lệnh SQL:
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Tên: Nguyên lý máy tính
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Tên: Nguyên lý máy tính
3. Bộ nhớ đệm cấp hai
Bộ nhớ đệm cấp hai là bộ nhớ đệm SessionFactory, tương tự như bộ nhớ đệm cấp một, cũng tải và lưu trữ đối tượng dựa trên ID. Sự khác biệt là bộ nhớ đệm cấp một chỉ hiệu lực trong Session, trong khi bộ nhớ đệm cấp hai hiệu lực trong SessionFactory. Khi truy cập đối tượng theo ID, hệ thống sẽ tìm trong bộ nhớ đệm cấp một trước, nếu không tìm thấy thì tìm trong bộ nhớ đệm cấp hai. Bộ nhớ đệm cấp hai bao gồm EHCache, OSCache, SwarmCache và JBossCache, v.v. Ở đây lấy EHCache làm ví dụ.
Bộ nhớ đệm cấp hai cần được quản lý bởi chương trình. Đầu tiên, cấu hình Maven để tải các tệp Jar liên quan, thêm vào tệp pom:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.1.0.Final</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.8.3</version>
</dependency>
Tạo tệp cấu hình EHCache ehcache.xml:
<ehcache>
<diskStore path="E:\Eclipse\MyWorkspace\Cache"/>
<defaultCache
maxElementsInMemory="10000"
eternal="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name="com.hzhi.monhoc.entity.MonHoc"
maxElementsInMemory="10000"
eternal="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
</ehcache>
defaultCache là cài đặt mặc định, cache bên dưới chỉ định lớp nào cần bộ nhớ đệm cấp hai. Nó thiết lập số lượng đối tượng bộ nhớ đệm tối đa, có hiệu lực vĩnh viễn hay không, số giây rảnh tối đa, số giây tồn tại tối đa, khi bộ nhớ đầy có ghi vào ổ cứng không, đường dẫn ghi vào ổ cứng, v.v.
Chỉnh sửa tệp hbm của lớp cần bộ nhớ đệm:
<class name="com.hzhi.monhoc.entity.MonHoc" table="mon_hoc">
<cache usage="read-only"/>
......
</class>
usage thiết lập chiến lược truy cập đồng thời, thường được đặt thành read-only.
Chỉnh sửa cấu hình SessionFactory trong tệp applicationContext.xml, thêm các thuộc tính bộ nhớ đệm cấp hai:
<!-- SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" >
<ref local="dataSource"/>
</property>
<!-- Cấu hình thuộc tính Hibernate -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.connection.isolation">8</prop>
<!-- Bộ nhớ đệm cấp hai -->
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">WEB-INF/ehcache.xml</prop>
</props>
</property><br></br> ......<br></br> </bean>
Chạy ví dụ dưới đây:
@Override
public void kiemTraBoNhoDem() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
MonHoc monHoc = (MonHoc) session.get(MonHoc.class, 1);
System.out.println("Tên: " + monHoc.getTen());
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
monHoc = (MonHoc) session.get(MonHoc.class, 1);
System.out.println("Tên: " + monHoc.getTen());
tx.commit();
session.close();
}
Kết quả:
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Tên: Nguyên lý máy tính
Tên: Nguyên lý máy tính
Mặc dù đã đóng Session, nhưng bộ nhớ đệm cấp hai vẫn tồn tại, vì vậy chỉ tạo ra một câu lệnh SQL.
Xem ví dụ tiếp theo:
@Override
public void kiemTraBoNhoDem() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from MonHoc");
Iterator iter = query.iterate();
while(iter.hasNext()){
System.out.println(((MonHoc)iter.next()).getTen());
}
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
query = session.createQuery("from MonHoc");
iter = query.iterate();
while(iter.hasNext()){
System.out.println(((MonHoc)iter.next()).getTen());
}
tx.commit();
session.close();
}
Kết quả:
Hibernate:
select
monhoc0_.ID as col_0_0_
from
mon_hoc monhoc0_
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Nguyên lý máy tính
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Mạng máy tính
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Nguyên lý cơ sở dữ liệu
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Ngôn ngữ C
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Tiếng Anh A
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Java
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Linux
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Giải tích cao cấp
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Văn học
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Vật lý đại học
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Kỹ thuật phần mềm
Hibernate:
select
monhoc0_.ID as col_0_0_
from
mon_hoc monhoc0_
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
Khi sử dụng phương thức list() của Query, chỉ tạo ra một câu lệnh SQL để truy vấn tất cả các đối tượng. Khi sử dụng phương thức iterate(), sẽ lấy được tất cả các ID của đối tượng trước, sau đó tạo một câu lệnh SQL cho mỗi ID để truy vấn. Session thứ hai cũng sử dụng phương thức iterate(), trước tiên tạo một câu lệnh SQL để lấy ID, sau đó tìm đối tượng theo ID. Vì đã bật bộ nhớ đệm cấp hai, đối tượng được tìm thấy trong bộ nhớ đệm cấp hai, vì vậy chỉ xuất ra trực tiếp mà không cần tạo lại câu lệnh SQL cho mỗi ID.
Dù là bộ nhớ đệm cấp một hay cấp hai, chỉ có thể lưu trữ đối tượng, không thể lưu trữ giá trị thuộc tính. Xem ví dụ dưới đây:
@Override
public void kiemTraBoNhoDem() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("select m.ten from MonHoc m");
List<String> tenMonHoc = query.list();
for(Iterator iter = tenMonHoc.iterator(); iter.hasNext();){
String ten = (String) iter.next();
System.out.println(ten);
}
System.out.println("----------");
query = session.createQuery("select m.ten from MonHoc m");
tenMonHoc = query.list();
for(Iterator iter = tenMonHoc.iterator(); iter.hasNext();){
String ten = (String) iter.next();
System.out.println(ten);
}
System.out.println("----------");
tx.commit();
session.close();
}
Kết quả chạy:
Hibernate:
select
monhoc0_.TEN as col_0_0_
from
mon_hoc monhoc0_
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Hibernate:
select
monhoc0_.TEN as col_0_0_
from
mon_hoc monhoc0_
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Mặc dù đã bật bộ nhớ đệm cấp hai, nhưng kết quả truy vấn không phải là đối tượng mà là thuộc tính, vì vậy không được lưu trữ trong bộ nhớ đệm. Lần truy vấn thứ hai vẫn tạo ra câu lệnh truy vấn. Để giải quyết vấn đề này, cần sử dụng bộ nhớ đệm truy vấn.
4. Bộ nhớ đệm truy vấn
Dựa trên việc cấu hình bộ nhớ đệm cấp hai, có thể thiết lập bộ nhớ đệm truy vấn. Thêm một dòng vào cài đặt SessionFactory:
<prop key="hibernate.cache.use_query_cache">true</prop>
Tức là đã bật bộ nhớ đệm truy vấn. Bộ nhớ đệm truy vấn cũng là bộ nhớ đệm ở cấp SessionFactory, hiệu lực trong toàn bộ SessionFactory.
Tắt bộ nhớ đệm cấp hai, chạy ví dụ dưới đây, thêm setCacheable(true) sau Query để bật bộ nhớ đệm truy vấn:
@Override
public void kiemTraBoNhoDem() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("select m.ten from MonHoc m");
query.setCacheable(true);
List<String> tenMonHoc = query.list();
for(Iterator iter = tenMonHoc.iterator(); iter.hasNext();){
String ten = (String) iter.next();
System.out.println(ten);
}
System.out.println("----------");
query = session.createQuery("select m.ten from MonHoc m");
query.setCacheable(true);
tenMonHoc = query.list();
for(Iterator iter = tenMonHoc.iterator(); iter.hasNext();){
String ten = (String) iter.next();
System.out.println(ten);
}
System.out.println("----------");
tx.commit();
session.close();
}
Kết quả:
Hibernate:
select
monhoc0_.TEN as col_0_0_
from
mon_hoc monhoc0_
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Vì hai truy vấn HQL giống nhau, nên chỉ tạo ra một câu lệnh SQL. Nhưng nếu thay đổi truy vấn thứ hai:
System.out.println("----------");
query = session.createQuery("select m.ten from MonHoc m where m.id > 5");
query.setCacheable(true);
tenMonHoc = query.list();
for(Iterator iter = tenMonHoc.iterator(); iter.hasNext();){
String ten = (String) iter.next();
System.out.println(ten);
}
System.out.println("----------");
Kết quả:
Hibernate:
select
monhoc0_.TEN as col_0_0_
from
mon_hoc monhoc0_
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Hibernate:
select
monhoc0_.TEN as col_0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID>5
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Vì câu lệnh HQL đã thay đổi, nên lần thứ hai cũng tạo ra câu lệnh SQL.
Bộ nhớ đệm truy vấn có thể lưu trữ thuộc tính, cũng có thể lưu trữ đối tượng, nhưng khi lưu trữ đối tượng, chỉ lưu trữ ID của đối tượng, không lưu trữ toàn bộ đối tượng. Xem ví dụ dưới đây:
@Override
public void kiemTraBoNhoDem() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from MonHoc");
query.setCacheable(true);
List<MonHoc> danhSach = query.list();
for (int i=0; i<danhSach.size(); i++){
System.out.println(danhSach.get(i).getTen());
}
System.out.println("----------");
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
query = session.createQuery("from MonHoc");
query.setCacheable(true);
danhSach = query.list();
for (int i=0; i<danhSach.size(); i++){
System.out.println(danhSach.get(i).getTen());
}
System.out.println("----------");
tx.commit();
session.close();
}
Kết quả:
Hibernate:
select
monhoc0_.ID as ID0_,
monhoc0_.TEN as TEN0_,
monhoc0_.MO_TA as MO_TA0_
from
mon_hoc monhoc0_
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Hibernate:
select
monhoc0_.ID as ID0_0_,
monhoc0_.TEN as TEN0_0_,
monhoc0_.MO_TA as MO_TA0_0_
from
mon_hoc monhoc0_
where
monhoc0_.ID=?
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Vì đã bật bộ nhớ đệm truy vấn nhưng chưa bật bộ nhớ đệm cấp hai, mặc dù sử dụng phương thức list() để truy vấn tất cả các đối tượng cùng một lúc, nhưng bộ nhớ đệm truy vấn chỉ lưu trữ ID của đối tượng, không lưu trữ toàn bộ đối tượng. Vì vậy, trong Session thứ hai, HQL "from MonHoc" giống với trước, không tạo ra câu lệnh SQL, nhưng vì chưa bật bộ nhớ đệm cấp hai, không lưu trữ toàn bộ đối tượng, chỉ có thể tạo một câu lệnh SQL cho mỗi ID. Mặc dù hai lần đều sử dụng phương thức list(), nhưng lần đầu tiên tạo câu lệnh SQL để truy vấn tất cả các đối tượng cùng một lúc, còn lần thứ hai là tạo câu lệnh SQL cho từng ID dựa trên ID trong bộ nhớ đệm truy vấn.
Nếu cùng lúc bật cả bộ nhớ đệm truy vấn và bộ nhớ đệm cấp hai, trong Session thứ hai không cần tạo câu lệnh SQL cho từng ID nữa:
Hibernate:
select
monhoc0_.ID as ID0_,
monhoc0_.TEN as TEN0_,
monhoc0_.MO_TA as MO_TA0_
from
mon_hoc monhoc0_
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------
Nguyên lý máy tính
Mạng máy tính
Nguyên lý cơ sở dữ liệu
Ngôn ngữ C
Tiếng Anh A
Java
Linux
Giải tích cao cấp
Văn học
Vật lý đại học
Kỹ thuật phần mềm
----------