原文發佈於:http://www.gufeng.tech/ 穀風的個人主頁
JPA全稱Java Persistence API即Java持久化API,是一種通過註解或者XML配置描述映射關係來實現將實體持久化到數據庫的一種java持久化實現方案。JPA和JDBC一樣,都是jdk提供的API,各廠商提供實現,目前的實現有Apache的OpenJPA以及應用廣泛的Hibernate,當然還有其它的實現,這裏就不一一列出了。JAP作爲一種標準,已經獲得了JavaEES容器的支持,同時也仍然可以在JavaSE環境中使用,這就奠定了它可被廣泛應用的前提。
JPA在隨着SpringBoot的默認支持,再次被廣泛提及、應用。作爲一種ORM的技術,除了擁有ORM的特點之外,還有一些它自己的特點:
1 標準化
JPA是java標準化組織JCP提出的一項持久化標準,所以其任何實現都提供了相同的API,即便更換了實現方案,兼容性也是非常好的。
2 更面向對象
JPA支持面向對象中的繼承、多態及多個類間的關聯關係,因此使得程序員在使用JPA開發時可以方便的使用面向對象的思維,過渡性比較好。
3 功能全面
JPA支持事務、併發等特性,而不僅僅是一個簡單的持久化框架,因而可以在實際使用中發揮更大的作用。
4 卓越的查詢能力
JPA是面向對象的,定義了JPQL(Java Persistence Query Language),JPQL是一種針對實體的查詢語句,操作對象是實體,可以以面向對象的方式構造查詢語句,在這點上類似Hibernate的HQL。JPQL能夠支持批量操作(如更新和修改)、join、group by、having等子句,同時還支持子查詢,因此其查詢能力可見一斑。
5 集成方便
這一點從SpringBoot選擇了JPA就能夠看出來。在JPA框架下創建實體只需要使用javax.persistence.Entity註解即可,其接口使用也非常簡單容易上手,尤其重要的一點是JPA被設計成非***式的,天然註定了它能夠被非常容易的集成。
JPA涉及的技術
1 元數據
關於映射關係,JPA同時支持註解和XML。
2 API
操作簡單,將程序員從繁瑣的SQL中解放出來。
3 查詢語言
通過面向對象的方式進行數據查詢,避免程序與SQL語句的的耦合。
上面提及了JPA的各種好處,那麼作爲一種技術方案(或者叫規範),JPA有沒有缺點呢?答案是肯定的,JPA將數據庫關係以實體及實體間關係表示,必然存在一種將數據庫的複雜概念(如一對一、一對多、多對多等關係)轉移到程序中。這會帶來以下一些問題:
1 程序可讀性差,如果遇到特別複雜的業務邏輯,那麼對於實體的定義也會非常困難,這需要設計者具有非常高的抽象、規範、設計能力。
2 數據關係發生變化時,實體必然也會發生變化。
3 將關係型數據庫映射到面向對象程序上,本身就有着深度的複雜性,兩者關注點不同,當關聯到一起的時候必然會來帶衝突。數據庫關心的是數據以及數據的完整性;面向對象關心的是對象的成員及行爲。
接下來介紹下JPA的一些核心類:
1 EntityManagerFactory
EntityManagerFactory 是 EntityManager 的工廠類,負責創建 EntityManager 對象。
2 EntityManager
EntityManager 是 JPA 應用中使用的基本對象,通過它提供的相應方法可以管理持久化對象,也可以新建或者刪除持久化對象。EntityManager 還負責創建 Query 實例。在容器外使用時,EntityManagerFactory 和 EntityManager 之間是一對一的關係。
3 EntityTransaction
EntityTransaction 提供 Entity 操作時需要的事務管理,和 EntityManager 是一對一的關係。在查詢操作時不需要使用 EntityTransaction,而在對象持久化、狀態更新、對象刪除等情況下則必須使用顯式的使用 EntityTransaction 的相關方法管理事務。
4 Query
Query 是查詢實體的接口,Query 對象可以從 EntityManager 中獲得。根據 EJB 3.0 規範中的描述,Query 接口需要同時支持 JPQL 和原生態 SQL 兩種語法。
5 Persistence
Persistence 是一個工具類,負責根據配置文件提供的參數創建 EntityManagerFactory 對象。
最後看一段JPA的示例代碼:
import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "user") public class User { @Id @Column(name = "id") private String id; @Column(name = "name") private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import java.util.List; public class Main { static EntityManagerFactory emf = Persistence.createEntityManagerFactory("gufengJPA"); static EntityManager em = emf.createEntityManager(); public static void main(String[] args) { User person = new User(); person.setId(123456l); person.setName("張無忌"); em.persist(person); add(person); System.out.println("user id:" + person.getId()); User user = find(person.getId()); System.out.println(user); List<User> all = findAll(); for (User u : all) { System.out.println(u); } em.close(); emf.close(); } public static void add(User user) { em.getTransaction().begin(); em.persist(user); em.getTransaction().commit(); } public static User find(Object id) { User user = em.find(User.class, id); return user; } public static List<User> findAll() { List<User> users = em.createQuery("select u from User u") .getResultList(); return users; } }
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <!-- 持久化單元 --> <persistence-unit name="gufengJPA" transaction-type="RESOURCE_LOCAL"> <!--可以指定,如果不指定則從META-INF/services加載--> <!--<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>--> <!-- <class>com.micmiu.hibernate.jpa.UserInfo</class> --> <!--<provider>com.tongweb.openjpa.persistence.PersistenceProviderImpl</provider>--> <!--<jta-data-source>MyDataSource</jta-data-source>--> <!-- class 定義指定持久化的實體類 注意配置屬性hibernate.archive.autodetection=false --> <!--<class>jpa.User</class>--> <properties> <property name="hibernate.archive.autodetection" value="class,hbm"/> <!--Hibernate 方言 --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/> <!--數據庫url --> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test"/> <!--數據庫驅動 --> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <!--配置數據庫用戶名 --> <property name="hibernate.connection.username" value="root"/> <!--配置數據庫密碼 --> <property name="hibernate.connection.password" value="123456"/> <!--設置外連接抓取樹的最大深度 --> <!--<property name="hibernate.max_fetch_depth" value="3"/>--> <!--自動輸出schema創建DDL語句 --> <!--<property name="hibernate.hbm2ddl.auto" value="update"/>--> <property name="hibernate.show_sql" value="true"/> </properties> </persistence-unit> </persistence>
這裏需要注意的是JPA配置文件persistence.xml 必需放到類路徑的根路徑的META-INF文件夾下。
Persistence.createEntityManagerFactory("gufengJPA");
上面這行代碼是通過Java的SPI機制獲得具體實現的。在hibernate的jar包裏有個META-INF/services/javax.persistence.spi.PersistenceProvider 文件,這裏指定的實現爲 org.hibernate.jpa.HibernatePersistenceProvider。再有多個的時候,Persistence只會使用第一個。