一篇 JPA 總結


概述

下面是 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)
  • 導入 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 對應 identityMySQL 對應 auto increment
      • IDENTITY:采用數據庫 ID自增長的方式來自增主鍵字段,Oracle 不支持這種方式
      • AUTO: JPA自動選擇合適的策略,是默認選項
      • TABLE:通過表產生主鍵,框架借由表模擬序列產生主鍵,使用該策略可以使應用更易於數據庫移植。
      • SEQUENCE:通過序列產生主鍵,通過 @SequenceGenerator 注解指定序列名,MySql 不支持這種方式
  • @Basic

    • 表示一個簡單的屬性到數據表的字段的映射,對於沒有任何標注的 getXxx() 方法,默認為 @Basic
      • fetch 表示屬性的讀取策略,有 EAGER 和 LAZY 兩種,分別為主支抓取和延遲加載
      • optional 表示該屬性是否允許為 null,默認為 true
  • @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 語句)
    • 使用 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(),用於執行updatedelete語句。
    • 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,其需要屬性 DataSourcejpaVendorAdapter(JPA 提供商的適配器,通過內部 bean 的方式)、packagesToScan(Entity 在哪個包下),配置 JPA 基本屬性(show_sql 等)
      • 配置 JPA 使用的事務管理器(JPAtransactionManager)
      • 配置事務
    • 在 DAO 中使用 EntityManager

      • 如何獲取到和當前事務關聯的 EntityManager 對象?
      • 通過 @PesistenceContext 注解標記成員變量
    • 一覽圖

以上就是我所學到有關 JPA 的知識,還望有用!再就是希望大牛們可以提點建設性的建議,共同進步,先謝謝了!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM