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); //ACompany newCompany = new Company();
newCompany.setCompanyName("UBISOFT");
session.save(newCompany); //B
transaction.commit(); //C
session.close();
A:此处不会发出SQL去更新数据库。
B:此处会发出insert语句去更新数据库,但数据是未提交的状态,如果数据库的隔离级别是Read Committed的话就可以看到。
insert intocompany(company_name)
values(?)
C:此处会发出update语句去更新company信息。
updatecompany setcompany_name=? wherecompany_id=?
这里之所以会发出SQL语句,因为transaction.commit()会调用flush()方法。
下面我们自己来调用flush()方法:
Company company = (Company)session.get(Company.class, 1);
company.setCompanyName("Santa Monica");
session.update(company);
session.flush(); //ACompany 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); //ACompany 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 intocompany(company_name, company_id)
values(?, ?)updatecompany setcompany_name=? wherecompany_id=?
从上面可以看出:Hibernate在用缓存中的对象去操作数据库时,并不是按照我们程序的顺序去执行,而是先执行insert,然后才执行update和delete。这就有可能让执行结果跟我们所期待的不一样。这个时候就应该使用flush()方法手动的让Hibernate及时去发送SQL语句。上面的例子中,我们如果在A步骤后面加入session.flush()方法,则update就会比insert先执行。
转载于:https://blog.51cto.com/jaeger/1757270