一、JPA簡介
JPA全稱為Java Persistence API。JPA提供了一種簡單高效的方式來管理Java對象(POJO)到關系型數據庫的映射,此類Java對象成為JPA實體或簡稱實體。JPA和Hibernate之間的關系,可以簡單的理解為JPA是標準接口,Hibernate是實現。從功能上來說,JPA現在就是Hibernate功能的一個子集。Hibernate 從3.2開始,就開始兼容JPA。Hibernate3.2獲得了Sun TCK的 JPA(Java Persistence API) 兼容認證。
??那么Hibernate是如何實現與JPA的這種關系的呢?Hibernate主要是通過三個組件來實現的,及hibernate-annotation、hibernate-entitymanager和hibernate-core。
- hibernate-annotation是Hibernate支持annotation方式配置的基礎,它包括了標準的JPA annotation以及Hibernate自身特殊功能的annotation。
- hibernate-core是Hibernate的核心實現,提供了Hibernate所有的核心功能。
- hibernate-entitymanager實現了標準的JPA,可以把它看成hibernate-core和JPA之間的適配器,它并不直接提供ORM的功能,而是對hibernate-core進行封裝,使得Hibernate符合JPA的規范。
總的來說,JPA是規范,Hibernate是框架,JPA是持久化規范,而Hibernate實現了JPA。
二、JPA的實體狀態
實體對象的狀態,在Hibernate有瞬時、持久、離線三種,而JPA里有new,managed,detached,removed,兩者的這些狀態都是一一對應的。
三、JPA配置
我們看看基于Hibernate提供的一個比較完整的JPA2.1的配置文件。
pom.xml:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.1.0.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.1.0.Final</version>
<scope>compile</scope>
</dependency>
<!--Hibernate整合C3P0實現連接池-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>5.1.0.Final</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.1.0.Final</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
persistence.xml:
<!--JPA2.1規范要求每一個持久化單元必須有一個名字,不能為空。即persistence-unit name="main"的name不能為空-->
<persistence-unit name="main" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!-- 指定連接數據庫所用的驅動 -->
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver"></property>
<!-- 指定連接數據庫的url,hibernate連接的數據庫名 -->
<property name="hibernate.connection.url" value="jdbc:postgresql://128.27.10:8081/test"></property>
<property name="hibernate.onnection.characterEncoding" value="UTF-8"></property>
<!-- 指定連接數據庫的用戶名 -->
<property name="hibernate.connection.username" value="root"></property>
<!-- 指定連接數據庫的密碼 -->
<property name="hibernate.connection.password" value="123"></property>
<!-- Connection Pool Provider -->
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"></property>
<!-- 指定連接池里最大連接數 -->
<property name="hibernate.c3p0.max_size" value="256"></property>
<!-- 指定連接池里最小連接數 -->
<property name="hibernate.c3p0.min_size" value="4"></property>
<!-- 指定連接池里連接的超時時長 -->
<property name="hibernate.c3p0.timeout" value="4096"></property>
<!-- 指定連接池里最大緩存多少個Statement對象 -->
<property name="hibernate.c3p0.max_statements" value="100"></property>
<property name="hibernate.c3p0.idle_test_period" value="120"></property>
<property name="hibernate.c3p0.acquire_increment" value="2"></property>
<property name="hibernate.c3p0.validate" value="true"></property>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"></property>
<property name="hibernate.cache.use_second_level_cache" value="true"></property>
<property name="hibernate.cache.use_query_cache" value="true"></property>
<!-- 指定數據庫方言 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL9Dialect"></property>
<!-- 根據需要自動創建數據表 -->
<!--<property name="hibernate.hbm2ddl.auto" value="update"></property>-->
<!-- 顯示Hibernate持久化操作所生成的SQL -->
<property name="hibernate.show_sql" value="true"></property>
<!-- 將SQL腳本進行格式化后再輸出 -->
<property name="hibernate.format_sql" value="true"></property>
<property name="hibernate.current_session_context_class" value="managed"></property>
<property name="javax.persistence.validation.mode" value="none"></property>
</properties>
</persistence-unit>
四、JPA的基本使用
public class JPABase(){
//實體管理器
protected ThreadLocal<EntityManager> entityManager;
public JPABase(ThreadLocal<EntityManager> tlemImpl) {
this.entityManager= tlemImpl;
}
//得到當下的entityManager
public EntityManager currentEM() {
return (EntityManager)this.entityManager.get();
}
//新建一個entityManager
public EntityManager newEM() {
return ((EntityManager)this.entityManager.get()).getEntityManagerFactory().createEntityManager();
}
//啟動事務
public void begin() throws IllegalStateException {
((EntityManager)this.entityManager.get()).getTransaction().begin();
}
//更新實體。當實體正在被容器管理時,你可以調用實體的set 方法對數據進行修改,在容器決定flush 時,更新的數據才會同步到數據庫。
public void flush() throws TransactionRequiredException, PersistenceException {
((EntityManager)this.entityManager.get()).flush();
}
//提交事務
public void commit() throws IllegalStateException, RollbackException {
((EntityManager)this.entityManager.get()).getTransaction().commit();
}
//撤消(回滾)當前事務。即撤消事務啟動后的所有數據庫更新操作,從而不對數據庫產生影響
public void rollback() throws IllegalStateException, PersistenceException {
((EntityManager)this.entityManager.get()).getTransaction().rollback();
}
//使當前事務只能被撤消
public void fail() throws IllegalStateException {
((EntityManager)this.entityManager.get()).getTransaction().setRollbackOnly();
}
//關閉事務
public void close() throws IllegalStateException {
((EntityManager)this.entityManager.get()).close();
this.entityManager.remove();
}
//給對象加鎖
public void lock(Object o, LockModeType lock) throws IllegalStateException {
((EntityManager)this.entityManager.get()).lock(o, lock);
}
//添加實體,相當于add。通常persist之后會調用flush方法將實例插入數據庫中
public void persist(Object o){
((EntityManager)this.entityManager.get()).persist(o);
}
//addOrUpdate
public User merge(Object o){
return ((EntityManager)this.entityManager.get()).merge(o);
}
//刪除對象
public void delete(Object o){
((EntityManager)this.entityManager.get()).remove(o);
}
//返回Query對象,以執行JPQL語句
public Query query(String jpql) {
return ((EntityManager)this.entityManager.get()).createQuery(jpql);
}
//返回Query對象,以執行SQL語句
public Query createNativeQuery(String sql) {
return ((EntityManager)this.entityManager.get()).createNativeQuery(sql);
}
}
五、JPA的高級查詢
首先讓我們來了解一下以下類:
CriteriaQuery:查詢構造器(查詢語句拼裝器)。組合要查詢的內容和查詢的條件。
CriteriaBuiller:CriteriaQuery工廠。
Root:查詢根對象,定義查詢的From子句中能出現的類型。CriteriaQuery.select(root)就可以拼裝出select * from User 這一串。
Predicate:構造條件器。Predicate[] 可以支持多個過濾條件。
構造高級查詢上下文:
public class CriteriaQueryContext<T> {
public Class<T> e;
public CriteriaQuery<T> c;
public CriteriaBuilder b;
public Root<T> r;
...
}
public <X> CriteriaQueryContext<X> criteria(Class<X> class) {
CriteriaQueryContext ctx = new CriteriaQueryContext();
ctx.e = class;
ctx.b = ((EntityManager)this.entityManager.get()).getCriteriaBuilder();
ctx.c = ctx.b.createQuery(class);
ctx.r = ctx.c.from(class);
return ctx;
}
查詢條件的使用
??在criteria 查詢中,查詢條件通過Predicate 或Expression 實例應用到CriteriaQuery 對象上,這些條件使用 CriteriaQuery .where 方法應用到CriteriaQuery對象上。
??CriteriaBuilder 也是作為Predicate 實例的工廠,Predicate對象通過調用CriteriaBuilder 的條件方法( equal,notEqual, gt, ge,lt, le,between,like等)創建。 Predicate實例也可以用Expression實例的isNull, isNotNull 和in方法獲得,復合的Predicate 語句可以使用CriteriaBuilder的and, or等方法構建。
下面的代碼片段展示了Predicate 實例檢查年齡大于24歲的員工實例:
public User getUserById(String userId)
{
CriteriaQueryContext<User> c = this.criteria(User.class);
Predicate condition = c.b.equal(c.r.<String>get("userid"), userId);
c.c.where(condition);
return(
this.entityManager.createQuery(c.c).getSingleResult();
);
}
Expression 用在查詢語句的select,where和having子句中,該接口有 isNull, isNotNull 和 in方法。
CriteriaQueryContext<User> c = this.criteria(User.class);
c.c.where(c.r.get(User.age).in(20, 24));
Criteria Query也允許開發者編寫復合謂詞,通過該查詢可以為多條件測試下面的查詢檢查兩個條件。
CriteriaQueryContext<User> c = this.criteria(User.class);
c.c.where(
criteriaBuilder.and(
criteriaBuilder.like(c.r.get(User.name), "M%"),
criteriaBuilder.equal(c.r.get(User.age), 25)
));
我們也可以使用Predicate[]來添加多個過濾條件,Predicate[]使用模式為:
List<Predicate> predicatesList = new ArrayList<Predicate>();
predicatesList.add(.....Pridicate....)
criteriaQuery.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
Predicate[]的demo:
List<Predicate> predicatesList = new ArrayList<Predicate>();
Predicate p1 = c.b.in(c.r.get("id")).value(id);
predicatesList.add(p1);
Predicate p2 = c.b.in(c.r.get("name")).value(name);
predicatesList.add(p2);
c.c.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
c.c.orderBy(c.b.asc(c.r.get(age)));