Hibernate的一級緩存其實就是Session內(nèi)置的一個Map,用來緩存它操作過的實體對象,對象的主關(guān)鍵字ID是Map的key,實體對象就是對應(yīng)的值。所以,一級緩存是以實體對象為單位進(jìn)行存儲的,訪問時也是以實體為單位的(直接訪問屬性是不能使用緩存的),并且要求使用主關(guān)鍵字ID來進(jìn)行訪問。
一級緩存是由Session提供的,所以它只存在于Session的生命周期中,當(dāng)程序調(diào)用save(),update(),saveorupdate()等方法以及調(diào)用查詢接口list,filter,iterate時,如果session緩存中還不存在相應(yīng)的對象,Hibernate會把該對象加入到一級緩存中,當(dāng)Session關(guān)閉的時候該Session所管理的一級緩存也會立即被清除。當(dāng)程序調(diào)用get(),load(),iterate(查詢實體對象才支持一級緩存,查詢普通屬性則不支持一級緩存)時,Hibernate會先到緩存中去拿,如果緩存中已經(jīng)存在目標(biāo)對象,則直接拿來而不再查詢數(shù)據(jù)庫,否則,必須發(fā)出查詢語句到數(shù)據(jù)庫中查。
對于一級緩存的使用,其實大多都是由Hibernate自動維護(hù)的,我們能做的是很少的,既不能卸載它,也不能對它進(jìn)行任何的配置。但是,好在Hibernate給我們提供了兩個方法可以對它進(jìn)行簡單的管理:session.clear(),session.evict() 。前者是清空一級緩存中所有的對象,后者是把某一個對象從一級緩存中清除。項目中,當(dāng)需要進(jìn)行大批量數(shù)據(jù)一次性更新時,在不知不覺中hibernate會占用大量內(nèi)存,這時就應(yīng)該階段性地調(diào)用clear()方法來清空一級緩存中的對象,控制一級緩存的大小,以避免產(chǎn)生內(nèi)存溢出的情況。
如果數(shù)據(jù)量特別大,我們一般考慮采用jdbc實現(xiàn),因為它不用把大批量的數(shù)據(jù)事先加載到內(nèi)存中,然后再進(jìn)行更新與修改。所以不會消耗大量內(nèi)存。如果jdbc也不能滿足要求可以考慮采用數(shù)據(jù)本身的特定導(dǎo)入工具等其它辦法。
1.實體類:Student.java
public class Student {
private Integer id;
private String name;
//一系列的setter.getter方法
}
2.映射文件:
Student.hbm.xml
<class name="com.sxt.hibernate.cache.entity.Student" table="sxt_hibernate_student">
<id name="id" length="4">
<generator class="native"></generator>
</id>
<property name="name" length="10"></property>
</class>
3.Hibernate配置文件
省略。
4. 測試方法:
public static void main(String[] args) {
Session session = null;
Transaction t = null;
try {
session = HibernateUtils.getSession();
t = session.beginTransaction();
/**
* 在同一個session中發(fā)出兩次load查詢
*/
/* //如果前面沒有用session操作過此student對象,在這里會發(fā)出sql語句.
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
//不會發(fā)出sql,因為load使用一級緩存
student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());*/
/**
* 在同一個session中發(fā)出兩次get查詢
*/
/* Student student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());
//不會發(fā)出sql,因為get也使用一級緩存
student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());*/
/**
* 在同一個session中發(fā)出兩次iterate查詢實體對象
*/
/* Student student = (Student)session.createQuery("from Student s where s.id=1").iterate().next();
System.out.println("student.name=" + student.getName());
//會發(fā)出查詢id的sql,不會發(fā)出查詢實體對象的sql,因為iterate在查詢實體對象時也使用緩存
student = (Student)session.createQuery("from Student s where s.id=1").iterate().next();
System.out.println("student.name=" + student.getName());*/
/**
* 在同一個session中發(fā)出兩次iterate查詢實體對象
*/
/* //發(fā)出sql直接插name,不再插id
//select student0_.name as col_0_0_ from sxt_hibernate_student student0_ where student0_.id=1
String name = (String)session.createQuery("select s.name from Student s where s.id=1").iterate().next();
System.out.println("student.name=" + name);
//iterate查詢普通屬性,一級緩存不會緩存,所以發(fā)出sql
//由此可見,一級緩存是緩存實體對象的
name = (String)session.createQuery("select s.name from Student s where s.id=1").iterate().next();
System.out.println("student.name=" + name);*/
/**
* 在同一個session中先save,在發(fā)出load查詢save過的數(shù)據(jù)
*/
/* Student stu = new Student();
stu.setName("吳奇隆");
Serializable id = session.save(stu);//主鍵生成方式采用native,是個序列化的id.
//不會發(fā)出sql,因為save是使用緩存的.save之后,會把對象放到一級緩存中.
//再load時,直接到一級緩存中去拿就可以了.
Student student = (Student)session.load(Student.class, id);
System.out.println("student.name=" + student.getName());*/
/**
* 向數(shù)據(jù)庫中批量加入5000條數(shù)據(jù)
*/
/* for (int i=0; i<5000; i++) {
Student student = new Student();
student.setName("stu_" + i);
session.save(student);
//每20條數(shù)據(jù)就強(qiáng)制session將數(shù)據(jù)持久化
//同時清除緩存,避免大量數(shù)據(jù)造成內(nèi)存溢出
if ( i % 30 == 0) {
session.flush();
session.clear();
}
}*/
/**
* 在數(shù)據(jù)庫中一次性更新大批量數(shù)據(jù)
*/
/* Iterator<Student> students=session.createQuery("from Student s where s.id>100").iterate();
while(students.hasNext()){
Student stu =(Student)students.next();
stu.setName("n_"+stu.getName());
//將本批插入的對象立即寫入數(shù)據(jù)庫并釋放內(nèi)存
session.flush();
session.clear();
}*/
/**
* Hibernate并不適合處理大批量的數(shù)據(jù),通常我們都跳過Hibernate API,而直接采用JDBC API來做.
*/
/* Connection conn =session.connection();
PreparedStatement pstmt = conn.prepareStatement("update sxt_hibernate_student set name='s'||substr(name,4,8) "+"where id >100");
pstmt.executeUpdate();*/
/**
* 其實批處理大量數(shù)據(jù)更新最好的解決方法就是用創(chuàng)建存儲過程,直接利用底層數(shù)據(jù)庫.這樣性能最好.
*/
/*
*create or replace procedure StudentUpdate(s_id in number) as
*begin
*update sxt_hibernate_student set name='n_'||name where id>s_id;
*end;
*/
Connection conn=session.connection();
String str="{call StudentUpdate(?)}";
CallableStatement cstmt= conn.prepareCall(str);
cstmt.setInt(1,100);
cstmt.executeUpdate();
t.commit();
} catch (Exception e) {
e.printStackTrace();
t.rollback();
} finally {
HibernateUtils.closeSession(session);
}
/**
* 開啟兩個session中發(fā)出load查詢
*/
/* try{
session=HibernateUtils.getSession();
t=session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
}catch(Exception e){
e.printStackTrace();
t.rollback();
}finally{
HibernateUtils.closeSession(session);
}
try{
session=HibernateUtils.getSession();
t=session.beginTransaction();
//會發(fā)出查詢語句,因為一級緩存時和session綁定的,每個session都有自己的一級緩存,不同session間不能共享一級緩存.
//上一個session已經(jīng)關(guān)閉了,所以此處還要發(fā)出查詢語句.
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
}catch(Exception e){
e.printStackTrace();
t.rollback();
}finally{
HibernateUtils.closeSession(session);
}*/
}