1. 程式人生 > >jpa學習筆記

jpa學習筆記

[email protected]
//工具方法. 不需要對映為資料表的一列. 
@Transient
public String getInfo(){
return "lastName: " + lastName + ", email: " + email;
}




[email protected]
//設定資料庫列的型別精確度
@Temporal(TemporalType.TIMESTAMP)
public Date getCreatedTime() {
return createdTime;
}


@Temporal(TemporalType.DATE)
public Date getBirth() {
return birth;
}




-------------find/getReference----------------------
find相當於hibernate的get,getReference相當於hibernate的load
區別:getReference只有使用查詢出來的物件才會傳送SQL,而get執行查詢就立即傳送SQL查詢


--------------persist----------
persist儲存,相當於hibernate的save
和 hibernate 的 save 方法的不同之處: 若物件有 id, 則不能執行 insert 操作, 而會丟擲異常. 


--------------remove--------------
刪除,類似於 hibernate的delete 方法
但注意: 該方法只能移除 持久化 物件. 而 hibernate 的 delete 方法實際上還可以移除 遊離物件.
//Customer customer = new Customer();刪除這個物件會出錯,只能使用下面哪種方式,先查詢再刪除
//customer.setId(2);

Customer customer = entityManager.find(Customer.class, 2);
entityManager.remove(customer);


----------------merge------------------
總的來說: 類似於 hibernate Session 的 saveOrUpdate 方法.
//1. 若傳入的是一個臨時物件
//會建立一個新的物件, 把臨時物件的屬性複製到新的物件中, 然後對新的物件執行持久化操作. 所以
//新的物件中有 id, 但以前的臨時物件中沒有 id. 


//2.若傳入的是一個遊離物件, 即傳入的物件有 OID. 
//1. 若在 EntityManager 快取中沒有該物件
//2. 若在資料庫中也沒有對應的記錄
//3. JPA 會建立一個新的物件, 然後把當前遊離物件的屬性複製到新建立的物件中
//4. 對新建立的物件執行 insert 操作. 


//3.若傳入的是一個遊離物件, 即傳入的物件有 OID. 
//1. 若在 EntityManager 快取中沒有該物件
//2. 若在資料庫中也有對應的記錄
//3. JPA 會查詢對應的記錄, 然後返回該記錄對一個的物件, 再然後會把遊離物件的屬性複製到查詢到的物件中.
//4. 對查詢到的物件執行 update 操作. 


//4.若傳入的是一個遊離物件, 即傳入的物件有 OID. 
//1. 若在 EntityManager 快取中有對應的物件
//2. JPA 會把遊離物件的屬性複製到查詢到EntityManager 快取中的物件中.
//3. EntityManager 快取中的物件執行 UPDATE. 


------------------flush---------------
同 hibernate 中 Session 的 flush 方法. 
作用:傳送SQL語句,但是沒有提交事務之前,資料庫還看不到資料
如:
Customer customer = entityManager.find(Customer.class, 1);
System.out.println(customer);

customer.setLastName("AA");//在這句話這裡更改了lastname

entityManager.flush();//沒有提交之前可以使用flush方法傳送sql


--------------------refresh-----------------
重新獲取一個物件資訊,看是不是最新的,把新的資料重新獲取




==================級聯操作==============
-------------單向多對一---------
儲存多對一時, 建議先儲存 1 的一端, 後儲存 n 的一端, 這樣不會多出額外的 UPDATE 語句.


//對映單向 n-1 的關聯關係
//使用 @ManyToOne 來對映多對一的關聯關係
//使用 @JoinColumn 來對映外來鍵. 
//可使用 @ManyToOne 的 fetch 屬性來修改預設的關聯屬性的載入策略
@JoinColumn(name="CUSTOMER_ID")
@ManyToOne(fetch=FetchType.LAZY)




------------------單向、雙向 一對多-----------------------
//對映單向 1-n 的關聯關係
//使用 @OneToMany 來對映 1-n 的關聯關係
//使用 @JoinColumn 來對映外來鍵列的名稱
//可以使用 @OneToMany 的 fetch 屬性來修改預設的載入策略
//可以通過 @OneToMany 的 cascade 屬性來修改預設的刪除策略. 
//注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 屬性, 則 @OneToMany 端就不能再使用 @JoinColumn 屬性了. 
//@JoinColumn(name="CUSTOMER_ID")
@OneToMany(fetch=FetchType.LAZY,cascade={CascadeType.REMOVE},mappedBy="customer")
public Set<Order> getOrders() {
return orders;
}


//若是雙向 1-n 的關聯關係, 執行儲存時
//若先儲存 n 的一端, 再儲存 1 的一端, 預設情況下, 會多出 n 條 UPDATE 語句.
//若先儲存 1 的一端, 則會多出 n 條 UPDATE 語句
//在進行雙向 1-n 關聯關係時, 建議使用 n 的一方來維護關聯關係, 而 1 的一方不維護關聯絡, 這樣會有效的減少 SQL 語句. 
//注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 屬性, 則 @OneToMany 端就不能再使用 @JoinColumn 屬性了. 

//單向 1-n 關聯關係執行儲存時, 一定會多出 UPDATE 語句.
//因為 n 的一端在插入時不會同時插入外來鍵列. 


//預設情況下, 若刪除 1 的一端, 則會先把關聯的 n 的一端的外來鍵置空, 然後進行刪除. 
//可以通過 @OneToMany 的 cascade 屬性來修改預設的刪除策略. 




--------------------雙向一對一-------------------
//使用 @OneToOne 來對映 1-1 關聯關係。
//若需要在當前資料表中新增主鍵則需要使用 @JoinColumn 來進行對映. 注意, 1-1 關聯關係, 所以需要新增 unique=true
@JoinColumn(name="MGR_ID", unique=true)
@OneToOne(fetch=FetchType.LAZY)


//對於不維護關聯關係, 沒有外來鍵的一方, 使用 @OneToOne 來進行對映, 建議設定 mappedBy=true
@OneToOne(mappedBy="mgr")
public Department getDept() {


//雙向 1-1 的關聯關係, 建議先儲存不維護關聯關係的一方, 即沒有外來鍵的一方, 這樣不會多出 UPDATE 語句.
//1.預設情況下, 若獲取維護關聯關係的一方, 則會通過左外連接獲取其關聯的物件. 
//但可以通過 @OntToOne 的 fetch 屬性來修改載入策略.
//1. 預設情況下, 若獲取不維護關聯關係的一方, 則也會通過左外連接獲取其關聯的物件. 
//可以通過 @OneToOne 的 fetch 屬性來修改載入策略. 但依然會再發送 SQL 語句來初始化其關聯的物件
//這說明在不維護關聯關係的一方, 不建議修改 fetch 屬性. 


-------------------多對多----------------
//使用 @ManyToMany 註解來對映多對多關聯關係
//使用 @JoinTable 來對映中間表
//1. name 指向中間表的名字
//2. joinColumns 對映當前類所在的表在中間表中的外來鍵
//2.1 name 指定外來鍵列的列名
//2.2 referencedColumnName 指定外來鍵列關聯當前表的哪一列
//3. inverseJoinColumns 對映關聯的類所在中間表的外來鍵
@JoinTable(name="ITEM_CATEGORY",
joinColumns={@JoinColumn(name="ITEM_ID", referencedColumnName="ID")}, //referencedColumnName也可以不寫
inverseJoinColumns={@JoinColumn(name="CATEGORY_ID", referencedColumnName="ID")})
@ManyToMany
public Set<Category> getCategories() {
return categories;
}


@ManyToMany(mappedBy="categories")
public Set<Item> getItems() {


===================快取================
Customer customer1 = entityManager.find(Customer.class, 1);
Customer customer2 = entityManager.find(Customer.class, 1);
這樣查詢兩個相同資料,會自動使用一級快取,會看到只會傳送一條SQL


如果需要使用二級快取,需要在persistence.xml中加如下配置:
<!-- 
配置二級快取的策略 
ALL:所有的實體類都被快取
NONE:所有的實體類都不被快取. 
ENABLE_SELECTIVE:標識 @Cacheable(true) 註解的實體類將被快取
DISABLE_SELECTIVE:快取除標識 @Cacheable(false) 以外的所有實體類
UNSPECIFIED:預設值,JPA 產品預設值將被使用
-->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>


<!-- 二級快取相關 -->
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<property name="hibernate.cache.use_query_cache" value="true"/><!--查詢快取-->
並新增快取jar包和ehcache.xml
在實體類上新增@Cacheable(true)


=====================查詢====================
---------------jpql--------------
String jpql = "FROM Customer c WHERE c.age > ?";
Query query = entityManager.createQuery(jpql);

//佔位符的索引是從 1 開始
query.setParameter(1, 1);
List<Customer> customers = query.getResultList();
System.out.println(customers.size());
-----------------
如果只想查詢部分列,那麼集合將會存放陣列
如果查詢部分列,不想集合存放陣列,那麼需要給實體提供一個對應的建構函式,並如下查詢:
String jpql = "SELECT new Customer(c.lastName, c.age) FROM Customer c WHERE c.id > ?";
List result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList();

System.out.println(result);
------------
也可以通過createNamedQuery查詢實體類中已經寫好的jpql
@NamedQuery(name="testNamedQuery", query="FROM Customer c WHERE c.id = ?")//這裡eclipse可能會顯示錯誤,但是不影響執行,也可以寫成select c FROM Customer c WHERE c.id = ?
@Table(name="JPA_CUTOMERS")
@Entity
public class Customer {


Query query = entityManager.createNamedQuery("testNamedQuery").setParameter(1, 3);
Customer customer = (Customer) query.getSingleResult();

System.out.println(customer);
--------------------
也可以使用sql語句查詢:
String sql = "SELECT age FROM jpa_cutomers WHERE id = ?";
Query query = entityManager.createNativeQuery(sql).setParameter(1, 3);

Object result = query.getSingleResult();
System.out.println(result);


-------------------使用 hibernate 的查詢快取--------------
在每次查詢後面跟上.setHint(QueryHints.HINT_CACHEABLE, true);如:
第一次查詢:Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);
第二次查詢:query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);


-----------------------
查詢時也可以使用OrderBy、GroupBy、關聯查詢、子查詢,如:
OrderBy:
String jpql = "FROM Customer c WHERE c.age > ? ORDER BY c.age DESC";
GroupBy:
String jpql = "SELECT o.customer FROM Order o "
+ "GROUP BY o.customer "
+ "HAVING count(o.id) >= 2";
關聯查詢:
String jpql = "FROM Customer c LEFT OUTER JOIN FETCH c.orders WHERE c.id = ?";
子查詢:
String jpql = "SELECT o FROM Order o "
+ "WHERE o.customer = (SELECT c FROM Customer c WHERE c.lastName = ?)";
使用 jpql 自帶的函式
String jpql = "SELECT lower(c.email) FROM Customer c";


------------
可以使用 JPQL 完成 UPDATE 和 DELETE 操作
String jpql = "UPDATE Customer c SET c.lastName = ? WHERE c.id = ?";