詳解Java的Hibernate框架中的緩存與原生SQL語句的使用
Hibernate緩存
緩存是所有關(guān)于應(yīng)用程序的性能優(yōu)化和它位于應(yīng)用程序和數(shù)據(jù)庫之間,以避免數(shù)據(jù)庫訪問多次,讓性能關(guān)鍵型應(yīng)用程序有更好的表現(xiàn)。
緩存對(duì)Hibernate很重要,它采用了多級(jí)緩存方案下文所述:
第一級(jí)緩存:
第一級(jí)緩存是Session的緩存,是一個(gè)強(qiáng)制性的緩存,通過它所有的請(qǐng)求都必須通過。 Session對(duì)象不斷自身的動(dòng)力的對(duì)象,提交到數(shù)據(jù)庫之前。
如果發(fā)出多個(gè)更新一個(gè)對(duì)象,Hibernate試圖拖延盡可能長的時(shí)間做了更新,以減少發(fā)出的更新SQL語句的數(shù)量。如果您關(guān)閉會(huì)話,所有被緩存的對(duì)象都將丟失,要么持久,或在數(shù)據(jù)庫中更新。
二級(jí)緩存:
二級(jí)緩存是可選的緩存和一級(jí)緩存,總是會(huì)征詢?nèi)魏卧噲D找到一個(gè)對(duì)象的二級(jí)緩存之前。第二級(jí)緩存可以在每個(gè)類和每個(gè)集合基礎(chǔ)上進(jìn)行配置,主要負(fù)責(zé)在會(huì)話緩存的對(duì)象。
任何第三方緩存可以使用Hibernate。org.hibernate.cache.CacheProvider接口提供,必須實(shí)施提供Hibernate一個(gè)句柄緩存實(shí)現(xiàn)。
查詢級(jí)別緩存:
Hibernate也實(shí)現(xiàn)了查詢結(jié)果集緩存與二級(jí)緩存的緊密集成在一起。
這是一個(gè)可選功能,需要兩個(gè)額外的物理緩存中保存緩存的查詢結(jié)果和地區(qū)當(dāng)一個(gè)表的最后更新的時(shí)間戳。這只是針對(duì)那些使用相同的參數(shù)經(jīng)常運(yùn)行的查詢非常有用。
二級(jí)緩存:
Hibernate使用一級(jí)緩存,默認(rèn)情況下,你什么都沒有做使用第一級(jí)緩存。讓我們直接進(jìn)入可選的第二級(jí)緩存。并不是所有的類受益于緩存,這樣一來就能禁用二級(jí)緩存是很重要的
Hibernate二級(jí)緩存被設(shè)置為兩個(gè)步驟。首先,必須決定要使用的并發(fā)策略。在此之后,可以配置緩存過期和使用緩存提供物理緩存屬性。
并發(fā)策略:
并發(fā)策略是一個(gè)中介的負(fù)責(zé)存儲(chǔ)數(shù)據(jù)項(xiàng)在緩存并從緩存中檢索它們。如果要啟用二級(jí)緩存,將必須決定,為每個(gè)持久化類和集合,要使用的緩存并發(fā)策略。
Transactional: 使用這種策略的主要讀取數(shù)據(jù)的地方,以防止過時(shí)的數(shù)據(jù)的并發(fā)事務(wù),在更新的罕見情況下是至關(guān)重要的。
Read-write: 再次使用這種策略的主要讀取數(shù)據(jù)的地方,以防止并發(fā)事務(wù)陳舊的數(shù)據(jù)是至關(guān)重要的,在更新的罕見情況。
Nonstrict-read-write: 這種策略不保證緩存與數(shù)據(jù)庫之間的一致性。使用此策略,如果數(shù)據(jù)很少改變和陳舊數(shù)據(jù)的可能性很小關(guān)鍵是不關(guān)注。
Read-only: 并發(fā)策略適用于數(shù)據(jù),永遠(yuǎn)不會(huì)改變。使用數(shù)據(jù)僅供參考。
如果我們要使用第二級(jí)緩存為我們的Employee類,讓我們添加告訴Hibernate使用可讀寫的高速緩存策略Employee實(shí)例所需的映射元素。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="Employee" table="EMPLOYEE"> <meta attribute="class-description"> This class contains the employee detail. </meta> <cache usage="read-write"/> <id name="id" type="int" column="id"> <generator class="native"/> </id> <property name="firstName" column="first_name" type="string"/> <property name="lastName" column="last_name" type="string"/> <property name="salary" column="salary" type="int"/> </class> </hibernate-mapping>
usage="read-write" 屬性告訴Hibernate使用一個(gè)可讀寫的并發(fā)策略定義的緩存。
緩存提供者:
考慮到會(huì)用你的緩存候選類的并發(fā)策略后,下一步就是選擇一個(gè)緩存提供程序。Hibernate迫使選擇一個(gè)緩存提供整個(gè)應(yīng)用程序。
在指定hibernate.cfg.xml配置文件中的緩存提供。選擇EHCache作為第二級(jí)緩存提供程序:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- Assume students is the database name --> <property name="hibernate.connection.url"> jdbc:mysql://localhost/test </property> <property name="hibernate.connection.username"> root </property> <property name="hibernate.connection.password"> root123 </property> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> <!-- List of XML mapping files --> <mapping resource="Employee.hbm.xml"/> </session-factory> </hibernate-configuration>
現(xiàn)在,需要指定緩存區(qū)域的屬性。EHCache都有自己的配置文件ehcache.xml,在應(yīng)用程序在CLASSPATH中。在ehcache.xml中Employee類高速緩存配置可能看起來像這樣:
<diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> <cache name="Employee" maxElementsInMemory="500" eternal="true" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" />
就這樣,現(xiàn)在啟用Employee類的二級(jí)緩存和Hibernate現(xiàn)在二級(jí)緩存,每當(dāng)瀏覽到一個(gè)雇員或當(dāng)通過標(biāo)識(shí)符加載雇員。
應(yīng)該分析你所有的類,并選擇適當(dāng)?shù)木彺娌呗詾槊總€(gè)類。有時(shí),二級(jí)緩存可能降級(jí)的應(yīng)用程序的性能。所以建議到基準(zhǔn)應(yīng)用程序第一次沒有啟用緩存,非常適合緩存和檢查性能。如果緩存不提高系統(tǒng)性能再有就是在使任何類型的緩存是沒有意義的。
查詢級(jí)別緩存:
使用查詢緩存,必須先使用 hibernate.cache.use_query_cache="true"屬性配置文件中激活它。如果將此屬性設(shè)置為true,讓Hibernate的在內(nèi)存中創(chuàng)建所需的高速緩存來保存查詢和標(biāo)識(shí)符集。
接下來,使用查詢緩存,可以使用Query類的setCacheable(Boolean)方法。例如:
Session session = SessionFactory.openSession(); Query query = session.createQuery("FROM EMPLOYEE"); query.setCacheable(true); List users = query.list(); SessionFactory.closeSession();
Hibernate也支持通過一個(gè)緩存區(qū)域的概念非常細(xì)粒度的緩存支持。緩存區(qū)是這是給定一個(gè)名稱緩存的一部分。
Session session = SessionFactory.openSession(); Query query = session.createQuery("FROM EMPLOYEE"); query.setCacheable(true); query.setCacheRegion("employee"); List users = query.list(); SessionFactory.closeSession();
此代碼使用方法告訴Hibernate來存儲(chǔ)和查找在緩存中的員工方面的查詢。
Hibernate原生SQL
可以使用原生SQL來表達(dá)數(shù)據(jù)庫查詢,如果想利用數(shù)據(jù)庫特有的功能,如查詢提示或者Oracle中的CONNECT關(guān)鍵字。 Hibernate3.x允許使用手寫SQL語句,包括存儲(chǔ)過程,所有的創(chuàng)建,更新,刪除和load操作。
應(yīng)用程序?qū)臅?huì)話創(chuàng)建一個(gè)原生SQL查詢(Session接口上)createSQLQuery()方法:
public SQLQuery createSQLQuery(String sqlString) throws HibernateException
當(dāng)傳遞一個(gè)包含SQL查詢到createSQLQuery()方法,可以將SQL結(jié)果與任何現(xiàn)有的Hibernate實(shí)體,聯(lián)接,或者一個(gè)標(biāo)量結(jié)果使用addEntity()方法,addJoin(),和addScalar()方法關(guān)聯(lián)的字符串。
標(biāo)量查詢:
最基本的SQL查詢是從一個(gè)或多個(gè)表中得到標(biāo)量(數(shù)值)的列表。以下是語法使用原生SQL標(biāo)量的值:
String sql = "SELECT first_name, salary FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); List results = query.list();
實(shí)體的查詢:
上面的查詢都是返回標(biāo)量值,也就是從resultset中返回的“裸”數(shù)據(jù)。以下是語法通過addEntity()方法來從原生SQL查詢獲得實(shí)體對(duì)象作為一個(gè)整體。
String sql = "SELECT * FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); List results = query.list();
命名SQL查詢:
以下是語法通過addEntity()方法來從原生SQL查詢獲得實(shí)體對(duì)象和使用命名SQL查詢。
String sql = "SELECT * FROM EMPLOYEE WHERE id = :employee_id"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); query.setParameter("employee_id", 10); List results = query.list();
Native SQL 例子:
考慮下面的POJO類:
public class Employee { private int id; private String firstName; private String lastName; private int salary; public Employee() {} public Employee(String fname, String lname, int salary) { this.firstName = fname; this.lastName = lname; this.salary = salary; } public int getId() { return id; } public void setId( int id ) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName( String first_name ) { this.firstName = first_name; } public String getLastName() { return lastName; } public void setLastName( String last_name ) { this.lastName = last_name; } public int getSalary() { return salary; } public void setSalary( int salary ) { this.salary = salary; } }
讓我們創(chuàng)建下面的EMPLOYEE表來存儲(chǔ)Employee對(duì)象:
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
以下將被映射文件。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="Employee" table="EMPLOYEE"> <meta attribute="class-description"> This class contains the employee detail. </meta> <id name="id" type="int" column="id"> <generator class="native"/> </id> <property name="firstName" column="first_name" type="string"/> <property name="lastName" column="last_name" type="string"/> <property name="salary" column="salary" type="int"/> </class> </hibernate-mapping>
最后,我們將創(chuàng)建應(yīng)用程序類的main()方法來運(yùn)行,我們將使用原生SQL查詢的應(yīng)用程序:
import java.util.*; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.SessionFactory; import org.hibernate.SQLQuery; import org.hibernate.Criteria; import org.hibernate.Hibernate; import org.hibernate.cfg.Configuration; public class ManageEmployee { private static SessionFactory factory; public static void main(String[] args) { try{ factory = new Configuration().configure().buildSessionFactory(); }catch (Throwable ex) { System.err.println("Failed to create sessionFactory object." + ex); throw new ExceptionInInitializerError(ex); } ManageEmployee ME = new ManageEmployee(); /* Add few employee records in database */ Integer empID1 = ME.addEmployee("Zara", "Ali", 2000); Integer empID2 = ME.addEmployee("Daisy", "Das", 5000); Integer empID3 = ME.addEmployee("John", "Paul", 5000); Integer empID4 = ME.addEmployee("Mohd", "Yasee", 3000); /* List down employees and their salary using Scalar Query */ ME.listEmployeesScalar(); /* List down complete employees information using Entity Query */ ME.listEmployeesEntity(); } /* Method to CREATE an employee in the database */ public Integer addEmployee(String fname, String lname, int salary){ Session session = factory.openSession(); Transaction tx = null; Integer employeeID = null; try{ tx = session.beginTransaction(); Employee employee = new Employee(fname, lname, salary); employeeID = (Integer) session.save(employee); tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } return employeeID; } /* Method to READ all the employees using Scalar Query */ public void listEmployeesScalar( ){ Session session = factory.openSession(); Transaction tx = null; try{ tx = session.beginTransaction(); String sql = "SELECT first_name, salary FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); List data = query.list(); for(Object object : data) { Map row = (Map)object; System.out.print("First Name: " + row.get("first_name")); System.out.println(", Salary: " + row.get("salary")); } tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } } /* Method to READ all the employees using Entity Query */ public void listEmployeesEntity( ){ Session session = factory.openSession(); Transaction tx = null; try{ tx = session.beginTransaction(); String sql = "SELECT * FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); List employees = query.list(); for (Iterator iterator = employees.iterator(); iterator.hasNext();){ Employee employee = (Employee) iterator.next(); System.out.print("First Name: " + employee.getFirstName()); System.out.print(" Last Name: " + employee.getLastName()); System.out.println(" Salary: " + employee.getSalary()); } tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } } }
編譯和執(zhí)行:
下面是步驟來編譯并運(yùn)行上述應(yīng)用程序。請(qǐng)確保在進(jìn)行的編譯和執(zhí)行之前,適當(dāng)?shù)卦O(shè)置PATH和CLASSPATH。
執(zhí)行ManageEmployee二進(jìn)制文件來運(yùn)行程序。
會(huì)得到以下結(jié)果,并記錄將在EMPLOYEE表中創(chuàng)建。
$java ManageEmployee
.......VARIOUS LOG MESSAGES WILL DISPLAY HERE........ First Name: Zara, Salary: 2000 First Name: Daisy, Salary: 5000 First Name: John, Salary: 5000 First Name: Mohd, Salary: 3000 First Name: Zara Last Name: Ali Salary: 2000 First Name: Daisy Last Name: Das Salary: 5000 First Name: John Last Name: Paul Salary: 5000 First Name: Mohd Last Name: Yasee Salary: 3000
如果檢查EMPLOYEE表,它應(yīng)該記錄下已:
mysql> select * from EMPLOYEE;
+----+------------+-----------+--------+ | id | first_name | last_name | salary | +----+------------+-----------+--------+ | 26 | Zara | Ali | 2000 | | 27 | Daisy | Das | 5000 | | 28 | John | Paul | 5000 | | 29 | Mohd | Yasee | 3000 | +----+------------+-----------+--------+ 4 rows in set (0.00 sec)
相關(guān)文章
Spring與MyBatis集成?AOP整合PageHelper插件的操作過程
Spring與MyBatis集成的主要目的是為了提供更強(qiáng)大的數(shù)據(jù)訪問和事務(wù)管理能力,以及簡化配置和提高開發(fā)效率,這篇文章主要介紹了Spring與MyBatis集成AOP整合PageHelper插件,需要的朋友可以參考下2023-08-08java常用Lambda表達(dá)式使用場(chǎng)景源碼示例
這篇文章主要為大家介紹了java常用Lambda表達(dá)式使用場(chǎng)景源碼示例及應(yīng)用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03Spring Data JPA中 in 條件參數(shù)的傳遞方式
這篇文章主要介紹了Spring Data JPA中 in 條件參數(shù)的傳遞方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Java實(shí)現(xiàn)讀取TXT和CSV文件內(nèi)容
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)讀取TXT和CSV文件內(nèi)容的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-02-02Java Servlet線程中AsyncContext異步處理Http請(qǐng)求
這篇文章主要介紹了Java Servlet線程中AsyncContext異步處理Http請(qǐng)求及在業(yè)務(wù)中應(yīng)用,AsyncContext是Servlet 3.0使Servlet 線程不再需要一直阻塞,直到業(yè)務(wù)處理完畢才能再輸出響應(yīng),最后才結(jié)束該Servlet線程2023-03-03org.apache.ibatis.binding.BindingException異常報(bào)錯(cuò)原因以及詳細(xì)解決方案
這篇文章主要給大家介紹了關(guān)于org.apache.ibatis.binding.BindingException異常報(bào)錯(cuò)原因以及詳細(xì)解決方案的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02MyBatis圖文并茂講解注解開發(fā)一對(duì)多查詢
這篇文章主要介紹了SpringBoot中Mybatis注解一對(duì)多查詢的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07解決JSONObject.toJSONString()輸出null的問題
這篇文章主要介紹了解決JSONObject.toJSONString()輸出null的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02