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="http://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(); } }