概述
下面是 JDBC 在 Java 應用和數據庫之間的位置,充當着一個中間者,供 Java 應用程序訪問所有類別的數據庫,建立一個標准
JPA 如同 JDBC 一樣,為 Java 應用程序使用 ORM 框架建立一個標准
- JPA 和 Hibernate 的關系
- JPA 是規范:JPA 本質上是一種 ORM 規范,不是 ORM 框架,只是定制了一些規范,提供了一些編程的 API 接口,具體實現由 ORM 廠商實現
- Hibernate 是實現:Hibernate 除了是一種 ORM 框架之外,他也是一種 JPA 實現
HelloWorld
-
步驟
- 創建 presitence.xml,在這個文件中配置持久化單元
- 指定跟哪個數據庫進行交互
- 指定使用哪個持久化框架以及配置該框架的基本屬性
- 創建實體類,使用 annotation 來描述實體類跟數據庫表之間的映射關系
- 使用 JPA API 完成數據的增、刪、改、查操作
- 創建 EntityManagerFactory(對應於 Hibernate 中的 SessionFactory)
- 創建 EntityManager(對應 Hibernate 中的 Session)
- 創建 presitence.xml,在這個文件中配置持久化單元
-
導入 jar 包
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</version> </dependency>
-
persistence.xml
-
JPA 規范要求在類路徑的 META-INF 目錄下防止 persistencce.xml,文件的名稱是固定的
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL"> <!--配置使用什么 ORM 產品--> <!--若 JPA 項目中只有一個 JPA 產品的實現,則可以不配置該節點--> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <class>com.jpa.first.both.many2many.Item</class> <properties> <!--配置數據庫連接--> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/jpa"/> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.username" value="root"/> <property name="hibernate.connection.password" value="zy961029"/> <!--配置 hibernate 的基本屬性--> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> </properties> </persistence-unit> </persistence>
-
注解
-
@Entity
- @Entity 標注用於實體類聲明語句之前,指出該Java 類為實體類,將映射到指定的數據庫表。
-
@Table
- 當實體類與其映射的數據庫表名不同名時需要使用 @Table 標注說明,該標注與 @Entity 標注並列使用
-
@id
- @Id 標注用於聲明一個實體類的屬性映射為數據庫的主鍵列
- @Id標注也可置於屬性的getter方法之前
-
@GeneratedValue
- @GeneratedValue 用於標注主鍵的生成策略,通過 strategy 屬性指定。默認情況下,JPA 自動選擇一個最適合底層數據庫的主鍵生成策略:SqlServer 對應 identity,MySQL 對應 auto increment
- IDENTITY:采用數據庫 ID自增長的方式來自增主鍵字段,Oracle 不支持這種方式
- AUTO: JPA自動選擇合適的策略,是默認選項
- TABLE:通過表產生主鍵,框架借由表模擬序列產生主鍵,使用該策略可以使應用更易於數據庫移植。
- SEQUENCE:通過序列產生主鍵,通過 @SequenceGenerator 注解指定序列名,MySql 不支持這種方式
- @GeneratedValue 用於標注主鍵的生成策略,通過 strategy 屬性指定。默認情況下,JPA 自動選擇一個最適合底層數據庫的主鍵生成策略:SqlServer 對應 identity,MySQL 對應 auto increment
-
@Basic
- 表示一個簡單的屬性到數據表的字段的映射,對於沒有任何標注的 getXxx() 方法,默認為 @Basic
- fetch 表示屬性的讀取策略,有 EAGER 和 LAZY 兩種,分別為主支抓取和延遲加載
- optional 表示該屬性是否允許為 null,默認為 true
- 表示一個簡單的屬性到數據表的字段的映射,對於沒有任何標注的 getXxx() 方法,默認為 @Basic
-
@Column
- 當實體的屬性與其映射的數據庫表的列不同名時需要使用 @Column 標注說明,還有屬性 unique、nullable、length 等
-
@Transient
- 表示該屬性並非一個到數據庫表的字段的映射,ORM 框架將忽略該屬性
- 如果一個屬性並非數據庫表的字段映射,就務必將其標識為 @Transient,否則ORM 框架默認為其注解 @Basic,例如工具方法不需要映射
-
@Temporal
- 在 JavaAPI 中沒有定義 Date 類型的精度,而在數據庫中表示 Date 類型的數據類型有 Date,Time,TimeStamp 三種精度(日期,時間,兩者兼具),進行屬性映射的時候可以使用 @Temporal 注解調整精度
JPA API
-
EntityManagerFactory
- EntityManagerFactory 用來創建 EntityManager 實例
- 使用 Persistence 類獲取 EntityManagerFactory 實例,該類包含一個名為 createEntityManagerFactory 的靜態方法
- createEntityManager 有兩個重載方法,如下:
- 第二個重載方法和上述的方法唯一不同的是不需要傳入第二個參數
- isOpen(),檢查 EntityManagerFactory 是否處於打開狀態
- close(),關閉 EntityManagerFactory,EntityManagerFactory 關閉后將釋放所有資源,isOpen() 方法將返回 false
-
EntityManager
-
-
代碼
public void testJpa() { // 創建 EntityManagerFactory String persistenceUnitName = "persistenceUnit"; EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName); // 創建 EntityManager EntityManager entityManager = entityManagerFactory.createEntityManager(); // 獲取事務對象並開啟事務 EntityTransaction entityTransaction = entityManager.getTransaction(); entityTransaction.begin(); // 進行持久化操作 entityManager.xxx(); // 提交事務 entityTransaction.commit(); // 關閉 EntityManager entityManager.close(); // 關閉 EntityManagerFactory entityManagerFactory.close(); }
-
常用方法測試
-
我們在上述代碼持久化部分測試以下方法,將初始化部分代碼放入 @Before 內,將提交事務部分代碼放入 @After 內,持久化操作方法在 @Test 內執行
public class EntityManagerMethodTest { private EntityManagerFactory entityManagerFactory; private EntityManager entityManager; private EntityTransaction entityTransaction; @Before public void init() { entityManagerFactory = Persistence.createEntityManagerFactory("persistenceUnit"); entityManager = entityManagerFactory.createEntityManager(); entityTransaction = entityManager.getTransaction(); entityTransaction.begin(); } @After public void destroy() { entityTransaction.commit(); entityManager.close(); entityManagerFactory.close(); } @Test public void test() { // 持久化操作 } }
-
find():類似於 Hibernate 中 Session 的 get 方法
-
getReference():類似於 Hibernate 中 Session 的 load 方法,即在需要的時候才會去執行 SQL 語句,初始化對象,否則返回的為代理對象
-
persistence():類似於 Hibernate 中 Session 的 save 方法,但此方法所要存取的對象若有 id,那么會拋異常
-
remove():類似於 Hibernate 中 Session 的 delete 方法,但此方法只可刪除持久化對象,而 hibernate 的方法可以刪除游離對象(不在緩存中,但在數據庫中可能有對象,該對象有 id;緩存是指利用方法從數據庫中獲取到對象且將其初始化了,那么關閉 entityManager、提交事務后該對象依舊可使用)
-
-
關聯關系映射(使用 IDEA 可以使用實體生成表,也可以使用對應的額表逆向生成實體類)
-
單向多對一(orders - customer)
-
表結構(oreders 表中有 customer 表的外鍵映射 cus_id)
-
實體映射
-
@ToString @Entity @Table(name = "customer", schema = "jpa") public class CustomerEntity { private int id; private String cusName; @Id @Column(name = "id", nullable = false) @GeneratedValue(strategy = GenerationType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "cus_name", nullable = true, length = 20) public String getCusName() { return cusName; } public void setCusName(String cusName) { this.cusName = cusName; } }
-
-
IDEA 逆向生成實體(記得添加主鍵生成策略)
-
多對一映射方法測試
-
添加數據
/** * n-1 將數據插入表中,建議先插入一的一端 */ @Test public void testMany2OnePersistence() { CustomerEntity customerEntity = new CustomerEntity(); customerEntity.setCusName("bgZyy"); OrdersEntity orderEntity1 = new OrdersEntity(); orderEntity1.setOrderNaem("CC-a-AA"); OrdersEntity orderEntity2 = new OrdersEntity(); orderEntity2.setOrderNaem("DD-b-BB"); orderEntity1.setCustomerByCusId(customerEntity); orderEntity2.setCustomerByCusId(customerEntity); entityManager.persist(customerEntity); entityManager.persist(orderEntity1); entityManager.persist(orderEntity2); }
-
獲取數據
-
-
-
單向一對多(company - employee)
-
表結構
-
實體映射
-
關聯關系維護
-
一對多方法測試
-
添加數據
@Test public void testOne2ManyPersistence() { CompanyEntity companyEntity = new CompanyEntity(); companyEntity.setComName("IT"); EmployeeEntity employeesEntity = new EmployeeEntity(); employeesEntity.setEmpName("gg"); EmployeeEntity employeesEntity1 = new EmployeeEntity(); employeesEntity1.setEmpName("yy"); // 員工集合 Collection<EmployeeEntity> employeeEntities = new ArrayList<>(); employeeEntities.add(employeesEntity); employeeEntities.add(employeesEntity1); // 為 company 添加員工的集合信息 companyEntity.setEmployeesById(employeeEntities); // 執行保存操作 entityManager.persist(employeesEntity); entityManager.persist(employeesEntity1); entityManager.persist(companyEntity); }
-
獲取數據
@Test public void testOne2ManyFind() { CompanyEntity companyEntity; companyEntity = entityManager.find(CompanyEntity.class, 1); System.out.println(companyEntity.getComName()); System.out.println(companyEntity.getEmployeesById().size()); }
-
-
-
雙向一對一映射
-
表結構
-
實體映射
-
方法測試
- 保存數據(先保存不維護關聯關系的一端,否則會多出 UPDATE 語句)
- 保存數據(先保存不維護關聯關系的一端,否則會多出 UPDATE 語句)
-
使用 IDEA 反向生成實體(雙向一對一)
-
-
雙向多對多映射
- 配置一覽圖(實體生成數據表),核心配置如下圖所示,對於添加數據獲取數據代碼不再展示
- 配置一覽圖(實體生成數據表),核心配置如下圖所示,對於添加數據獲取數據代碼不再展示
JPQL(Java Persistence Query Language)
- JPQL 語言可以是 select、update、delete 語句,他們都是通過 Query 接口封裝執行的。
- Query接口封裝了執行數據庫查詢的相關方法。調用 EntityManager 的 createQuery、create NamedQuery 及 createNativeQuery 方法可以獲得查詢對象,進而可調用 Query 接口的相關方法來執行查詢操作。
- 方法測試
-
獲取某一范圍所有屬性的集合
-
獲取某一范圍部分屬性的集合,其和獲取所有屬性的集合所使用的方法一樣,不同的是 jpql 語句不一樣,且需要對應的實體有部分屬性的構造器
-
使用本地 SQL 語句查詢,和以上兩個所使用的方法不一樣,此時使用 createNativeQuery()
-
JPQL 還支持二級緩存,order by 子句,group by 子句,聚合查詢,having 子句,關聯查詢,子查詢等,JPQL 還有大量函數,如字符串處理函數,算術函數和日期函數等功能,這里就不再一一列舉,下面列出常用的方法和函數(了解即可):
-
常用函數
- concat(String s1, String s2):字符串合並/連接函數。
- substring(String s, int start, int length):取字串函數。
- trim([leading|trailing|both,] [char c,] String s):從字符串中去掉首/尾指定的字符或空格。
- lower(String s):將字符串轉換成小寫形式。
- upper(String s):將字符串轉換成大寫形式。
- length(String s):求字符串的長度。
-
Query 接口主要方法
- int executeUpdate(),用於執行update或delete語句。
- List getResultList(),用於執行select語句並返回結果集實體列表。
- Object getSingleResult(),用於執行只返回單個結果實體的select語句
- Query setFirstResult(int startPosition),用於設置從哪個實體記錄開始返回查詢結果。
- Query setMaxResults(int maxResult),用於設置返回結果實體的最大數。與setFirstResult結合使用可實現分頁查詢。
Spring 整合 JPA
-
整合什么
- Spring 管理 EntityManager,JPA 使用聲明式事務
-
使用什么整合
- LocalContainerEntityManagerFactoryBean,其適用於所有環境
-
整合步驟
-
jar 包
- Spring + Hibernate + JPA + C3P0 + MySQL
-
創建 Spring 配置文件
- 配置數據源
- 配置 EntityManagerFactoryBean,即 LocalContainerEntityManagerFactoryBean,其需要屬性 DataSource、jpaVendorAdapter(JPA 提供商的適配器,通過內部 bean 的方式)、packagesToScan(Entity 在哪個包下),配置 JPA 基本屬性(show_sql 等)
- 配置 JPA 使用的事務管理器(JPAtransactionManager)
- 配置事務
-
在 DAO 中使用 EntityManager
- 如何獲取到和當前事務關聯的 EntityManager 對象?
- 通過 @PesistenceContext 注解標記成員變量
-
一覽圖
-
以上就是我所學到有關 JPA 的知識,還望有用!再就是希望大牛們可以提點建設性的建議,共同進步,先謝謝了!