hibernate一級快取詳解
一級快取的生命週期和session的生命週期一致,當前sessioin一旦關閉,一級快取就消失,session間不能共享一級快取的資料,因此一級快取也叫session級的快取或事務級快取。一級快取只存實體物件的 ,它不會快取一般的物件屬性(查詢快取可以),即當獲得物件後,就將該物件的快取起來,如果在同一session中如果再去獲取這個物件時,它會先判斷快取中有沒有該物件的id,如果有就直接從快取中取出,反之則去資料庫中取,取的同時將該物件的快取起來,有以下方法可以支援一級快取:
•get()
•save()
•load()
•iterate(查詢實體物件)
Query和Criteria的list()和uniqueResult()只會寫快取,但不會從快取中讀取(除非結合查詢快取)。一級快取無法取消,但可以管理,可以使用session.clear()、session.evict()清除或驅逐。
以下對hibernate的一級快取進行程式碼測試:hibernate.cfg.xml配置:
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url"> jdbc:mysql://localhost/hibernate_testcache </property>
<property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property>
<property name="hibernate.show_sql">true</property>
<mapping resource="com/sunyzc/hibernate/testcache/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
實體Bean:User.java
package
com.sunyzc.hibernate.testcache;
public class User {
private int id;
private String name;
//getters、setters
}
User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd
<hibernate-mapping>
<class name="com.sunyzc.hibernate.testcache.User" table="t_user">
<id name="id">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
</class>
</hibernate-mapping>
建立表的工具:ExportDB.java
package com.sunyzc.hibernate.testcache;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class ExportDB {
public static void main(String[] args) {
//讀取hibernate.cfg.xml檔案
Configuration cfg = new Configuration().configure();
SchemaExportexport = new SchemaExport(cfg);
export.create(true,true);
}
}
HibernateUtils.java
package com.sunyzc.hibernate.testcache;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtils {
private static SessionFactory factory;
static{
try{
factory = new Configuration().configure().buildSessionFactory();
}catch(Exception e) {
e.printStackTrace();
}
}
public static Session getSession() {
return factory.openSession();
}
public static void closeSession(Session session) {
if(session != null&& session.isOpen()) {
session.close();
}
}
}
測試1:在同一個session中發出兩次load查詢
public void testCache1() {
Session session = null;
try{
session= HibernateUtils.getSession();
session.beginTransaction();
User user = (User) session.load(User.class,1);
System.out.println("user.name="+ user.getName());
//不會發出sql,因為load使用快取
user= (User) session.load(User.class,1);
System.out.println("user.name="+ user.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}
finally
{
HibernateUtils.closeSession(session);
}
}
輸出結果:
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from t_user user0_ where user0_.id=?
user.name=user0
user.name=user0
測試2:在同一個session中發出兩次get查詢
public void testCache2() {
Session session = null;
try{
session= HibernateUtils.getSession();
session.beginTransaction();
User user = (User) session.get(User.class,1);
System.out.println("user.name="+ user.getName());
//不會發出sql,因為get使用快取
user = (User) session.get(User.class,1);
System.out.println("user.name="+ user.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
輸出結果:
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from t_user user0_ where user0_.id=?
user.name=user0
user.name=user0
測試3:在同一個session中發出兩次iterate查詢實體物件
public void testCache3() {
Session session = null;
try{
session= HibernateUtils.getSession();
session.beginTransaction();
String hql = "from User u where u.id=1";
User user = (User) session.createQuery(hql).iterate().next();
System.out.println("user.name="+ user.getName());
//會發出查詢id的sql,不會發出查詢實體物件的sql,因為iterate使用快取
user= (User) session.createQuery(hql).iterate().next();
System.out.println("user.name="+ user.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
輸出結果:
Hibernate: select user0_.id as col_0_0_ from t_user user0_ where user0_.id=1
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from t_user user0_ where user0_.id=?
user.name=user0
Hibernate: select user0_.id as col_0_0_ from t_user user0_ where user0_.id=1
user.name=user0
測試4:在通一個session中發出兩次uniqueResult查詢實體物件不會快取,如果第一次用uniqueResult 第二次用iterate查詢是會快取的
public void testCache4() {
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
String hql = "from User u where u.id=1";
User user = (User) session.createQuery(hql).uniqueResult();
System.out.println("user.name="+ user.getName());
user= (User) session.createQuery(hql).uniqueResult();
System.out.println("user.name="+ user.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
18
}
輸出結果:
Hibernate: select user0_.id as id0_, user0_.name as name0_ from t_user user0_ where user0_.id=1
user.name=user0
Hibernate: select user0_.id as id0_, user0_.name as name0_ from t_user user0_ where user0_.id=1
user.name=user0
測試5:在同一個session中發出兩次list查詢實體物件不會快取
public void testCache4() {
Session session = null;
try{
session= HibernateUtils.getSession();
session.beginTransaction();
List list = session.createQuery("from User u").list();
System.out.println("user.name="+ list.get(0).getName());
list= session.createQuery("from User u").list();
System.out.println("user.name="+ list.get(0).getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
輸出結果:
Hibernate: select user0_.id as id0_, user0_.name as name0_ from t_user user0_
user.name=user0
Hibernate: select user0_.id as id0_, user0_.name as name0_ from t_user user0_
user.name=user0
測試6:在同一個session中發出兩次iterate查詢普通屬性不會快取
public void testCache4() {
Session session = null;
try{
session= HibernateUtils.getSession();
session.beginTransaction();
String hql = "select u.name from User u where u.id=1";
String name = (String) session.createQuery(hql).iterate().next();
System.out.println("user.name="+ name);
//iterate查詢普通屬性,一級快取不會快取,所以發出sql
//一級快取是快取實體物件的
name = (String) session.createQuery(hql).iterate().next();
System.out.println("user.name="+ name);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
輸出結果:
Hibernate: select user0_.name as col_0_0_ from t_user user0_ where user0_.id=1
user.name=user0
Hibernate: select user0_.name as col_0_0_ from t_user user0_ where user0_.id=1
user.name=user0
測試7:開啟兩個session中發出load查詢
public void testCache7() {
Session session1 = null;
Session session2 = null;
try{
session1 = HibernateUtils.getSession();
session2 = HibernateUtils.getSession();
session1.beginTransaction();
session2.beginTransaction();
User user1 = (User) session1.load(User.class,1);
System.out.println("user1.name="+ user1.getName());
//會發出查詢語句,session間不能共享一級快取的資料
//因為它會伴隨session的生命週期存在和消亡
User user2 = (User) session2.load(User.class,1);
System.out.println("user2.name="+ user2.getName());
session1.getTransaction().commit();
session2.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session1.getTransaction().rollback();
session2.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session1);
HibernateUtils.closeSession(session2);
}
}
輸出結果:
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from t_user user0_ where user0_.id=?
user1.name=user0
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from t_user user0_ where user0_.id=?
user2.name=user0
測試8:在同一個session中先save,再發出load查詢save過的資料
public void testCache8() {
Session session = null;
try{
session= HibernateUtils.getSession();
session.beginTransaction();
User u = new User();
u.setName("張三");
Serializable id = session.save(u);
//不會發出sql,因為save是使用快取的
User user = (User) session.load(User.class,id);
System.out.println("user.name="+ user.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
輸出結果:
Hibernate: insert into t_user (name) values (?)
user.name=張三
如何避免一次性大量的實體資料入庫導致記憶體溢位?
先flush(),再clear()。 save方法會將save的物件放入一級快取中,如果要save大批物件,則應該要及時清空一級快取,可以採用Session的clear()方法。若不清理快取,可發現javaw程序佔用記憶體持續增長,到一定程度會記憶體溢位的。如果資料量特別大,考慮採用jdbc實現,如果jdbc也不能滿足要求可以考慮採用資料本身的特定匯入工具。
測試9:向資料庫中批量加入1000條資料
public void testCache9() {
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
for(int i = 0;i < 1000; i++) {
User user = new User();
user.setName("user"+ i);
session.save(user);
//每20條資料就強制session將資料持久化
//同時清除快取,避免大量資料造成記憶體溢位
if(i % 20== 0){
session.flush();
session.clear();
}
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}