【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(); } }
查詢資料庫