Spring Data Jpa(1):Jpa概述
一、ORM概述[瞭解]
ORM(Object-Relational Mapping) 表示對象關係映射。只要有一套程序能夠做到建立對象與數據庫的關聯,操作對象就可以直接操作數據庫數據,就可以說這套程序實現了ORM對象關係映射
簡單的說:ORM就是建立實體類和數據庫表之間的關係,從而達到操作實體類就相當於操作數據庫表的目的。
Mybatis(ibatis)、Hibernate、Jpa都是使用ORM思想
二、hibernate與JPA的概述[瞭解]
2.1 hibernate概述
Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了非常輕量級的對象封裝,它將POJO與數據庫表建立映射關係,是一個全自動的orm框架,hibernate可以自動生成SQL語句,自動執行,使得Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。
2.2 JPA概述
JPA的全稱是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基於ORM的規範,內部是由一系列的接口和抽象類構成。
JPA通過JDK 5.0註解描述對象-關係表的映射關係,並將運行期的實體對象持久化到數據庫中。 JPA框架中支持大數據集、事務、併發等容器級事務
2.3 JPA與hibernate的關係
JPA規範本質上就是一種ORM規範,注意不是ORM框架——因爲JPA並未提供ORM實現,它只是制訂了一些規範,提供了一些編程的API接口,但具體實現則由服務廠商來提供實現。
JPA和Hibernate的關係就像JDBC和JDBC驅動的關係,JPA是規範,Hibernate除了作爲ORM框架之外,它也是一種JPA實現。也就是說,如果使用JPA規範進行數據庫操作,底層需要hibernate作爲其實現類完成數據持久化工作。
三、JPA的實現
maven工程導入座標
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- hibernate對jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- log日誌 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
創建客戶的數據庫表
/*創建客戶表*/
CREATE TABLE cst_customer (
cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客戶編號(主鍵)',
cust_name varchar(32) NOT NULL COMMENT '客戶名稱(公司名稱)',
cust_source varchar(32) DEFAULT NULL COMMENT '客戶信息來源',
cust_industry varchar(32) DEFAULT NULL COMMENT '客戶所屬行業',
cust_level varchar(32) DEFAULT NULL COMMENT '客戶級別',
cust_address varchar(128) DEFAULT NULL COMMENT '客戶聯繫地址',
cust_phone varchar(64) DEFAULT NULL COMMENT '客戶聯繫電話',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
創建客戶的實體類
public class Customer implements Serializable {
private Long custId;
private String custName;
private String custSource;
private String custIndustry;
private String custLevel;
private String custAddress;
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;
}
}
3.1編寫實體類和數據庫表的映射配置[重點]
在實體類上使用JPA註解
的形式配置映射關係
/**
* * 所有的註解都是使用JPA的規範提供的註解,
* * 所以在導入註解包的時候,一定要導入javax.persistence下的
*/
@Entity //聲明實體類
@Table(name="cst_customer") //建立實體類和表的映射關係
@Data
public class Customer {
@Id//聲明當前私有屬性爲主鍵
@GeneratedValue(strategy=GenerationType.IDENTITY) //配置主鍵的生成策略
@Column(name="cust_id") //指定和表中cust_id字段的映射關係
private Long custId;
@Column(name="cust_name") //指定和表中cust_name字段的映射關係
private String custName;
@Column(name="cust_source")//指定和表中cust_source字段的映射關係
private String custSource;
@Column(name="cust_industry")//指定和表中cust_industry字段的映射關係
private String custIndustry;
@Column(name="cust_level")//指定和表中cust_level字段的映射關係
private String custLevel;
@Column(name="cust_address")//指定和表中cust_address字段的映射關係
private String custAddress;
@Column(name="cust_phone")//指定和表中cust_phone字段的映射關係
private String custPhone;
}
常用註解的說明
@Entity
作用:指定當前類是實體類。
@Table
作用:指定實體類和表之間的對應關係。
屬性:
name:指定數據庫表的名稱
@Id
作用:指定當前字段是主鍵。
@GeneratedValue
作用:指定主鍵的生成方式。。
屬性:
strategy :指定主鍵生成策略。
@Column
作用:指定實體類屬性和數據庫表之間的對應關係
屬性:
name:指定數據庫表的列名稱。
unique:是否唯一
nullable:是否可以爲空
inserttable:是否可以插入
updateable:是否可以更新
columnDefinition: 定義建表時創建此列的DDL
secondaryTable: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字搭建開發環境[重點]
3.2配置JPA的核心配置文件
在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:事務管理的方式
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="xmgl0609"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
<!--配置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>
實現操作
@Test
public void test() {
/**
* 創建實體管理類工廠,藉助Persistence的靜態方法獲取
* 其中傳遞的參數爲持久化單元名稱,需要jpa配置文件中指定
*/
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//創建實體管理類
EntityManager em = factory.createEntityManager();
//獲取事務對象
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
Customer c = new Customer();
c.setCustName("xxx");
//保存操作
em.persist(c);
//提交事務
tx.commit();
//釋放資源
em.close();
factory.close();
}
3.3JPA中的主鍵生成策略
通過註解來映射hibernate實體的,主鍵標識爲@Id, 其生成規則由@GeneratedValue設定的。
JPA提供的四種標準用法爲TABLE,SEQUENCE,IDENTITY,AUTO。
具體說明如下:
IDENTITY
:主鍵由數據庫自動生成(主要是自動增長型)
用法:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long custId;
TABLE
:使用一個特定的數據庫表格來保存主鍵
用法:
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator="payablemoney_gen")
@TableGenerator(name = "pk_gen",
table="tb_generator",
pkColumnName="gen_name",
valueColumnName="gen_value",
pkColumnValue="PAYABLEMOENY_PK",
allocationSize=1
)
private Long custId;
//@TableGenerator的定義:
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface TableGenerator {
//表示該表主鍵生成策略的名稱,它被引用在@GeneratedValue中設置的“generator”值中
String name();
//表示表生成策略所持久化的表名,例如,這裏表使用的是數據庫中的“tb_generator”。
String table() default "";
//catalog和schema具體指定表所在的目錄名或是數據庫名
String catalog() default "";
String schema() default "";
//屬性的值表示在持久化表中,該主鍵生成策略所對應鍵值的名稱。例如在“tb_generator”中將“gen_name”作爲主鍵的鍵值
String pkColumnName() default "";
//屬性的值表示在持久化表中,該主鍵當前所生成的值,它的值將會隨着每次創建累加。例如,在“tb_generator”中將“gen_value”作爲主鍵的值
String valueColumnName() default "";
//屬性的值表示在持久化表中,該生成策略所對應的主鍵。例如在“tb_generator”表中,將“gen_name”的值爲“CUSTOMER_PK”。
String pkColumnValue() default "";
//表示主鍵初識值,默認爲0。
int initialValue() default 0;
//表示每次主鍵值增加的大小,例如設置成1,則表示每次創建新記錄後自動加1,默認爲50。
int allocationSize() default 50;
UniqueConstraint[] uniqueConstraints() default {};
}
//這裏應用表tb_generator,定義爲 :
CREATE TABLE tb_generator (
id NUMBER NOT NULL,
gen_name VARCHAR2(255) NOT NULL,
gen_value NUMBER NOT NULL,
PRIMARY KEY(id)
)
四、JPA的API介紹
4.1 Persistence對象
Persistence對象主要作用是用於獲取EntityManagerFactory對象的 。通過調用該類的createEntityManagerFactory靜態方法,根據配置文件中持久化單元名稱創建EntityManagerFactory。
//1. 創建 EntitymanagerFactory
@Test
String unitName = "myJpa";
EntityManagerFactory factory= Persistence.createEntityManagerFactory(unitName);
4.2 EntityManagerFactory
EntityManagerFactory 接口主要用來創建 EntityManager 實例
//創建實體管理類
EntityManager em = factory.createEntityManager();
由於EntityManagerFactory 是一個線程安全的對象
(即多個線程訪問同一個EntityManagerFactory 對象不會有線程安全問題),並且EntityManagerFactory 的創建極其浪費資源,所以在使用JPA編程時,我們可以對EntityManagerFactory 的創建進行優化,只需要做到一個工程只存在一個EntityManagerFactory 即可
static {
//1.加載配置文件,創建entityManagerFactory
factory = Persistence.createEntityManagerFactory("myJpa");
}
4.3 EntityManager
在 JPA 規範中, EntityManager是完成持久化操作的核心對象。實體類作爲普通 java對象,只有在調用 EntityManager將其持久化後纔會變成持久化對象。EntityManager對象在一組實體類與底層數據源之間進行 O/R 映射的管理。它可以用來管理和更新 Entity Bean, 根椐主鍵查找 Entity Bean, 還可以通過JPQL語句查詢實體。
我們可以通過調用EntityManager的方法完成獲取事務,以及持久化數據庫的操作
方法說明:
getTransaction : 獲取事務對象
persist : 保存操作
merge : 更新操作
remove : 刪除操作
find/getReference : 根據id查詢
4.4 EntityTransaction
在 JPA 規範中, EntityTransaction是完成事務操作的核心對象,對於EntityTransaction在我們的java代碼中承接的功能比較簡單
begin:開啓事務
commit:提交事務
rollback:回滾事務
五、抽取JPAUtil工具類
package cn.itcast.dao;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public final class JPAUtil {
// JPA的實體管理器工廠:相當於Hibernate的SessionFactory
private static EntityManagerFactory em;
// 使用靜態代碼塊賦值
static {
// 注意:該方法參數必須和persistence.xml中persistence-unit標籤name屬性取值一致
em = Persistence.createEntityManagerFactory("myPersistUnit");
}
/**
* 使用管理器工廠生產一個管理器對象
*
* @return
*/
public static EntityManager getEntityManager() {
return em.createEntityManager();
}
}
六、使用JPA完成CRUD
package cn.itcast.test;
import cn.itcast.domain.Customer;
import cn.itcast.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaTest {
/**
* 測試jpa的保存
* 案例:保存一個客戶到數據庫中
* Jpa的操作步驟
* 1.加載配置文件創建工廠(實體管理器工廠)對象
* 2.通過實體管理器工廠獲取實體管理器
* 3.獲取事務對象,開啓事務
* 4.完成增刪改查操作
* 5.提交事務(回滾事務)
* 6.釋放資源
*/
@Test
public void testSave() {
// //1.加載配置文件創建工廠(實體管理器工廠)對象
// EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
// //2.通過實體管理器工廠獲取實體管理器
// EntityManager em = factory.createEntityManager();
EntityManager em = JpaUtils.getEntityManager();
//3.獲取事務對象,開啓事務
EntityTransaction tx = em.getTransaction(); //獲取事務對象
tx.begin();//開啓事務
//4.完成增刪改查操作:保存一個客戶到數據庫中
Customer customer = new Customer();
customer.setCustName("xxx");
customer.setCustIndustry("xx");
//保存,
em.persist(customer); //保存操作
//5.提交事務
tx.commit();
//6.釋放資源
em.close();
// factory.close();
}
/**
* 根據id查詢客戶
* 使用find方法查詢:
* 1.查詢的對象就是當前客戶對象本身
* 2.在調用find方法的時候,就會發送sql語句查詢數據庫
*
* 立即加載
*
*
*/
@Test
public void testFind() {
//1.通過工具類獲取entityManager
EntityManager entityManager = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3.增刪改查 -- 根據id查詢客戶
/**
* find : 根據id查詢數據
* class:查詢數據的結果需要包裝的實體類類型的字節碼
* id:查詢的主鍵的取值
*/
Customer customer = entityManager.find(Customer.class, 1l);
// System.out.print(customer);
//4.提交事務
tx.commit();
//5.釋放資源
entityManager.close();
}
/**
* 根據id查詢客戶
* getReference方法
* 1.獲取的對象是一個動態代理對象
* 2.調用getReference方法不會立即發送sql語句查詢數據庫
* * 當調用查詢結果對象的時候,纔會發送查詢的sql語句:什麼時候用,什麼時候發送sql語句查詢數據庫
*
* 延遲加載(懶加載)
* * 得到的是一個動態代理對象
* * 什麼時候用,什麼使用纔會查詢
*/
@Test
public void testReference() {
//1.通過工具類獲取entityManager
EntityManager entityManager = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3.增刪改查 -- 根據id查詢客戶
/**
* getReference : 根據id查詢數據
* class:查詢數據的結果需要包裝的實體類類型的字節碼
* id:查詢的主鍵的取值
*/
Customer customer = entityManager.getReference(Customer.class, 1l);
System.out.print(customer);
//4.提交事務
tx.commit();
//5.釋放資源
entityManager.close();
}
/**
* 刪除客戶的案例
*
*/
@Test
public void testRemove() {
//1.通過工具類獲取entityManager
EntityManager entityManager = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3.增刪改查 -- 刪除客戶
//i 根據id查詢客戶
Customer customer = entityManager.find(Customer.class,1l);
//ii 調用remove方法完成刪除操作
entityManager.remove(customer);
//4.提交事務
tx.commit();
//5.釋放資源
entityManager.close();
}
/**
* 更新客戶的操作
* merge(Object)
*/
@Test
public void testUpdate() {
//1.通過工具類獲取entityManager
EntityManager entityManager = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3.增刪改查 -- 更新操作
//i 查詢客戶
Customer customer = entityManager.find(Customer.class,1l);
//ii 更新客戶
customer.setCustIndustry("xxx");
entityManager.merge(customer);
//4.提交事務
tx.commit();
//5.釋放資源
entityManager.close();
}
}
七、JPQL實現CRUD操作
JPQL全稱Java Persistence Query Language
基於首次在EJB2.0中引入的EJB查詢語言(EJB QL),Java持久化查詢語言(JPQL)是一種可移植的查詢語言,旨在以面向對象表達式語言的表達式,將SQL語法和簡單查詢語義綁定在一起。
其特徵與原生SQL語句類似,並且完全面向對象,通過類名和屬性訪問,而不是表名和表的屬性。
代碼如下:
package cn.itcast.test;
import cn.itcast.domain.Customer;
import cn.itcast.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import java.util.List;
/**
* 測試jqpl查詢
*/
public class JpqlTest {
/**
* 查詢全部
* jqpl:from cn.itcast.domain.Customer
* sql:SELECT * FROM cst_customer
*/
@Test
public void testFindAll() {
//1.獲取entityManager對象
EntityManager em = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查詢全部
String jpql = "from Customer ";
Query query = em.createQuery(jpql);//創建Query查詢對象,query對象纔是執行jqpl的對象
//發送查詢,並封裝結果集
List list = query.getResultList();
for (Object obj : list) {
System.out.print(obj);
}
//4.提交事務
tx.commit();
//5.釋放資源
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 testOrders() {
//1.獲取entityManager對象
EntityManager em = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查詢全部
String jpql = "from Customer order by custId desc";
Query query = em.createQuery(jpql);//創建Query查詢對象,query對象纔是執行jqpl的對象
//發送查詢,並封裝結果集
List list = query.getResultList();
for (Object obj : list) {
System.out.println(obj);
}
//4.提交事務
tx.commit();
//5.釋放資源
em.close();
}
/**
* 使用jpql查詢,統計客戶的總數
* sql:SELECT COUNT(cust_id) FROM cst_customer
* jpql:select count(custId) from Customer
*/
@Test
public void testCount() {
//1.獲取entityManager對象
EntityManager em = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查詢全部
//i.根據jpql語句創建Query查詢對象
String jpql = "select count(custId) from Customer";
Query query = em.createQuery(jpql);
//ii.對參數賦值
//iii.發送查詢,並封裝結果
/**
* getResultList : 直接將查詢結果封裝爲list集合
* getSingleResult : 得到唯一的結果集
*/
Object result = query.getSingleResult();
System.out.println(result);
//4.提交事務
tx.commit();
//5.釋放資源
em.close();
}
/**
* 分頁查詢
* sql:select * from cst_customer limit 0,2
* jqpl : from Customer
*/
@Test
public void testPaged() {
//1.獲取entityManager對象
EntityManager em = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查詢全部
//i.根據jpql語句創建Query查詢對象
String jpql = "from Customer";
Query query = em.createQuery(jpql);
//ii.對參數賦值 -- 分頁參數
//起始索引
query.setFirstResult(0);
//每頁查詢的條數
query.setMaxResults(2);
//iii.發送查詢,並封裝結果
/**
* getResultList : 直接將查詢結果封裝爲list集合
* getSingleResult : 得到唯一的結果集
*/
List list = query.getResultList();
for(Object obj : list) {
System.out.println(obj);
}
//4.提交事務
tx.commit();
//5.釋放資源
em.close();
}
/**
* 條件查詢
* 案例:查詢客戶名稱以‘傳智播客’開頭的客戶
* sql:SELECT * FROM cst_customer WHERE cust_name LIKE ?
* jpql : from Customer where custName like ?
*/
@Test
public void testCondition() {
//1.獲取entityManager對象
EntityManager em = JpaUtils.getEntityManager();
//2.開啓事務
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查詢全部
//i.根據jpql語句創建Query查詢對象
String jpql = "from Customer where custName like ? ";
Query query = em.createQuery(jpql);
//ii.對參數賦值 -- 佔位符參數
//第一個參數:佔位符的索引位置(從1開始),第二個參數:取值
query.setParameter(1,"傳智播客%");
//iii.發送查詢,並封裝結果
/**
* getResultList : 直接將查詢結果封裝爲list集合
* getSingleResult : 得到唯一的結果集
*/
List list = query.getResultList();
for(Object obj : list) {
System.out.println(obj);
}
//4.提交事務
tx.commit();
//5.釋放資源
em.close();
}
}