JPA與hibernate——-JPA01
ORM概述
ORM(Object-Relational Mapping) 表示對象關係映射。在面向對象的軟體開發中,通過ORM,就可以把對象映射到關係型資料庫中。只要有一套程式能夠做到建立對象與資料庫的關聯,操作對象就可以直接操作資料庫數據,就可以說這套程式實現了ORM對象關係映射
簡單的說:ORM就是建立實體類和資料庫表之間的關係,從而達到操作實體類就相當於操作資料庫表的目的。
為什麼要使用ORM
當實現一個應用程式時(不使用O/R Mapping),我們可能會寫特別多數據訪問層的程式碼,從資料庫保存數據、修改數據、刪除數據,而這些程式碼都是重複的。而使用ORM則會大大減少重複性程式碼。對象關係映射(Object Relational Mapping,簡稱ORM),主要實現程式對象到關係資料庫數據的映射。
常見的ORM框架
Mybatis(ibatis)、hibernate、Jpa
hibernate與JPA的概述
hibernate概述
Hibernate是一個開放源程式碼的對象關係映射框架,它對JDBC進行了非常輕量級的對象封裝,它將POJO與資料庫表建立映射關係,是一個全自動的orm框架,hibernate可以自動生成SQL語句,自動執行,使得Java程式設計師可以隨心所欲的使用對象編程思維來操縱資料庫。
JPA概述
JPA的全稱是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基於ORM的規範,內部是由一系列的介面和抽象類構成。
JPA通過JDK 5.0註解描述對象-關係表的映射關係,並將運行期的實體對象持久化到資料庫中。
JPA的優勢
1. 標準化
JPA 是 JCP 組織發布的 Java EE 標準之一,因此任何聲稱符合 JPA 標準的框架都遵循同樣的架構,提供相同的訪問API,這保證了基於JPA開發的企業應用能夠經過少量的修改就能夠在不同的JPA框架下運行。
2. 容器級特性的支援
JPA框架中支援大數據集、事務、並發等容器級事務,這使得 JPA 超越了簡單持久化框架的局限,在企業應用發揮更大的作用。
3. 簡單方便
JPA的主要目標之一就是提供更加簡單的編程模型:在JPA框架下創建實體和創建Java 類一樣簡單,沒有任何的約束和限制,只需要使用 javax.persistence.Entity進行注釋,JPA的框架和介面也都非常簡單,沒有太多特別的規則和設計模式的要求,開發者可以很容易的掌握。JPA基於非侵入式原則設計,因此可以很容易的和其它框架或者容器集成
4. 查詢能力
JPA的查詢語言是面向對象而非面向資料庫的,它以面向對象的自然語法構造查詢語句,可以看成是Hibernate HQL的等價物。JPA定義了獨特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一種擴展,它是針對實體的一種查詢語言,操作對象是實體,而不是關係資料庫的表,而且能夠支援批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能夠提供的高級查詢特性,甚至還能夠支援子查詢。
5. 高級特性
JPA 中能夠支援面向對象的高級特性,如類之間的繼承、多態和類之間的複雜關係,這樣的支援能夠讓開發者最大限度的使用面向對象的模型設計企業應用,而不需要自行處理這些特性在關係資料庫的持久化。
JPA與hibernate的關係
JPA規範本質上就是一種ORM規範,注意不是ORM框架——因為JPA並未提供ORM實現,它只是制訂了一些規範,提供了一些編程的API介面,但具體實現則由服務廠商來提供實現。
JPA和Hibernate的關係就像JDBC和JDBC驅動的關係,JPA是規範,Hibernate除了作為ORM框架之外,它也是一種JPA實現。JPA怎麼取代Hibernate呢?JDBC規範可以驅動底層資料庫嗎?答案是否定的,也就是說,如果使用JPA規範進行資料庫操作,底層需要hibernate作為其實現類完成數據持久化工作。
案例介紹
此處使用hibernate 5.4.10.Final maven 3.6.3
導入依賴
<dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.4.10.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> <version>5.4.10.Final</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
創建配置文件persistence.xml
注意:配置文件的目錄
在java工程的src路徑下創建一個名為META-INF的文件夾,在此文件夾下創建一個名為persistence.xml的配置文件
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="//java.sun.com/xml/ns/persistence" version="2.0"> <!-- 需要配置persistence-unit 節點 持久化單元 name : 持久化單元名稱 transaction-type : 事務管理方式 RESOURCE_LOCAL : 本地事務管理 JTA : 分散式事務管理 --> <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL"> <!-- jpa的實現方式 --> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <!-- 配置資料庫的資訊 用戶名 : javax.persistence.jdbc.user 密碼 : javax.persistence.jdbc.password 連接地址 : javax.persistence.jdbc.url 驅動 : javax.persistence.jdbc.driver --> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.password" value="root"/> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <!--配置jpa實現方(hibernate)的配置資訊 顯示sql : false|true 自動創建資料庫表 : hibernate.hbm2ddl.auto create : 程式運行時創建資料庫表(如果有表,先刪除表再創建) update :程式運行時創建表(如果有表,不會創建表) none :不會創建表 --> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence>
創建實體類
package com.ytfs.entity; import javax.persistence.*; import java.io.Serializable; /** * @Classname Customer * @Description TODO(客戶實體類) * @Date 2020/4/28 22:34 * @Created by ytfs * 客戶的實體類 * 配置映射關係 * 1.實體類和表的映射關係 * @Entity:聲明實體類 * @Table : 配置實體類和表的映射關係 * name : 配置資料庫表的名稱 * 2.實體類中屬性和表中欄位的映射關係 * */ @Entity @Table(name = "cst_customer") public class Customer implements Serializable { /** * @Id:聲明主鍵的配置 * @GeneratedValue:配置主鍵的生成策略 * strategy * GenerationType.IDENTITY :自增,mysql * * 底層資料庫必須支援自動增長(底層資料庫支援的自動增長方式,對id自增) * GenerationType.SEQUENCE : 序列,oracle * * 底層資料庫必須支援序列 * GenerationType.TABLE : jpa提供的一種機制,通過一張資料庫表的形式幫助我們完成主鍵自增 * GenerationType.AUTO : 由程式自動的幫助我們選擇主鍵生成策略 * @Column:配置屬性和欄位的映射關係 * name:資料庫表中欄位的名稱 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "cust_id") private Long custId; @Column(name = "cust_name") private String custName; @Column(name = "cust_source") private String custSource; @Column(name = "cust_industry") private String custIndustry; @Column(name = "cust_level") private String custLevel; @Column(name = "cust_address") private String custAddress; @Column(name = "cust_phone") private String custPhone; public Long getCustId() { return custId; } public void setCustId(Long custId) { this.custId = custId; } public String getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; } public String getCustSource() { return custSource; } public void setCustSource(String custSource) { this.custSource = custSource; } public String getCustIndustry() { return custIndustry; } public void setCustIndustry(String custIndustry) { this.custIndustry = custIndustry; } public String getCustLevel() { return custLevel; } public void setCustLevel(String custLevel) { this.custLevel = custLevel; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } @Override public String toString() { return "Customer{" + "custId=" + custId + ", custName='" + custName + '\'' + ", custSource='" + custSource + '\'' + ", custIndustry='" + custIndustry + '\'' + ", custLevel='" + custLevel + '\'' + ", custAddress='" + custAddress + '\'' + ", custPhone='" + custPhone + '\'' + '}'; } }
參數的解釋
@Entity
作用:指定當前類是實體類。
@Table
作用:指定實體類和表之間的對應關係。
屬性:
name:指定資料庫表的名稱
@Id
作用:指定當前欄位是主鍵。
@GeneratedValue
作用:指定主鍵的生成方式。。
屬性:
strategy :指定主鍵生成策略。
@Column
作用:指定實體類屬性和資料庫表之間的對應關係
屬性:
name:指定資料庫表的列名稱。
unique:是否唯一
nullable:是否可以為空
inserttable:是否可以插入
updateable:是否可以更新
columnDefinition: 定義建表時創建此列的DDL
secondaryTable: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字搭建開發環境[重點]
創建JPA的連接工具類
package com.ytfs.utils; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; /** * @Classname Jpautil * @Description TODO(Jpa實體類管理工廠) * @Date 2020/4/28 23:38 * @Created by ytfs */ public class JpaUtil { //執行緒安全的 private static final EntityManagerFactory FACTORY; static { /*這裡的實體類工廠的persistenceUnitName是配置文件中 <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL"> */ FACTORY = Persistence.createEntityManagerFactory("myJpa"); } /** * 返回實體類管理對象 * @return */ public static EntityManager getEM(){ return FACTORY.createEntityManager(); } }
創建測試類
package com.ytfs; import com.ytfs.entity.Customer; import com.ytfs.utils.JpaUtil; import org.junit.Test; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; /** * @Classname test * @Description TODO(JPA測試) * @Date 2020/4/28 22:58 * @Created by ytfs */ public class test { /** * 保存客戶 */ @Test public void testSave() { /* //創建實體類的工廠管理對象 EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa"); //創建實體類管理對象 EntityManager entityManager = factory.createEntityManager();*/ EntityManager entityManager = JpaUtil.getEM(); //開啟事務 entityManager.getTransaction().begin(); //創建一個對象 Customer customer = new Customer(); customer.setCustName("張三"); customer.setCustAddress("重慶市沙坪壩區"); //保存對象 entityManager.persist(customer); //提交事務 entityManager.getTransaction().commit(); //釋放資源 entityManager.close(); //factory.close(); 利用工具類之後就不用關閉工廠對象,因為執行緒安全只需要一個,後面的還會一直用 } /** * @throws * @description find方式通過Id查詢 * @author 雨聽風說 * @updateTime 2020/4/29 13:03 * 使用find方法查詢: * 1.查詢的對象就是當前客戶對象本身 * 2.在調用find方法的時候,就會發送sql語句查詢資料庫 * <p> * 立即載入 */ @Test public void testFind() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //獲取事務對象 EntityTransaction tx = em.getTransaction(); //開啟事務 tx.begin(); //通過實體類管理對象查詢 Customer customer = em.find(Customer.class, 1L); //customer.soutv System.out.println("customer = " + customer); //提交事務 tx.commit(); //釋放資源 em.close(); } /** * @param * @return * @throws * @description 通過事務管理對象的get方式通過Id查詢 * @author 雨聽風說 * @updateTime 2020/4/29 13:13 * getReference方法 * 1.獲取的對象是一個動態代理對象 * 2.調用getReference方法不會立即發送sql語句查詢資料庫 * * 當調用查詢結果對象的時候,才會發送查詢的sql語句:什麼時候用,什麼時候發送sql語句查詢資料庫 * <p> * 延遲載入(懶載入) * * 得到的是一個動態代理對象 * * 什麼時候用,什麼使用才會查詢 */ @Test public void testGetRefrence() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //獲取事務對象並開啟事務 EntityTransaction tx = em.getTransaction(); tx.begin(); //通過實體類對象查詢 Customer customer = em.getReference(Customer.class, 1L); System.out.println("customer = " + customer); //提交事務 tx.commit(); //釋放資源 em.close(); } /** * @param * @return * @throws * @description 刪除對象 * @author 雨聽風說 * @updateTime 2020/4/29 13:17 */ @Test public void testRemove() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //獲取事務對象並開啟事務 EntityTransaction tx = em.getTransaction(); tx.begin(); //通過實體類對象刪除 //i 通過id查詢需要刪除的對象 Customer customer = em.find(Customer.class, 1L); //ii 調用remove方法,傳入需要刪除的對象 em.remove(customer); //提交事務 tx.commit(); //釋放資源 em.close(); } /** * @param * @return * @throws * @description 更新對象 * @author 雨聽風說 * @updateTime 2020/4/29 13:17 */ @Test public void testUpdate() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //獲取事務對象並開啟事務 EntityTransaction tx = em.getTransaction(); tx.begin(); //通過實體類對象更新 //i 通過id查詢需要更新的對象 Customer customer = em.find(Customer.class, 2L); customer.setCustName("電動小馬達"); //ii 調用merge方法,傳入需要更新的對象 em.merge(customer); //提交事務 tx.commit(); //釋放資源 em.close(); } }
JPQL查詢(Java Persistence Query Language)
基於首次在EJB2.0中引入的EJB查詢語言(EJB QL),Java持久化查詢語言(JPQL)是一種可移植的查詢語言,旨在以面向對象表達式語言的表達式,將SQL語法和簡單查詢語義綁定在一起·使用這種語言編寫的查詢是可移植的,可以被編譯成所有主流資料庫伺服器上的SQL。
其特徵與原生SQL語句類似,並且完全面向對象,通過類名和屬性訪問,而不是表名和表的屬性。
測試
package com.ytfs; import com.ytfs.utils.JpaUtil; import org.junit.Test; import javax.persistence.EntityManager; import javax.persistence.Query; import java.util.List; /** * @Classname JpqlTest * @Description TODO(Jpql的測試案例) * @Date 2020/4/29 13:22 * @Created by ytfs */ public class JpqlTest { /** * jpql查詢全部 * jqpl:from com.ytfs.entity.Customer * sql:SELECT * FROM cst_customer */ @Test public void testFindAll() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //通過實體類管理對象獲取事務對象並開啟事務 em.getTransaction().begin(); //書寫jpql語句 String jpql = "from Customer"; //通過實體類管理對象執行查詢 Query query = em.createQuery(jpql); //獲取查詢結果 List list = query.getResultList(); //遍歷輸出 list.stream().forEach(System.out::println); //提交事務 em.getTransaction().commit(); //釋放資源 em.close(); } /** * 排序查詢: 倒序查詢全部客戶(根據id倒序) * sql:SELECT * FROM cst_customer ORDER BY cust_id DESC * jpql:from Customer order by custId desc * * 進行jpql查詢 * 1.創建query查詢對象 * 2.對參數進行賦值 * 3.查詢,並得到返回結果 */ @Test public void test() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //通過實體類管理對象獲取事務對象並開啟事務 em.getTransaction().begin(); //書寫jpql語句 String jpql = "from Customer order by custId desc"; //通過實體類管理對象執行查詢 Query query = em.createQuery(jpql); //獲取查詢結果 List list = query.getResultList(); //遍歷輸出 list.stream().forEach(System.out::println); //提交事務 em.getTransaction().commit(); //釋放資源 em.close(); } /** * 使用jpql查詢,統計客戶的總數 * sql:SELECT COUNT(cust_id) FROM cst_customer * jpql:select count(custId) from Customer */ @Test public void testCount() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //通過實體類管理對象獲取事務對象並開啟事務 em.getTransaction().begin(); //書寫jpql語句 String jpql = "select count(custId) from Customer"; //通過實體類管理對象執行查詢 Query query = em.createQuery(jpql); //ii.對參數賦值 //iii.發送查詢,並封裝結果 /** * getResultList : 直接將查詢結果封裝為list集合 * getSingleResult : 得到唯一的結果集 */ Object singleResult = query.getSingleResult(); //遍歷輸出 System.out.println("singleResult = " + singleResult); //提交事務 em.getTransaction().commit(); //釋放資源 em.close(); } /** * 分頁查詢 * sql:select * from cst_customer limit 0,2 * jqpl : from Customer */ @Test public void testByPage() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //通過實體類管理對象獲取事務對象並開啟事務 em.getTransaction().begin(); //書寫jpql語句 String jpql = "from Customer"; //通過實體類管理對象執行查詢 Query query = em.createQuery(jpql); //ii.對參數賦值 //開始的索引 query.setFirstResult(0); //每頁的條數 query.setMaxResults(2); //iii.發送查詢,並封裝結果 /** * getResultList : 直接將查詢結果封裝為list集合 * getSingleResult : 得到唯一的結果集 */ //獲取查詢結果 List list = query.getResultList(); //遍歷輸出 list.stream().forEach(System.out::println); //提交事務 em.getTransaction().commit(); //釋放資源 em.close(); } /** * 條件查詢 * 案例:查詢客戶名稱包含『小馬』的客戶 * sql:SELECT * FROM cst_customer WHERE cust_name LIKE ?1 * jpql : from Customer where custName like ?1 */ @Test public void testByCondition() { //通過工具類獲取實體類管理對象 EntityManager em = JpaUtil.getEM(); //通過實體類管理對象獲取事務對象並開啟事務 em.getTransaction().begin(); //書寫jpql語句 String jpql = "from Customer where custName like ?1 "; //通過實體類管理對象執行查詢 Query query = em.createQuery(jpql); //ii.對參數賦值 //第一個參數:佔位符的索引位置(jpql語句後的數字代表,直接問號出現異常),第二個參數:取值 query.setParameter(1, "%小馬%"); //iii.發送查詢,並封裝結果 /** * getResultList : 直接將查詢結果封裝為list集合 * getSingleResult : 得到唯一的結果集 */ //獲取查詢結果 List list = query.getResultList(); //遍歷輸出 list.stream().forEach(System.out::println); //提交事務 em.getTransaction().commit(); //釋放資源 em.close(); } }