1. 程式人生 > >【JavaEE學習筆記】Hibernate_03_快取機制,自定義通用HibernateDAO工具類

【JavaEE學習筆記】Hibernate_03_快取機制,自定義通用HibernateDAO工具類

Hibernate_03

A.Hibernate快取

1.一級快取

一級快取是Session快取,屬於事務範圍的快取,由hibernate管理的

只要應用程式通過Session介面來執行CRUD操作

Hibernate就會啟用一級快取,把資料庫中的資料以物件的形式拷貝到快取中

對於批量更新和批量刪除操作,如果不希望啟用第一級快取

可以繞過Hibernate API,直接使用JDBC API操作

一級快取中物件的生命週期為:當session關閉後,就自動銷燬

2.二級快取

二級快取是SessionFactory級別的快取,屬於程序或群集範圍的快取

二級快取是可選外掛,預設不啟用

就是查詢的時候會把結果快取到二級快取中

如果同一個sessionFactory建立的其他session執行了相同的操作

hibernate就會從二級快取中拿結果,而不會再連線資料庫

主要使用第三方快取外掛,如使用Ehcache二級快取實現

使用步驟:

a.匯入Hibernate和mysql資料庫驅動以及druid的jar包


b.匯入二級快取的外掛包:optional下ehcache中三個jar


c.在主配置檔案中hibernate.cfg.xml 新增配置資訊

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
                                         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">  
<hibernate-configuration>  
    <session-factory>  
        <!-- 資料庫資訊要改 -->  
        <property name="diverClassName">com.mysql.jdbc.Driver</property>  
        <property name="url">jdbc:mysql://localhost:3306/test</property>  
        <property name="username">root</property>  
        <property name="password">root</property>  
  
        <!-- 新增durid驅動 -->  
        <property name="hibernate.connection.provider_class">com.alibaba.druid.support.hibernate.DruidConnectionProvider</property>  
  
        <!-- mysql方言 -->  
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</property>  
        <!-- 建立表方式為自動更新 -->  
        <property name="hibernate.hbm2ddl.auto">update</property>  
        <!-- 是否顯示sql語句 -->  
        <property name="hibernate.show_sql">true</property>  
        <!-- 是否格式化 -->  
        <property name="hibernate.format_sql">true</property>  
  
        <!-- 配置連線池初始化大小 -->  
        <property name="initialSize">2</property>  
        <!-- 最小空閒連線數 -->  
        <property name="minIdle">1</property>  
        <!-- 最大連線數 -->  
        <property name="maxActive">300</property>  
        <!-- 獲取連線等待超時的時間,單位:毫秒 -->  
        <property name="maxWait">60000</property>  
        <!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒 -->  
        <property name="timeBetweenEvictionRunsMillis">60000</property>  
        <!-- 配置一個連線在池中最小生存的時間,單位是毫秒 -->  
        <property name="minEvictableIdleTimeMillis">300000</property>  
  
        <!-- 開啟二級快取 -->  
        <property name="hibernate.cache.use_second_level_cache">true</property>  
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>  
  
        <!-- 對映位置 -->  
        <mapping resource="org/xxxx/pojo/UserInfo.hbm.xml" />  
  
    </session-factory>  
</hibernate-configuration>  

d.在src根目錄下配置ehcache.xml

name:cache唯一標識

eternal:快取是否永久有效  

maxElementsInMemory:記憶體中最大快取物件數

overflowToDisk(true,false):快取物件到最大數後,寫到硬碟中

diskPersistent:硬碟持久化 

timeToIdleSeconds:快取清除時間  

timeToLiveSeconds:快取存活時間

memoryStoreEvictionPolicy:快取清空策略 

FIFO:first in first out 先進先出 

LFU: Less Frequently Used 一直以來最少被使用的

LRU:Least Recently Used  最近最少使用的

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
	updateCheck="false">
	<defaultCache maxElementsInMemory="1000" eternal="false"
		timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"></defaultCache>
	<cache name="userCache" eternal="false" maxElementsInMemory="1000"
		overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="3600"
		timeToLiveSeconds="3600" memoryStoreEvictionPolicy="LFU"></cache>
</ehcache>

e.在需要被快取的物件中hbm檔案中的<class>標籤下新增<cache>子標籤

UserInfo.java

package org.xxxx.pojo;

import java.io.Serializable;

public class UserInfo implements Serializable {

	private static final long serialVersionUID = 1L;

	private int id;
	private String username;
	private String password;

	public UserInfo() {
		super();
		// TODO Auto-generated constructor stub
	}

	public UserInfo(String username, String password) {
		super();
		this.username = username;
		this.password = password;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "UserInfo [id=" + id + ", username=" + username + ", password=" + password + "]";
	}

}

UserInfo.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">
<!-- Generated 2018-1-13 20:10:19 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping package="org.xxxx.pojo">
    <class name="UserInfo" table="USERINFO">
    	<!-- 配置快取許可權為只讀 -->
    	<cache usage="read-only"></cache>
        <id name="id" type="int">
            <column name="UID" />
            <!-- 自增長 -->
            <generator class="native" />
        </id>
        <property name="username" type="java.lang.String">
            <column name="USERNAME" />
        </property>
        <property name="password" type="java.lang.String">
            <column name="PASSWORD" />
        </property>
    </class>
</hibernate-mapping>

f.測試

package org.xxxx.pojo;


import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Test_01 {
	public static void main(String[] args) {
		// 載入配置檔案
		Configuration config = new Configuration().configure();

		// 建立Session工廠
		SessionFactory factory = config.buildSessionFactory();

		// 獲取Session
		Session session = factory.openSession();

		// 獲取id為3的UserInfo,預設從Session快取中嘗試載入
		UserInfo uInfo = session.get(UserInfo.class, 3);
		System.out.println(uInfo);
		System.out.println("-------------------------------------");
		
		// 再次查詢
		UserInfo uInfo2 = session.get(UserInfo.class, 3);
		System.out.println(uInfo2);
		System.out.println("-------------------------------------");
		
		// 釋放資源
		session.close();

		// 再次開啟
		// 獲取Session
		Session session2 = factory.openSession();
		UserInfo uInfo3 = session2.get(UserInfo.class, 3);
		System.out.println(uInfo3);
		session2.close();
		
	}
}

第一次查詢會生成查詢語句,並查詢資料庫

再次查詢都會從cache中獲取資料

3.查詢快取

Hibernate還為查詢結果提供了一個查詢快取,它依賴於第二級快取

在配置檔案中開啟查詢快取

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                                         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<!-- 資料庫資訊要改 -->
		<property name="diverClassName">com.mysql.jdbc.Driver</property>
		<property name="url">jdbc:mysql://localhost:3306/test</property>
		<property name="username">root</property>
		<property name="password">root</property>

		<!-- 新增durid驅動 -->
		<property name="hibernate.connection.provider_class">com.alibaba.druid.support.hibernate.DruidConnectionProvider</property>

		<!-- mysql方言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</property>
		<!-- 建立表方式為自動更新 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 是否顯示sql語句 -->
		<property name="hibernate.show_sql">true</property>
		<!-- 是否格式化 -->
		<property name="hibernate.format_sql">true</property>

		<!-- 配置連線池初始化大小 -->
		<property name="initialSize">2</property>
		<!-- 最小空閒連線數 -->
		<property name="minIdle">1</property>
		<!-- 最大連線數 -->
		<property name="maxActive">300</property>
		<!-- 獲取連線等待超時的時間,單位:毫秒 -->
		<property name="maxWait">60000</property>
		<!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒 -->
		<property name="timeBetweenEvictionRunsMillis">60000</property>
		<!-- 配置一個連線在池中最小生存的時間,單位是毫秒 -->
		<property name="minEvictableIdleTimeMillis">300000</property>

		<!-- 開啟查詢快取 -->
		<property name="hibernate.cache.use_query_cache">true</property>

		<!-- 開啟二級快取 -->
		<property name="hibernate.cache.use_second_level_cache">true</property>
		<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

		<!-- 對映位置 -->
		<mapping resource="org/xxxx/pojo/UserInfo.hbm.xml" />

	</session-factory>
</hibernate-configuration>

需要在所有的查詢方法中設定setCacheable(true)

測試

package org.xxxx.pojo;


import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;

public class Test_01 {
	public static void main(String[] args) {
		// 載入配置檔案
		Configuration config = new Configuration().configure();

		// 建立Session工廠
		SessionFactory factory = config.buildSessionFactory();

		// 獲取Session
		Session session = factory.openSession();

		// 定義hql語句,獲取id為1的資訊
		String hql = "from UserInfo where id=1";
		
		// 執行
		@SuppressWarnings("unchecked")
		Query<UserInfo> query = session.createQuery(hql).setCacheable(true);
		
		// 獲取結果
		UserInfo uInfo = query.uniqueResult();
		System.out.println(uInfo);
		
		// 釋放資源
		session.close();
		
		System.out.println("-----------------------------------------------------");
		
		// 再次執行上面程式碼
		// 獲取Session
		Session session1 = factory.openSession();

		// 執行
		@SuppressWarnings("unchecked")
		Query<UserInfo> query1 = session1.createQuery(hql).setCacheable(true);
		
		// 獲取結果
		UserInfo uInfo1 = query1.uniqueResult();
		System.out.println(uInfo1);
		
		// 釋放資源
		session1.close();
	}
}

可以看出,第二次查詢沒有連線資料庫,直接從快取中拿到

B.通用HibernateDAO工具類(配置檔案和UserInfo參照上面的)

1.獲取Session工具類

HibernateUtil類,簡化獲取和關閉Session,使用到了單例模式

package org.xxxx.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
	// 執行緒本地變數,存放執行緒物件session
	private static final ThreadLocal<Session> threadLocal = new ThreadLocal<>();
	private static final SessionFactory factory;

	// 初始化,保證只有一個factory,防止出現多執行緒問題
	static {
		Configuration config = new Configuration().configure();
		factory = config.buildSessionFactory();
	}

	// 私有構造,不讓建立物件
	private HibernateUtil() {
	}

	// 獲取Session
	public static Session getSession() {
		// 從執行緒本地變數中獲取當前執行緒物件
		Session session = threadLocal.get();

		// 判斷是否存在
		if (session == null) {
			// 為空,建立一個
			session = factory.openSession();

			// 放置到執行緒變數中
			threadLocal.set(session);
		}
		return session;
	}

	// 關閉Session
	public static void closeSession() {
		// 獲取session
		Session session = threadLocal.get();

		// 非空判斷
		if (session != null) {
			session.close();

			// 將執行緒變數置空
			threadLocal.set(null);
		}
	}
}

2.通用DAO介面

package org.xxxx.util;

import java.io.Serializable;
import java.util.List;

public interface BaseDAO<T> {
	// 儲存
	public Serializable save(final T entity);

	// 更新,必須帶有主鍵
	public void update(final T entity);

	// 儲存或更新
	public void saveOrUpdate(final T entity);

	// 刪除,必須帶有主鍵
	public void delete(final T entity);

	// 通過物件識別符號獲取物件
	public T findById(final Serializable oid);

	// 返回所有物件
	public List<T> findAll();

	// 獲取分頁記錄
	List<T> findByPage(String hql, int start, int pageSize);

	// 通用查詢方法:queryName是命名查詢名字
	public List<T> executeQuery(String hql, Object... params);

	// 通用查詢方法:queryName是命名查詢名字
	public List<T> executeNamedQuery(String queryName, Object... params);

	// 通用更新方法:queryName是命名查詢名字
	public int executeUpdate(String hql, Object... params);

	// 通用更新方法:queryName是命名查詢名字
	public int executeNamedUpdate(String hqlName, Object... params);
}

3.通用介面子類

具體抽象實現類可以實現自己的介面,同時繼承該子類

使用抽象類不能被例項化,要使用該實現類必須繼承

子實現類

package org.xxxx.util;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.hibernate.query.Query;

// 抽象類,不能被例項化,要使用只能繼承
public abstract class BaseDAPOImpl<T> implements BaseDAO<T> {
	// 儲存泛型的實際引數,即子類所指定的T所對應的型別
	// Hibernate底層通過反射實現,所以T即為所對應的類的型別
	private Class<T> clazz;

	@SuppressWarnings("unchecked")
	public BaseDAPOImpl() {
		// 返回實際泛型型別
		Type type = getClass().getGenericSuperclass();
		ParameterizedType types = (ParameterizedType) type;
		Type[] typeArr = types.getActualTypeArguments();
		this.clazz = (Class<T>) typeArr[0].getClass();
	}

	@Override
	public Serializable save(T entity) {
		Serializable uid = HibernateUtil.getSession().save(entity);
		return uid;
	}

	@Override
	public void update(T entity) {
		HibernateUtil.getSession().update(entity);
	}

	@Override
	public void saveOrUpdate(T entity) {
		HibernateUtil.getSession().saveOrUpdate(entity);
	}

	@Override
	public void delete(T entity) {
		HibernateUtil.getSession().delete(entity);
	}

	@Override
	public T findById(Serializable oid) {
		return HibernateUtil.getSession().get(clazz, oid);
	}

	@Override
	public List<T> findAll() {
		// 通過反射獲取類名
		@SuppressWarnings("unchecked")
		Query<T> query = HibernateUtil.getSession().createQuery("from" + clazz.getSimpleName());
		return query.getResultList();
	}

	@Override
	public List<T> findByPage(String hql, int start, int pageSize) {
		@SuppressWarnings("unchecked")
		Query<T> query = HibernateUtil.getSession().createQuery(hql);

		// 設定開始id以及每頁顯示數量
		query.setFirstResult(start);
		query.setMaxResults(pageSize);
		return query.getResultList();
	}

	@Override
	public List<T> executeQuery(String hql, Object... params) {
		@SuppressWarnings("unchecked")
		Query<T> query = HibernateUtil.getSession().createQuery(hql);

		// 獲取引數長度
		int len = params.length;

		// 非空判斷
		if (len != 0) {
			for (int i = 0; i < len; i++) {
				// 設定第i個引數
				query.setParameter(i, params[i]);
			}
		}

		return query.getResultList();
	}

	@Override
	public List<T> executeNamedQuery(String queryName, Object... params) {
		@SuppressWarnings("unchecked")
		Query<T> query = HibernateUtil.getSession().getNamedQuery(queryName);

		// 設定引數
		int len = params.length;

		if (len != 0) {
			for (int i = 0; i < len; i++) {
				query.setParameter(i, params[i]);
			}
		}
		return query.getResultList();
	}

	@Override
	public int executeUpdate(String hql, Object... params) {
		@SuppressWarnings("unchecked")
		Query<T> query = HibernateUtil.getSession().createQuery(hql);

		int len = params.length;

		if (len != 0) {
			for (int i = 0; i < len; i++) {
				query.setParameter(i, params[i]);
			}
		}
		return query.executeUpdate();
	}

	@Override
	public int executeNamedUpdate(String hqlName, Object... params) {
		@SuppressWarnings("unchecked")
		Query<T> query = HibernateUtil.getSession().getNamedQuery(hqlName);

		int len = params.length;

		if (len != 0) {
			for (int i = 0; i < len; i++) {
				query.setParameter(i, params[i]);
			}
		}

		return query.executeUpdate();
	}

}

子實現類的子類

package org.xxxx.util;

// 可以擴充套件父類的功能
public class UserInfoDaoImpl<UserInfo> extends BaseDAPOImpl<UserInfo> {
}

4.測試

package org.xxxx.util;

import org.hibernate.Transaction;
import org.xxxx.pojo.UserInfo;

public class TestDao {
	public static void main(String[] args) {
		// 建立物件
		UserInfoDaoImpl<UserInfo> impl = new UserInfoDaoImpl<>();
		
		// 開啟事物
		Transaction action = HibernateUtil.getSession().beginTransaction();
		
		// 新增資料
		UserInfo uInfo = new UserInfo("yangqi", "123456");
		impl.save(uInfo);
		
		// 提交事物
		action.commit();
		
		// 關閉
		HibernateUtil.closeSession();
	}
}

查詢資料庫