一、ORM概述
- ORM(Object-Relational Mapping) 表示對象關係映射。在面向對象的軟件開發中,通過ORM,就可以把對象映射到關係型數據庫中。只要有一套程序能夠做到建立對象與數據庫的關聯,操作對象就可以直接操作數據庫數據,就可以說這套程序實現了ORM對象關係映射。簡單的說:ORM就是建立實體類和數據庫表之間的關係,從而達到操作實體類就相當於操作數據庫表的目的。
- 當實現一個應用程序時(不使用O/R Mapping),我們可能會寫特別多數據訪問層的代碼,從數據庫保存數據、修改數據、刪除數據,而這些代碼都是重複的。而使用ORM則會大大減少重複性代碼。對象關係映射(Object Relational Mapping,簡稱ORM),主要實現程序對象到關係數據庫數據的映射。
- 常見的 ORM 框架:Mybatis、Hibernate、Jpa
二、hibernate與JPA
1、hibernate 概述
- Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了非常輕量級的對象封裝,它將POJO與數據庫表建立映射關係,是一個全自動的orm框架。
- hibernate可以自動生成SQL語句,自動執行,使得Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。
2、JPA概述
- JPA的全稱是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基於ORM的規範,內部是由一系列的接口和抽象類構成。
- JPA通過JDK 5.0註解描述對象-關係表的映射關係,並將運行期的實體對象持久化到數據庫中。
3、JPA的優勢
-
標準化
JPA 是 JCP 組織發佈的 Java EE 標準之一,因此任何聲稱符合 JPA 標準的框架都遵循同樣的架構,提供相同的訪問API,這保證了基於JPA開發的企業應用能夠經過少量的修改就能夠在不同的JPA框架下運行。 -
容器級特性的支持
JPA框架中支持大數據集、事務、併發等容器級事務,這使得 JPA 超越了簡單持久化框架的侷限,在企業應用發揮更大的作用。 -
簡單方便
JPA的主要目標之一就是提供更加簡單的編程模型:在JPA框架下創建實體和創建Java 類一樣簡單,沒有任何的約束和限制,只需要使用 javax.persistence.Entity進行註釋,JPA的框架和接口也都非常簡單,沒有太多特別的規則和設計模式的要求,開發者可以很容易的掌握。JPA基於非侵入式原則設計,因此可以很容易的和其它框架或者容器集成 -
查詢能力
JPA的查詢語言是面向對象而非面向數據庫的,它以面向對象的自然語法構造查詢語句,可以看成是Hibernate HQL的等價物。JPA定義了獨特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一種擴展,它是針對實體的一種查詢語言,操作對象是實體,而不是關係數據庫的表,而且能夠支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能夠提供的高級查詢特性,甚至還能夠支持子查詢。 -
高級特性
JPA 中能夠支持面向對象的高級特性,如類之間的繼承、多態和類之間的複雜關係,這樣的支持能夠讓開發者最大限度的使用面向對象的模型設計企業應用,而不需要自行處理這些特性在關係數據庫的持久化。
4、JPA與hibernate的關係
- JPA規範本質上就是一種ORM規範,注意不是ORM框架——因爲JPA並未提供ORM實現,它只是制訂了一些規範,提供了一些編程的API接口,但具體實現則由服務廠商來提供實現。
- JPA和Hibernate的關係就像JDBC和JDBC驅動的關係,JPA是規範,Hibernate除了作爲ORM框架之外,它也是一種JPA實現。JPA怎麼取代Hibernate呢?JDBC規範可以驅動底層數據庫嗎?答案是否定的,也就是說,如果使用JPA規範進行數據庫操作,底層需要hibernate作爲其實現類完成數據持久化工作。
三、案例(一對多)
常用註解說明
@Entity
:指定當前類是持久化實體類,不能省略@Table
作用:指定實體類和表之間的對應關係,可以省略
默認:表名=類名(省略後)
屬性:name
:指定數據庫表的名稱@Id
:指定當前字段是主鍵。不能省略@GeneratedValue
作用:指定主鍵的生成方式。。
屬性:strategy
:指定主鍵生成策略。@Column
作用:指定實體類屬性和數據庫表之間的對應關係
屬性:
name
:指定數據庫表的列名稱。
unique
:是否唯一
nullable
:是否可以爲空
inserttable
:是否可以插入
updateable
:是否可以更新
columnDefinition
: 定義建表時創建此列的DDL
secondaryTable
: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字搭建開發環境[重點]@OneToMany
:映射一對多
mapperdBy:指定對方關聯屬性,表示當前類不管理關聯關係,相當於inverse=false
fetch:是否使用懶加載@ManyToOne
:映射多對一
@JoinColumn
:映射外鍵@ManyToMany
:映射多對多
mappedBy=“對方的關聯屬性”:相當於inverse=true,便是當前方不管理關聯關係
在管理關聯關係的一方配置關係表@JoinTable
@JoinTable
name
:關係表名
joinColumns
:當前表在中間表的外鍵
inverseJoinColumns
:對方表在中間表的外鍵
第一步:導入 jar 包 hibernate-entitymanager
第二步:編寫實體類和數據庫表的映射配置(在實體類上使用JPA註解的形式配置映射關係)Customer.java、Order.java
package cn.lemon.domain;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity/*指定當前類是持久化實體類(不能省略)*/
@Table(name = "customer_")/*指定實體類和數據庫表的對應關係(可以省略),省略後:表名=類名*/
public class Customer {
@Id/*指定當前字段是主鍵字段*/
@GeneratedValue(strategy = GenerationType.IDENTITY)/*指定主鍵的生成形式,GenerationType:主鍵生成策略*/
@Column(name = "ID")/*指定實體類和數據庫表的對應關係*/
private Integer id;
@Column(name = "NAME")
private String name;
/*客戶的多個訂單*/
@OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)/*一對多映射,mapperBy:指定對方關聯屬性,fetch:是否使用延遲加載*/
private Set<Order> orders = new HashSet<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}
package cn.lemon.domain;
import javax.persistence.*;
@Entity/*指定當前類是持久化實體類(不能省略)*/
@Table(name = "order_")/*指定實體類和數據庫表的對應關係(可以省略),省略後:表名=類名*/
public class Order {
@Id/*指定當前字段是主鍵字段*/
@GeneratedValue(strategy = GenerationType.IDENTITY)/*指定主鍵的生成形式,GenerationType:主鍵生成策略*/
@Column(name = "ID")/*指定實體類和數據庫表的對應關係*/
private Integer id;
@Column(name = "NAME")
private String name;
/*關聯屬性:訂單所屬的客戶*/
@ManyToOne(fetch = FetchType.LAZY)/*多對一映射,fetch = FetchType.LAZY 默認使用延遲加載(可以省略)*/
@JoinColumn(name = "CUST_ID")/*映射外鍵*/
private Customer customer;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
第三步:配置JPA的核心配置文件(路徑:resource/META-INF/persistence.xml)
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--JPA 核心配置文件-->
<!--需要配置persistence-unit節點
持久化單元:
name:持久化單元名稱
transaction-type:事務管理的方式
JTA:分佈式事務管理
RESOURCE_LOCAL:本地事務管理
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的實現方式 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--可選配置:配置jpa實現方的配置信息-->
<properties>
<!-- 數據庫信息
用戶名,javax.persistence.jdbc.user
密碼, javax.persistence.jdbc.password
驅動, javax.persistence.jdbc.driver
數據庫地址 javax.persistence.jdbc.url
-->
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="lemon"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///db_javapersistenceapi"/>
<!--配置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" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL55Dialect" />
</properties>
</persistence-unit>
</persistence>
第四步: 工具類 JpaUtils.java(用於讀取JPA核心配置文件產生 SessionFactory)
package cn.lemon.utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaUtils {
/**
* 解決實體管理器工廠的浪費資源和耗時問題
* 通過靜態代碼塊的形式,當程序第一次訪問此工具類時,創建一一個公共的實體管理器工廠對象
* 第一次訪問getEntityManager方法: 經過靜態代碼塊創建一 個factory對象, 再調用方法創建一 個EntityManager對象
* 第二次方法getEntityManager方法: 直接通過一-個已經創建好的factory對象,創建EntityManager對象
*/
private static EntityManagerFactory entityManagerFactory;
static {
entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");/*加載配置文件,創建 createEntityManagerFactory*/
}
public static EntityManager getEntityManager() {/*獲取 EntityManager 對象*/
return entityManagerFactory.createEntityManager();
}
}
第五步: 測試類
package cn.lemon.test;
import cn.lemon.domain.Customer;
import cn.lemon.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
public class OnetoManyTest {
@Test
public void testCreateTable() {
//當sessionFactory產生時產生表結構
JpaUtils.getEntityManager();
}
@Test
public void testAdd() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = new Customer();
c.setName("騰訊");
entityManager.persist(c);
tx.commit();
entityManager.close();
}
@Test
public void testUpdate() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = new Customer();
c.setId(1);
c.setName("百度");
entityManager.merge(c);
tx.commit();
entityManager.close();
}
@Test
public void testDelete() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = entityManager.find(Customer.class, 3);
entityManager.remove(c);
tx.commit();
entityManager.close();
}
@Test
public void testGet() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = entityManager.find(Customer.class, 1);
System.out.println(c.getName());
tx.commit();
entityManager.close();
}
@Test
public void testLoad() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = entityManager.getReference(Customer.class, 1); //使用懶加載(session.load())
tx.commit();
entityManager.close();
System.out.println(c.getName());
}
}
四、JPA中的主鍵生成策略
通過annotation(註解)來映射hibernate實體的,基於annotation的hibernate主鍵標識爲@Id
, 其生成規則由@GeneratedValue
設定的。@id和@GeneratedValue都是JPA的標準用法。
1、IDENTITY
:主鍵由數據庫自動生成(主要是自動增長型)
- 用法:
@GeneratedValue(strategy = GenerationType.IDENTITY)
2、SEQUENCE
:根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列。
3、AUTO
:主鍵由程序控制
- 用法:
@GeneratedValue(strategy = GenerationType.AUTO)
4、TABLE
:使用一個特定的數據庫表格來保存主鍵
五、JPA的API介紹
1、Persistence對象
- Persistence對象主要作用是用於獲取EntityManagerFactory對象的 。通過調用該類的createEntityManagerFactory靜態方法,根據配置文件中持久化單元名稱創建EntityManagerFactory。
2、EntityManagerFactory
- EntityManagerFactory 接口主要用來創建 EntityManager 實例
- 由於EntityManagerFactory 是一個線程安全的對象(即多個線程訪問同一個EntityManagerFactory 對象不會有線程安全問題),並且EntityManagerFactory 的創建極其浪費資源,所以在使用JPA編程時,我們可以對EntityManagerFactory 的創建進行優化,只需要做到一個工程只存在一個EntityManagerFactory 即可
3、EntityManager
- 在 JPA 規範中, EntityManager是完成持久化操作的核心對象。實體類作爲普通 java對象,只有在調用 EntityManager將其持久化後纔會變成持久化對象。EntityManager對象在一組實體類與底層數據源之間進行 O/R 映射的管理。它可以用來管理和更新 Entity Bean, 根椐主鍵查找 Entity Bean, 還可以通過JPQL語句查詢實體。
- 我們可以通過調用EntityManager的方法完成獲取事務,以及持久化數據庫的操作
方法說明:
- getTransaction : 獲取事務對象
- persist : 保存操作
- merge : 更新操作
- remove : 刪除操作
- find/getReference : 根據id查詢
4、EntityTransaction
- 在 JPA 規範中, EntityTransaction是完成事務操作的核心對象,對於EntityTransaction在我們的java代碼中承接的功能比較簡單
- begin:開啓事務
- commit:提交事務
- rollback:回滾事務
六、JPQL(Java Persistence Query Language) 查詢
package cn.lemon.test;
import cn.lemon.domain.Customer;
import cn.lemon.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;
public class JpqlTest {
@Test
/**
* jpql查詢,具體查詢案例和應用,參考hibernate的hql查詢
*/
public void testFindAll() {
EntityManager entityManager = JpaUtils.getEntityManager();
Query query = entityManager.createQuery("from Customer");
List<Customer> list = query.getResultList();
for (Customer customer : list) {
System.out.println(customer.getName());
}
}
@Test
public void testFindByCondition() {
EntityManager entityManager = JpaUtils.getEntityManager();
Query query = entityManager.createQuery("from Customer where name like ?0"); //jpql java persistense query language
List<Customer> list = query.setParameter(0, "%a%").getResultList();
for (Customer customer : list) {
System.out.println(customer.getName());
}
}
@Test
public void testFindByPage() {
EntityManager entityManager = JpaUtils.getEntityManager();
Query query = entityManager.createQuery("from Customer"); //jpql java persistense query language
List<Customer> list = query.setFirstResult(5).setMaxResults(5).getResultList();
for (Customer customer : list) {
System.out.println(customer.getName());
}
}
/**
* jpa的criteria查詢,具體參考hibernate的qbc查詢
*/
@Test
public void testFindByCriteria() {
EntityManager entityManager = JpaUtils.getEntityManager();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Customer> criteria = builder.createQuery(Customer.class);
Root<Customer> root = criteria.from(Customer.class);
criteria.select(root);
//條件
criteria.where(builder.like(root.get("name").as(String.class), "%a%"));
List<Customer> list = entityManager.createQuery(criteria).getResultList();
for (Customer customer : list) {
System.out.println(customer.getName());
}
}
}