原創播客,如需轉載請注明出處。原文地址:http://www.cnblogs.com/crawl/p/7704914.html
-----------------------------------------------------------------------------------------------------------------------------------------------------------
筆記中提供了大量的代碼示例,需要說明的是,大部分代碼示例都是本人所敲代碼並進行測試,不足之處,請大家指正~
本博客中所有言論僅代表博主本人觀點,若有疑惑或者需要本系列分享中的資料工具,敬請聯系 qingqing_crawl@163.com
-----------------------------------------------------------------------------------------------------------------------------------------------------------
前言:繼續介紹 JPA ,這一篇將介紹 JPA 的常用 API,以及在 JPA 中映射關聯關系。上一篇講到 JPA 和 Hibernate 關系密切,尤其是在 API 和映射關聯關系上,大家可以參看樓主關於 Hibernate 介紹的博客 Hibernate 學習筆記 - 1 和 Hibernate 學習筆記 - 2 ,與 Hibernate 類似的地方樓主也會特別指出。
四、JPA 的 API
1.Persistence :用於獲取 EntiryManagerFactory 的實例
1)常用方法:Persistence.createEntityManagerFactory(persistenceUnitName) 方法
1 String persistenceUnitName = "jpa-1"; 2 EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
2. EntiryManagerFactory :常用方法
1)獲取 EntiryManager
1 //創建 EntityManager,類似於 Hibernate 的 SessionFactory 2 EntityManager entityManager = entityManagerFactory.createEntityManager();
2)close() 方法,關閉自身,此方法不再演示
3. EntityManager 的常用 API
1)find() 方法,類似於 Hibernate 中的 Session 的 get() 方法,在執行 find 方法時就發送 SQL 語句
1 //類似於 Hibernate 中 Session 的 get 方法 2 @Test 3 public void testFind() { 4 Customer customer = entityManager.find(Customer.class, 1); 5 6 System.out.println("----------------------------------------"); 7 8 System.out.println(customer); 9 }
打印結果為:查看橫線的位置便可證明結論。
1 Hibernate: 2 select 3 customer0_.id as id1_2_0_, 4 customer0_.age as age2_2_0_, 5 customer0_.birth as birth3_2_0_, 6 customer0_.createTime as createTi4_2_0_, 7 customer0_.email as email5_2_0_, 8 customer0_.LAST_NAME as LAST_NAM6_2_0_ 9 from 10 JPA_CUSTOMER customer0_ 11 where 12 customer0_.id=? 13 ---------------------------------------- 14 Customer [id=1, lastName=AA, email=aa@163.com, age=21, birth=2015-10-22, createTime=2017-10-11 22:39:13.0]
2)getReference() 方法,類似於 Hibernate 的 Session 的 load() 方法
1 //相當於 Hibernate 中 Session 的 load 方法,若不使用查詢的對象則返回一個代理對象,到真正使用時才發送 SQL 語句查詢 2 //可能會發生懶加載異常 3 @Test 4 public void testGetReference() { 5 Customer customer = entityManager.getReference(Customer.class, 1); 6 System.out.println(customer.getClass().getName()); 7 8 System.out.println("---------------------------------------"); 9 10 // transaction.commit(); 11 // entityManager.close(); 12 13 System.out.println(customer); 14 }
打印結果為:打印的是一個代理對象,並且橫線打印在 SQL 前面。
com.software.jpa.helloworld.Customer_$$_javassist_1 --------------------------------------- Hibernate: select customer0_.id as id1_2_0_, customer0_.age as age2_2_0_, customer0_.birth as birth3_2_0_, customer0_.createTime as createTi4_2_0_, customer0_.email as email5_2_0_, customer0_.LAST_NAME as LAST_NAM6_2_0_ from JPA_CUSTOMER customer0_ where customer0_.id=? Customer [id=1, lastName=AA, email=aa@163.com, age=21, birth=2015-10-22, createTime=2017-10-11 22:39:13.0]
3)persistence() 方法,類似於 Hibernate 的 save() 方法,與 Hibernate 的 save() 方法不同的是其不能插入一個有 id 屬性的對象
1 //類似於 Hibernate 的 save 方法,使對象由臨時狀態變為持久化對象 2 //和 Hibernate 的 save 方法的區別為若有 id 屬性,則不會執行插入操作而會拋出異常 3 @Test 4 public void testPersistence() { 5 Customer customer = new Customer(); 6 customer.setLastName("BB"); 7 customer.setEmail("bb@163.com"); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setAge(21); 11 12 // customer.setId(100); 13 14 entityManager.persist(customer); 15 16 System.out.println(customer.getId()); 17 18 }
4)remove() 方法,類似於 Hibernate 中 Session 的 delete 方法,但是其不能刪除 游離化對象(僅有 id),執行 5,6行會拋出異常,因為 5 行的 customer 對象為游離化對象
1 //類似於 Hibernate Session 的 delete 方法,把對象對應的記錄從數據庫中刪除 2 //注:該方法只能移出 持久化 對象,而 Hibernate 的 delete 方法可以移除游離對象 3 @Test 4 public void testRemove() { 5 // Customer customer = new Customer(); 6 // customer.setId(2); 7 8 Customer customer = entityManager.find(Customer.class, 2); 9 10 entityManager.remove(customer); 11 12 }
5)merge() 方法,類似於 Hibernate 中 Session 的 saveOrUpdate() 方法
① 傳入的是一個臨時對象(沒有 id):會創建一個新的對象,把臨時對象的屬性復制到新的對象中,然后對新的對象執行持久化操作,13行執行了 merge() 方法,傳入了一個臨時對象,返回了一個新的對象,產看 15,16 行的結果可知,新的對象有 id,傳入的對象木有id,說明是將新的對象插入了數據庫
1 //1.若傳入的是一個臨時對象(沒有 Id) 2 //會創建一個新的對象,把臨時對象的屬性復制到新的對象中,然后對新的對象執行持久化操作 3 //所以 新的對象中有 id,而之前的臨時對象中沒有 id 4 @Test 5 public void testMerge1() { 6 Customer customer = new Customer(); 7 customer.setAge(23); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setEmail("cc@126.com"); 11 customer.setLastName("CC"); 12 13 Customer customer2 = entityManager.merge(customer); 14 15 System.out.println("customer's id:" + customer.getId());// null 16 System.out.println("customer's id:" + customer2.getId());// 2 17 }
② 傳入的是一個游離對象(有 ID):若在 EntityManager 緩存中沒有該對象,在數據庫中也沒有對應的記錄,JPA 會創建一個新的對象,把當前游離對象的屬性復制到新的對象中,對新創建的對象執行 insert 操作,樓主的數據庫對應的表中並沒有 id 為 100 customer,15 行同樣返回了一個新的對象,根據返回結果可知 ,確實插入的是新的對象
1 //2.若傳入的是一個游離對象,即傳入的對象有 OID 2 //若在 EntityManager 緩存中沒有該對象,在數據庫中也沒有對應的記錄,JPA 會創建一個新的對象, 3 //把當前游離對象的屬性復制到新的對象中,對新創建的對象執行 insert 操作 4 @Test 5 public void testMerge2() { 6 Customer customer = new Customer(); 7 customer.setAge(23); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setEmail("dd@126.com"); 11 customer.setLastName("DD"); 12 13 customer.setId(100); 14 15 Customer customer2 = entityManager.merge(customer); 16 17 System.out.println("customer's id:" + customer.getId());// 100 18 System.out.println("customer's id:" + customer2.getId());// 3 19 }
③ 傳入的是游離對象,即傳入的對象有 OID,緩存中沒有,但數據庫中有對應的對象:JPA 會查詢對應的記錄,然后返回該記錄對應的對象把當前游離對象的屬性復制到查詢到的對象中,對查詢到的對象執行 update 操作
1 //3.若傳入的是一個游離對象,即傳入的對象有 OID 2 //若在 EntityManager 緩存中沒有該對象,在數據庫中有對應的記錄,JPA 會查詢對應的記錄,然后返回該記錄對應的對象 3 //把當前游離對象的屬性復制到查詢到的對象中,對查詢到的對象執行 update 操作 4 @Test 5 public void testMerge3() { 6 Customer customer = new Customer(); 7 customer.setAge(23); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setEmail("ff@126.com"); 11 customer.setLastName("FF"); 12 13 customer.setId(3); 14 15 Customer customer2 = entityManager.merge(customer); 16 17 System.out.println(customer == customer2); //false 18 }
④ 傳入的是游離對象,即傳入的對象有 OID,EntityManager 緩存中有對應的對象:JPA 會把當前游離對象的屬性復制到查詢到的 EntityManager 緩存中的對象,對 EntityManager 緩存中的對象執行 update 操作
1 //4.若傳入的是一個游離對象,即傳入的對象有 OID 2 //若在 EntityManager 緩存中有對應的對象,JPA 會把當前游離對象的屬性復制到查詢到的 EntityManager 緩存中的對象, 3 //對 EntityManager 緩存中的對象執行 update 操作 4 @Test 5 public void testMerge4() { 6 Customer customer = new Customer(); 7 customer.setAge(23); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setEmail("dd@126.com"); 11 customer.setLastName("DD"); 12 13 customer.setId(3); 14 Customer customer2 = entityManager.find(Customer.class, 3); 15 16 entityManager.merge(customer); 17 18 System.out.println(customer == customer2); //false 19 }
4.EntityTransaction:JPA 中的事務操作
常用 API: begin() commit() rollback() 代碼不再演示
五、JPA 中映射關聯關系
1. 映射單向多對一的關聯關系:Order : Customer n:1 ,Order 中有 Customer 屬性,而 Customer 中沒有 Order 屬性(單向多對一區別於單向一對多)
1)創建 Order 實體類,標注注解,生成數據表,使用 @ManyToOne 映射多對一的關聯關系,使用 @JoinColumn 來標注外鍵
1 package com.software.jpa.helloworld; 2 3 import javax.persistence.Column; 4 import javax.persistence.Entity; 5 import javax.persistence.FetchType; 6 import javax.persistence.GeneratedValue; 7 import javax.persistence.Id; 8 import javax.persistence.JoinColumn; 9 import javax.persistence.ManyToOne; 10 import javax.persistence.Table; 11 12 @Table(name="JPA_ORDERS") 13 @Entity 14 public class Order { 15 16 private Integer id; 17 18 private String orderName; 19 20 @GeneratedValue 21 @Id 22 public Integer getId() { 23 return id; 24 } 25 26 public void setId(Integer id) { 27 this.id = id; 28 } 29 30 @Column(name="ORDER_NAME") 31 public String getOrderName() { 32 return orderName; 33 } 34 35 public void setOrderName(String orderName) { 36 this.orderName = orderName; 37 } 38 39 private Customer customer; 40 41 /** 42 * 映射單項 n-1 的關聯關系(Customer 和 Order,Order 中有 Customer 屬性,而 Customer 中沒有 Order 屬性) 43 * 使用 @ManyToOne 來映射多對一的關聯關系 44 * 使用 @JoinColumn 來映射外鍵 45 * 可以使用 @ManyToOne 的 fetch 屬性來修改默認的關聯屬性的加載策略 46 */ 47 @JoinColumn(name="CUSTOMER_ID") 48 @ManyToOne(fetch=FetchType.LAZY) 49 public Customer getCustomer() { 50 return customer; 51 } 52 53 public void setCustomer(Customer customer) { 54 this.customer = customer; 55 } 56 57 }
2)單向多對一的保存(persist):保存多對一時,建議先保存 1 的一端,后保存 n 的一端,這樣不會多出額外的 UPDATE 語句
3)獲取操作(find):默認情況下使用左外連接的方式來獲取 n 的一端的對象和其關聯的 1 的一端的對象,可以使用 @ManyToOne 的 fetch 屬性來修改默認的關聯屬性的加載策略
4)刪除操作(remove):不能直接刪除 1 的一端,因為有外鍵約束
5)修改操作:
2.映射單向 1-n 的關聯關系 Customer :Order 1 : n,Customer 中有 Order 的 Set 集合屬性,Order 中沒有 Customer的屬性
1)在 Customer 中添加 Order 的 Set 集合屬性,並映射 1-n 關聯關系,重新生成數據表
2)保存操作(persist):總會多出 UPDATE 語句,n 的一端在插入時不會同時插入外鍵列
3)查詢操作(find):默認使用懶加載
4)刪除操作(remove):默認情況下,若刪除 1 的一端,會先把關聯的 n 的一端的外鍵置空,然后再進行刪除,可以通過 @OneToMany 的 cascade 屬性修改默認的刪除策略(CascadeType.REMOVE 為級聯刪除)
3.映射雙向多對一的關聯關系(注:雙向多對一 同 雙向一對多)
1)實體:Customer 中有 Order 的 Set 集合屬性,Order 中有 Customer 的屬性,注兩個實體映射的外鍵列必須一致,都為 CUSTOMER_ID
2)保存操作(persist):
4.映射雙向一對一的關聯關系
1)實體:Manager 和 Department ,一個部門有一個經理,一個經理管一個部門
2)創建 Manager 類和 Department 類,Manager 類中有 Department 的引用,Department 中有 Manager 的引用,由 Department 來維護關聯關系(實際上雙向 1- 1 雙方均可以維護關聯關系),使用 @OneToOne 來映射 1-1 關聯關系。添加必要注解,生成數據表。
3)保存操作:
4)查詢操作:
5.映射雙向多對多的關聯關系
1)實體:Item 和 Category ,一個類別有多個商品,一個商品對應多個類別。雙方都包含對方的 Set 集合。創建實體類,添加對應的注解,生成數據表。
2)保存操作:
3)查詢操作:
----------------------------------------------------------------------------------------------------------------
相關鏈接:
JPA + SpringData 操作數據庫原來可以這么簡單 ---- 深入了解 JPA - 1
JPA + SpringData 操作數據庫原來可以這么簡單 ---- 深入了解 JPA - 3