Hibernate快取
快取是所有關於應用程式的效能最佳化和它位於應用程式和資料庫之間,以避免資料庫存取多次,讓效能關鍵型應用程式有更好的表現。
緩存對Hibernate很重要,它採用了多級緩存方案下文所述:
第一級緩存:
第一級緩存是Session的緩存,是一個強制性的緩存,通過它所有的請求都必須通過。 Session物件不斷自身的動力的對象,提交到資料庫之前。
如果發出多個更新一個對象,Hibernate試圖拖延盡可能長的時間做了更新,以減少發出的更新SQL語句的數量。如果您關閉會話,所有被快取的物件都會遺失,要么持久,或在資料庫中更新。
二級快取:
二級快取是可選的快取和一級緩存,總是會徵詢任何試圖找到一個物件的二級快取之前。第二級快取可以在每個類別和每個集合基礎上進行配置,主要負責在會話快取的物件。
任何第三方快取可以使用Hibernate。 org.hibernate.cache.CacheProvider介面提供,必須實作提供Hibernate一個句柄快取實作。
查詢層級快取:
Hibernate也實現了查詢結果集快取與二級快取的緊密整合在一起。
這是一個可選功能,需要兩個額外的實體快取中保存快取的查詢結果和地區當一個表的最後更新的時間戳。這只是針對那些使用相同的參數經常運行的查詢非常有用。
二級快取:
Hibernate使用一級緩存,預設情況下,你什麼都沒做使用第一級快取。讓我們直接進入可選的第二級快取。並不是所有的類別受益於緩存,這樣一來就能禁用二級緩存是很重要的
Hibernate二級緩存被設定為兩個步驟。首先,必須決定要使用的並發策略。在此之後,可以配置快取過期和使用快取提供實體快取屬性。
並發策略:
並發策略是一個中介的負責儲存資料項目在快取並從快取中檢索它們。如果要啟用二級緩存,將必須決定,為每個持久化類別和集合,要使用的快取並發策略。
Transactional: 使用此策略的主要讀取資料的地方,以防止過時的資料的並發事務,在更新的罕見情況下是至關重要的。
Read-write: 再次使用這種策略的主要讀取資料的地方,以防止並發事務陳舊的資料是至關重要的,在更新的罕見情況。
Nonstrict-read-write: 這種策略不保證快取與資料庫之間的一致性。使用此策略,如果數據很少改變和陳舊數據的可能性很小關鍵是不關注。
Read-only: 並發策略適用於數據,永遠不會改變。使用數據僅供參考。
如果我們要使用第二級快取為我們的Employee類,讓我們加入告訴Hibernate使用可讀寫的快取策略Employee實例所需的映射元素。
<?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使用一個可讀寫的並發策略定義的快取。
快取提供者:
考慮到會用你的快取候選類別的並發策略後,下一步就是選擇一個快取提供者。 Hibernate迫使選擇一個快取提供整個應用程式。
在指定hibernate.cfg.xml設定檔中的快取提供。選擇EHCache作為第二層快取提供者:
<?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>
現在,需要指定快取區域的屬性。 EHCache都有自己的設定檔ehcache.xml,在應用程式在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" />
就這樣,現在啟用Employee類的二級緩存和Hibernate現在二級緩存,每當瀏覽到一個僱員或當通過標識符加載僱員。
應該分析你所有的類,並選擇適當的快取策略為每個類別。有時,二級快取可能降級的應用程式的效能。所以建議到基準應用程式第一次沒有啟用緩存,非常適合快取和檢查效能。如果快取不提高系統效能再有就是在使任何類型的快取是沒有意義的。
查詢等級快取:
使用查詢緩存,必須先使用 hibernate.cache.use_query_cache="true"屬性設定檔中啟動它。如果將此屬性設為true,讓Hibernate的在記憶體中建立所需的快取來保存查詢和識別符集。
接下來,使用查詢緩存,可以使用Query類別的setCacheable(Boolean)方法。例如:
Session session = SessionFactory.openSession(); Query query = session.createQuery("FROM EMPLOYEE"); query.setCacheable(true); List users = query.list(); SessionFactory.closeSession();
Hibernate也支持通过一个缓存区域的概念非常细粒度的缓存支持。缓存区是这是给定一个名称缓存的一部分。
Session session = SessionFactory.openSession(); Query query = session.createQuery("FROM EMPLOYEE"); query.setCacheable(true); query.setCacheRegion("employee"); List users = query.list(); SessionFactory.closeSession();
此代码使用方法告诉Hibernate来存储和查找在缓存中的员工方面的查询。
Hibernate原生SQL
可以使用原生SQL来表达数据库查询,如果想利用数据库特有的功能,如查询提示或者Oracle中的CONNECT关键字。 Hibernate3.x允许使用手写SQL语句,包括存储过程,所有的创建,更新,删除和load操作。
应用程序将从会话创建一个原生SQL查询(Session接口上)createSQLQuery()方法:
public SQLQuery createSQLQuery(String sqlString) throws HibernateException
当传递一个包含SQL查询到createSQLQuery()方法,可以将SQL结果与任何现有的Hibernate实体,联接,或者一个标量结果使用addEntity()方法,addJoin(),和addScalar()方法关联的字符串。
标量查询:
最基本的SQL查询是从一个或多个表中得到标量(数值)的列表。以下是语法使用原生SQL标量的值:
String sql = "SELECT first_name, salary FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); List results = query.list();
实体的查询:
上面的查询都是返回标量值,也就是从resultset中返回的“裸”数据。以下是语法通过addEntity()方法来从原生SQL查询获得实体对象作为一个整体。
String sql = "SELECT * FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); List results = query.list();
命名SQL查询:
以下是语法通过addEntity()方法来从原生SQL查询获得实体对象和使用命名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; } }
让我们创建下面的EMPLOYEE表来存储Employee对象:
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>
最后,我们将创建应用程序类的main()方法来运行,我们将使用原生SQL查询的应用程序:
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(); } } }
编译和执行:
下面是步骤来编译并运行上述应用程序。请确保在进行的编译和执行之前,适当地设置PATH和CLASSPATH。
执行ManageEmployee二进制文件来运行程序。
会得到以下结果,并记录将在EMPLOYEE表中创建。
$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表,它应该记录下已:
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)
更多詳解Java的Hibernate框架中的快取與原生SQL語句的使用相关文章请关注PHP中文网!