注解
Hibernate注解是一個沒有使用XML文件來定義映射的最新方法。可以在除或替換的XML映射元數據使用注解。
Hibernate的注解是強大的方式來提供元數據對象和關系表的映射。所有的元數據被杵到一起的代碼POJO java文件這可以幫助用戶在開發過程中同時要了解表的結構和POJO。
如果打算讓應用程序移植到其他EJB3規范的ORM應用程序,必須使用注解來表示映射信息,但仍然如果想要更大的靈活性,那么應該使用基于XML的映射去。
環境設置Hibernate注釋
首先,必須確保使用的是JDK5.0,否則,需要JDK升級到JDK5.0帶注解的原生支持的優勢。
其次,需要安裝Hibernate的3.x注解分發包,可從SourceForge上: (Download Hibernate Annotation) 并拷貝 hibernate-annotations.jar, lib/hibernate-comons-annotations.jar 和 lib/ejb3-persistence.jar 從Hibernate注解分配到CLASSPATH
注釋的類實例:
正如提到的,同時使用Hibernate注釋工作的所有元數據杵成隨著代碼的POJO java文件上面這可以幫助用戶在開發過程中同時了解表結構和POJO。
考慮到將要使用下面的EMPLOYEE表來存儲的對象:
1
2
3
4
5
6
7
|
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) ); |
下面是用注解來映射與定義的EMPLOYEE表的對象Employee類的映射:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
import javax.persistence.*; @Entity @Table (name = "EMPLOYEE" ) public class Employee { @Id @GeneratedValue @Column (name = "id" ) private int id; @Column (name = "first_name" ) private String firstName; @Column (name = "last_name" ) private String lastName; @Column (name = "salary" ) private int salary; public Employee() {} 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; } } |
Hibernate檢測@Id注釋是對一個字段,并假設它應該直接通過在運行時域訪問一個對象的屬性。如果將@Id注釋getId()方法,將通過getter和setter方法??默認情況下允許訪問屬性。因此,所有其他注釋也被放置在任一字段或getter方法??,以下所選擇的策略。下面的部分將解釋在上面的類中使用的注釋。
@Entity 注解:
在EJB3規范說明都包含在javax.persistence包,所以我們導入這個包作為第一步。其次,我們使用了@Entity注解來這標志著這個類作為一個實體bean Employee類,因此它必須有一個無參數的構造函數,總算是有保護的范圍可見。
@Table 注解:
@Table注釋允許指定的表將被用于保存該實體在數據庫中的詳細信息。
@Table注釋提供了四個屬性,允許覆蓋表的名稱,它的目錄,它的架構,并執行對列的唯一約束在表中。現在,我們使用的是剛剛是EMPLOYEE表的名稱。
@Id 和 @GeneratedValue 注解:
每個實體bean將有一個主鍵,注釋在類的@Id注解。主鍵可以是單個字段或根據表結構的多個字段的組合。
默認情況下,@Id注解會自動確定要使用的最合適的主鍵生成策略,但可以通過應用@GeneratedValue注釋,它接受兩個參數,strategy和generator,不打算在這里討論,只使用默認的默認鍵生成策略。讓Hibernate確定要使用的generator類型使不同數據庫之間代碼的可移植性。
@Column 注解:
@Column批注用于指定的列到一個字段或屬性將被映射的細節。可以使用列注釋以下最常用的屬性:
name屬性允許將顯式指定列的名稱。
length 屬性允許用于映射一個value尤其是對一個字符串值的列的大小。
nullable 屬性允許該列被標記為NOT NULL生成架構時。
unique 屬性允許被標記為只包含唯一值的列。
創建應用程序類:
最后,我們將創建應用程序類的main()方法來運行應用程序。我們將使用這個應用程序,以節省一些員工的記錄,然后我們將申請CRUD操作上的記錄。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
import java.util.List; import java.util.Date; import java.util.Iterator; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class ManageEmployee { private static SessionFactory factory; public static void main(String[] args) { try { factory = new AnnotationConfiguration(). configure(). //addPackage("com.xyz") //add package if used. addAnnotatedClass(Employee. class ). 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", 1000); Integer empID2 = ME.addEmployee("Daisy", "Das", 5000); Integer empID3 = ME.addEmployee("John", "Paul", 10000); /* List down all the employees */ ME.listEmployees(); /* Update employee's records */ ME.updateEmployee(empID1, 5000); /* Delete an employee from the database */ ME.deleteEmployee(empID2); /* List down new list of the employees */ ME.listEmployees(); } /* 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(); employee.setFirstName(fname); employee.setLastName(lname); employee.setSalary(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 */ public void listEmployees( ){ Session session = factory.openSession(); Transaction tx = null; try{ tx = session.beginTransaction(); List employees = session.createQuery("FROM Employee").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(); } } /* Method to UPDATE salary for an employee */ public void updateEmployee(Integer EmployeeID, int salary ){ Session session = factory.openSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Employee employee = (Employee)session.get(Employee.class, EmployeeID); employee.setSalary( salary ); session.update(employee); tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } } /* Method to DELETE an employee from the records */ public void deleteEmployee(Integer EmployeeID){ Session session = factory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); Employee employee = (Employee)session.get(Employee. class , EmployeeID); session.delete(employee); tx.commit(); } catch (HibernateException e) { if (tx!= null ) tx.rollback(); e.printStackTrace(); } finally { session.close(); } } } |
數據庫配置:
現在,讓我們創建hibernate.cfg.xml配置文件來定義數據庫的相關參數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<? 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" > cohondob </ property > </ session-factory > </ hibernate-configuration > |
編譯和執行:
下面是步驟來編譯并運行上述應用程序。請確保已在進行的編譯和執行之前,適當地設置PATH和CLASSPATH。
從路徑中刪除Employee.hbm.xml映射文件。
創建Employee.java源文件,如上圖所示,并編譯它。
創建ManageEmployee.java源文件,如上圖所示,并編譯它。
執行ManageEmployee二進制文件來運行程序。
會得到以下結果,并記錄將在EMPLOYEE表中。
1
2
3
4
5
6
7
8
|
$java ManageEmployee .......VARIOUS LOG MESSAGES WILL DISPLAY HERE........ First Name: Zara Last Name: Ali Salary: 1000 First Name: Daisy Last Name: Das Salary: 5000 First Name: John Last Name: Paul Salary: 10000 First Name: Zara Last Name: Ali Salary: 5000 First Name: John Last Name: Paul Salary: 10000 |
如果檢查EMPLOYEE表,它應該有以下記錄:
1
2
3
4
5
6
7
8
9
10
|
mysql> select * from EMPLOYEE; + ----+------------+-----------+--------+ | id | first_name | last_name | salary | + ----+------------+-----------+--------+ | 29 | Zara | Ali | 5000 | | 31 | John | Paul | 10000 | + ----+------------+-----------+--------+ 2 rows in set (0.00 sec mysql> |
緩存
緩存是所有關于應用程序的性能優化和它位于應用程序和數據庫之間,以避免數據庫訪問多次,讓性能關鍵型應用程序有更好的表現。
緩存對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實例所需的映射元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<? 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作為第二級緩存提供程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<? 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類高速緩存配置可能看起來像這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
< 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來存儲和查找在緩存中的員工方面的查詢。