evict()、clear()和flush()方法是Hibernate缓存的3种基本操作方法,本文主要介绍这3种方法的使用方式和具体区别。
Company表:
Company实体类:
import java.util.Set; public class Company { private int companyId; private String companyName; private Set<Employee> companyEmployees; public int getCompanyId() { return companyId; } public void setCompanyId(int companyId) { this.companyId = companyId; } public String getCompanyName() { return companyName; } public void setCompanyName(String companyName) { this.companyName = companyName; } public Set<Employee> getCompanyEmployees() { return companyEmployees; } public void setCompanyEmployees(Set<Employee> companyEmployees) { this.companyEmployees = companyEmployees; } }
Company hbm配置:
<hibernate-mapping> <class name="com.jaeger.hibernatetest.day7.lazy.Company" table="company"> <id name="companyId" column="company_id"> <generator class="native"></generator> </id> <property name="companyName" column="company_name"/> <set name="companyEmployees"> <key column="employee_company_id"></key> <one-to-many class="com.jaeger.hibernatetest.day7.lazy.Employee"/> </set> </class> </hibernate-mapping>
1. evict()和clear()方法
evict()和clear()方法都是从session中清除缓存,evict()是清除单个对象的缓存,而clear()是清除所有缓存。测试方法如下:
Company company = (Company)session.get(Company.class, 1); //A System.out.println(company.getCompanyName()); //B company = (Company)session.get(Company.class, 1); //C System.out.println(company.getCompanyName()); //D
A:此处发出SQL去数据库查询。
B:此处打印出:KONAMI。
C:此处取缓存,不会发SQL。
D:此处打印出:KONAMI。
下面我们在第二次get之前先清除缓存:
Company company = (Company)session.get(Company.class, 1); //A System.out.println(company.getCompanyName()); session.evict(company); //B company = (Company)session.get(Company.class, 1); //C System.out.println(company.getCompanyName());
A、C:这两处都会发相同的SQL去数据库查询。因为B处清除了缓存,调用clear()方法效果也是一样。
2. flush()方法
flush()方法会根据缓存中对象的操作生成相应的SQL语句去操作数据库。没有调用flush()的测试方法如下:
Company company = (Company)session.get(Company.class, 1); company.setCompanyName("Santa Monica"); session.update(company); //A Company newCompany = new Company(); newCompany.setCompanyName("UBISOFT"); session.save(newCompany); //B transaction.commit(); //C session.close();
A:此处不会发出SQL去更新数据库。
B:此处会发出insert语句去更新数据库,但数据是未提交的状态,如果数据库的隔离级别是Read Committed的话就可以看到。
insert into company (company_name) values (?)
C:此处会发出update语句去更新company信息。
update company set company_name=? where company_id=?
这里之所以会发出SQL语句,因为transaction.commit()会调用flush()方法。
下面我们自己来调用flush()方法:
Company company = (Company)session.get(Company.class, 1); company.setCompanyName("Santa Monica"); session.update(company); session.flush(); //A Company newCompany = new Company(); newCompany.setCompanyName("UBISOFT"); session.save(newCompany); transaction.commit(); //B session.close();
A:此处立刻会发出update语句去更新company信息。数据是未提交的状态。
B:此处会发出insert语句去更新数据库。数据是未提交的状态。
注意:Hibernate使用的主键生成策略为uuid时情况会有所不同,我们把Company实体类的companyId属性的类型改为String,再把Company hbm配置修改为<generator class="uuid">。下面还是使用上面的测试方法:
Company company = (Company)session.get(Company.class, 1); company.setCompanyName("Santa Monica"); session.update(company); //A Company newCompany = new Company(); newCompany.setCompanyName("UBISOFT"); session.save(newCompany); //B transaction.commit(); //C session.close();
A、B:这时update()和save()都不会向数据库发SQL语句,第一次测试中save()方法会发SQL语句是因为我们使用的是native主键生成策略,要拿到主键就必须先向数据库插入数据。而uuid是Hibernate自己生成的,所以只会加入缓存,而不会发出SQL语句。
C:这个地方很关键,我们先看看Hibernate生成的SQL语句:
insert into company (company_name, company_id) values (?, ?) update company set company_name=? where company_id=?
从上面可以看出:Hibernate在用缓存中的对象去操作数据库时,并不是按照我们程序的顺序去执行,而是先执行insert,然后才执行update和delete。这就有可能让执行结果跟我们所期待的不一样。这个时候就应该使用flush()方法手动的让Hibernate及时去发送SQL语句。上面的例子中,我们如果在A步骤后面加入session.flush()方法,则update就会比insert先执行。