1.JPA的相關概念
Java Persistence API。是SUN公司推出的一套基於ORM的規範。hibernate框架中提供了JPA的實現。
JPA通過JDK 5.0註解或XML描述對象-關係表的映射關係,並將運行期的實體對象持久化到數據庫中
1.1 JPA的優勢
- 1.標準化
- 2.容器級特性的支持
- 3.簡單方便
- 4.查詢能力
- 5.高級特性
1.2 學習JPA要明確的
- A、JPA是一套ORM規範,hibernate實現了JPA規範
- B、hibernate中有自己的獨立ORM操作數據庫方式,也有JPA規範實現的操作數據庫方式。
- C、在數據庫增刪改查操作中,我們hibernate和JPA的操作都要會。
2.JPA入門
導包
package cn.itcast.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 客戶實體類
*
* @author Administrator
*
*/
@Entity //表示當前類爲實例類
@Table(name="cst_customer") //建立類和表之間的關係
public class Customer implements Serializable {
@Id//表示主鍵
@GeneratedValue(strategy=GenerationType.IDENTITY)//主鍵生成策略
@Column(name="cust_id")//建立類中屬性和表中字段的映射
private Long custId;// 客戶ID
@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
作用:定當前類是實體類。寫上此註解用於在創建SessionFactory/EntityManager時,加載映射配置。 - @Table
作用:指定實體類和表之間的對應關係。
屬性:name:指定數據庫表的名稱 - @Id
指定當前字段是主鍵。 - @GeneratedValue
作用:指定主鍵的生成方式。
屬性:strategy :指定主鍵生成策略。JPA支持四種生成策略。 - @Column
作用:指定實體類屬性和數據庫表之間的對應關係
屬性: name:指定數據庫表的列名稱。
2.創建配置文件
在src下建立META-INF文件夾,並在META-INF文件夾下建立xml文件:persistence
要使用JPA,必須滿足兩個條件:
- 1、在類的根目錄(src)下,創建META-INF文件夾
- 2、在META-INF文件夾中建立名爲”persistence.xml”文件
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<!-- persistence-unit 頂層標籤 配置JPA的單元。
屬性:
name: 用於定義持久化單元的名字 (name必選,空值也合法);
transaction-type: 指定事務類型(可以省略)
取值:
JTA(默認值)
RESOURCE_LOCAL(數據庫本地的事務)
RESOURCE_LOCAL是數據庫級別的事務,只能針對一種數據庫,不支持分佈式的事務
-->
<persistence-unit name="myPersistenceUnit" transaction-type="RESOURCE_LOCAL">
<!-- 配置JPA規範實現的提供商 (可以省略) -->
<!-- HibernatePersistenceProvider是javax.persistence.PersistenceProvider接口的一個實現類 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 配置創建JPA操作對象時要掃描的類,用於解析註解,生成配置 (可以省略)-->
<class>cn.itcast.domain.Customer</class>
<!-- 配置Hibernate中的信息 -->
<properties>
<!-- 數據庫的連接信息 -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="root"/>
<!-- 指定方言 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<!-- 是否顯示SQL語句 -->
<property name="hibernate.show_sql" value="true"/>
<!-- 是否格式化SQL語句 -->
<property name="hibernate.format_sql" value="true"/>
<!-- 生成DDL的策略 -->
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
3.編寫工具類,用於獲取JPA的操作數據對象
JPAUtil工具類:
package cn.itcast.util;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.SynchronizationType;
/**
* JPA的工具類:
* 加載JPA的persistence.xml文件,用於生成工廠
* 調用工廠中的方法,創建操作數據庫的對象,並返回
*
* @author Administrator
*
*/
public class JPAUtil {
//JPA的實體管理器工廠(相當於Hibernate的SessionFactory)
private static EntityManagerFactory factory ;
//使用靜態代碼塊賦值
static{
try{
factory = Persistence.createEntityManagerFactory("myPersistenceUnit");
}catch(Exception ex){
ex.printStackTrace();
}
}
/**
* 使用管理器工廠生產一個管理器對象(獲取一個新的實體管理對象)
* @return
*/
public static EntityManager getEntityManager(){
//相當於Hibernate中的openSession()
return factory.createEntityManager();
}
}
2.1 常用註解說明
- @Entity
作用:指定當前類是實體類。寫上此註解用於在創建SessionFactory/EntityManager時,加載映射配置。 - @Table
作用:指定實體類和表之間的對應關係。
屬性:name:指定數據庫表的名稱 - @Id
指定當前字段是主鍵。 - @GeneratedValue
作用:指定主鍵的生成方式。
屬性:strategy :指定主鍵生成策略。JPA支持四種生成策略。 - @Column
作用:指定實體類屬性和數據庫表之間的對應關係
屬性:
name:指定數據庫表的列名稱。
unique:是否唯一
nullable:是否可以爲空
inserttable:是否可以插入
updateable:是否可以更新
columnDefinition: 定義建表時創建此列的DDL
secondaryTable: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字。
2.2 主鍵生存策略
通過annotation(註解)來映射hibernate實體的,基於annotation的hibernate主鍵標識爲@Id, 其生成規則由@GeneratedValue設定的。這裏的@id和@GeneratedValue都是JPA的標準用法。JPA提供的四種標準用法爲IDENTITY,SEQUENCE,AUTO,TABLE。具體說明如下:
1.IDENTITY
IDENTITY:主鍵由數據庫自動生成。
用法:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long custId;
2.SEQUENCE
SEQUENCE:根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列修改時遇到的問題
用法:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator="payablemoney_seq")
@SequenceGenerator(name="payablemoney_seq", sequenceName="seq_payment")
說明:
@SequenceGenerator源碼中的定義
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface SequenceGenerator {
String name();
String sequenceName() default "";
int initialValue() default 0;
int allocationSize() default 50;
}
name:表示該表主鍵生成策略的名稱,它被引用在@GeneratedValue中設置的”generator”值中。
sequenceName:屬性表示生成策略用到的數據庫序列名稱。
initialValue:表示主鍵初識值,默認爲0。
allocationSize:表示每次主鍵值增加的大小,例如設置1,則表示每次插入新記錄後自動加1,默認爲50。
3.AUTO
AUTO:主鍵由程序控制
用法:
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
4.TABLE
TABLE:使用一個特定的數據庫表格來保存主鍵
用法:
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator="pk_gen")
@TableGenerator(name = "pk_gen",
table="tb_generator",
pkColumnName="gen_name",
valueColumnName="gen_value",
pkColumnValue="PAYABLEMOENY_PK",
allocationSize=1
)
這裏應用表tb_generator,定義爲 :
CREATE TABLE tb_generator (
id NUMBER NOT NULL,
gen_name VARCHAR2(255) NOT NULL,
gen_value NUMBER NOT NULL,
PRIMARY KEY(id)
)
插入紀錄,供生成主鍵使用:
INSERT INTO tb_generator(id, gen_name, gen_value)VALUES (1,PAYABLEMOENY_PK', 1);
在主鍵生成後,這條紀錄的value值,按allocationSize遞增。
@TableGenerator的定義:
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface TableGenerator {
String name();
String table() default "";
String catalog() default "";
String schema() default "";
String pkColumnName() default "";
String valueColumnName() default "";
String pkColumnValue() default "";
int initialValue() default 0;
int allocationSize() default 50;
UniqueConstraint[] uniqueConstraints() default {};
}
其中屬性說明:
name:
表示該表主鍵生成策略的名稱,它被引用在@GeneratedValue中設置的“generator”值中。
table:
表示表生成策略所持久化的表名,例如,這裏表使用的是數據庫中的“tb_generator”。
catalog和schema:
具體指定表所在的目錄名或是數據庫名。
pkColumnName:
屬性的值表示在持久化表中,該主鍵生成策略所對應鍵值的名稱。例如在“tb_generator”中將“gen_name”作爲主鍵的鍵值
valueColumnName:
屬性的值表示在持久化表中,該主鍵當前所生成的值,它的值將會隨着每次創建累加。例如,在“tb_generator”中將“gen_value”作爲主鍵的值
pkColumnValue:
屬性的值表示在持久化表中,該生成策略所對應的主鍵。例如在“tb_generator”表中,將“gen_name”的值爲“CUSTOMER_PK”。
initialValue:
表示主鍵初識值,默認爲0。
allocationSize:
表示每次主鍵值增加的大小,例如設置成1,則表示每次創建新記錄後自動加1,默認爲50。
UniqueConstraint:
與@Table標記中的用法類似。
2.3 JPA的CRUD操作
1.保存
@Test
public void testSave() {
// 創建客戶對象
Customer c = new Customer();
c.setCustName("JPA-傳智黑馬2");
EntityManager em = null;// JPA數據庫操作對象
EntityTransaction tx = null;// JPA事務對象
try {
// 第1步:獲取操作數據庫對象(相當於Hibernate中的Session)
em = JPAUtil.getEntityManager();
// 第2步:獲取JPA中的事務對象 並開啓事務
tx = em.getTransaction();// 此時沒有開啓事務
tx.begin();// 開啓事務
// 第3步:執行操作
em.persist(c);
// 第4步:提交事務
tx.commit();
} catch (Exception ex) {
tx.rollback();// 事務回滾
ex.printStackTrace();
} finally {
// 第5步:釋放資源
em.close();
}
}
2.修改
@Test
public void testUpdate() {
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
// 第1步:獲取操作數據庫對象
em = JPAUtil.getEntityManager();
// 第2步:獲取JPA中的事務對象 並開啓事務
tx = em.getTransaction();// 此時沒有開啓事務
tx.begin();// 開啓事務
// 第3步:執行操作
Customer c = em.find(Customer.class, 97L);
c.setCustName("傳智專修學院");
// 第4步:提交事務
tx.commit();//使用JPA中快照機制實現更新
} catch (Exception ex) {
tx.rollback();// 事務回滾
ex.printStackTrace();
} finally {
// 第5步:釋放資源
em.close();
}
}
// 第二種實現修改的方式:利用EntityManager對象中的merge()方法
@Test
public void testUpdate2() {
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
// 第1步:獲取操作數據庫對象
em = JPAUtil.getEntityManager();
// 第2步:獲取JPA中的事務對象 並開啓事務
tx = em.getTransaction();// 此時沒有開啓事務
tx.begin();// 開啓事務
// 第3步:執行操作
Customer c = em.find(Customer.class, 97L);
c.setCustName("傳智專修學院");
em.merge(c);
// 第4步:提交事務
tx.commit();//使用JPA中快照機制實現更新
} catch (Exception ex) {
tx.rollback();// 事務回滾
ex.printStackTrace();
} finally {
// 第5步:釋放資源
em.close();
}
}
3.刪除
@Test
public void testDelete() {
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
// 第1步:獲取操作數據庫對象
em = JPAUtil.getEntityManager();
// 第2步:獲取JPA中的事務對象 並開啓事務
tx = em.getTransaction();// 此時沒有開啓事務
tx.begin();// 開啓事務
// 第3步:執行操作
Customer c = em.find(Customer.class, 94L);//先查詢
em.remove(c);//刪除
// 第4步:提交事務
tx.commit();//使用JPA中快照機制實現更新
} catch (Exception ex) {
tx.rollback();// 事務回滾
ex.printStackTrace();
} finally {
// 第5步:釋放資源
em.close();
}
}
4.查詢一個
@Test
public void testGetOne(){
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
// 第1步:獲取操作數據庫對象
em = JPAUtil.getEntityManager();
// 第2步:獲取JPA中的事務對象 並開啓事務
tx = em.getTransaction();// 此時沒有開啓事務
tx.begin();// 開啓事務
// 第3步:執行操作
Customer c = em.find(Customer.class, 97L);
// 第4步:提交事務
tx.commit();
System.out.println(c);
} catch (Exception e) {
tx.rollback();//事務回滾
e.printStackTrace();
}finally{
// 第5步:釋放資源
em.close();
}
}
/**
* 查詢一個客戶(使用延遲加載的策略)
*/
@Test
public void testLoadOne(){
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
// 第1步:獲取操作數據庫對象
em = JPAUtil.getEntityManager();
// 第2步:獲取JPA中的事務對象 並開啓事務
tx = em.getTransaction();// 此時沒有開啓事務
tx.begin();// 開啓事務
// 第3步:執行操作
Customer c = em.getReference(Customer.class, 97L);
// 第4步:提交事務
tx.commit();
System.out.println(c);
} catch (Exception e) {
tx.rollback();//事務回滾
e.printStackTrace();
}finally{
// 第5步:釋放資源
em.close();
}
}
5.查詢所有
@Test
public void testFindAll(){
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
// 第1步:獲取操作數據庫對象
em = JPAUtil.getEntityManager();
// 第2步:獲取JPA中的事務對象 並開啓事務
tx = em.getTransaction();// 此時沒有開啓事務
tx.begin();// 開啓事務
// 第3步:執行操作 Query對象不是之前Hibernate中的,而是屬於JPA下的對象(javax.persistence.Query)
/*使用EntityManager對象下的createQuery()方法獲得Query對象
查詢所有時,不能使用*號,而是需要在from關鍵字後面的類名上加別名
例如: select c from Customer c
*/
Query query =em.createQuery("select c from Customer c where custName like ?");
query.setParameter(1, "%傳智%");
List list = query.getResultList();
//第4步:提交事務
tx.commit();
for(Object obj : list){
System.out.println(obj);
}
}catch(Exception e){
tx.rollback();//事務回滾
e.printStackTrace();
}finally{
// 第5步:釋放資源
em.close();
}
}
獲取結果集常用方法:
getResultList() : 查詢結果是一個List集合
getSingleResult() : 查詢結果是一個對象
3.JPA一對多操作
3.1 常用註解
@OneToMany
- 作用:建立一對多的關係映射
- 屬性:
targetEntity:指定一對多中屬於多的那方實體類的字節碼
mappedBy:指定從表實體類中引用主表對象的名稱。
cascade:指定要使用的級聯操作
fetch:指定是否採用延遲加載
orphanRemoval:是否使用孤兒刪除
@ManyToOne
- 作用:建立多對一的關係
- 屬性:
targetEntity:指定一對多中屬於一的那方實體類的字節碼
cascade:指定要使用的級聯操作
fetch:指定是否採用延遲加載
optional:關聯是否可選。如果設置爲false,則必須始終存在非空關係。
@JoinColumn
- 作用:用於定義主鍵字段和外鍵字段的對應關係。
- 屬性:
name:指定外鍵字段的名稱
referencedColumnName:指定引用主表的主鍵字段名稱
unique:是否唯一。默認值不唯一
nullable:是否允許爲空。默認值允許。
insertable:是否允許插入。默認值允許。
updatable:是否允許更新。默認值允許。
columnDefinition:列的定義信息。
3.2 配置代碼
Customer配置,代碼如下:
package cn.itcast.domain;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
* 客戶實體類
*
* @author Jackie
*
*/
@Entity //表示當前類爲實例類
@Table(name="cst_customer") //建立類和表之間的關係
public class Customer implements Serializable {
@Id//表示主鍵
@GeneratedValue(strategy=GenerationType.IDENTITY)//主鍵生成策略
@Column(name="cust_id")//建立類中屬性和表中字段的映射
private Long custId;// 客戶ID
@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;// 客戶聯繫方式
// 一對多映射
@OneToMany(targetEntity=LinkMan.class,mappedBy="customer")
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
public Set<LinkMan> getLinkmans() {
return linkmans;
}
public void setLinkmans(Set<LinkMan> linkmans) {
this.linkmans = linkmans;
}
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 + "]";
}
}
LinkMan配置,代碼如下:
package cn.itcast.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* 聯繫人實體類
*
* @author Administrator
*
*/
@Entity
@Table(name="cst_linkman")
public class LinkMan implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="lkm_id")
private Long lkmId;//聯繫人編號(主鍵)
@Column(name="lkm_name")
private String lkmName;//聯繫人姓名
@Column(name="lkm_gender")
private String lkmGender;//聯繫人性別
@Column(name="lkm_phone")
private String lkmPhone;//聯繫人辦公電話
@Column(name="lkm_mobile")
private String lkmMobile;//聯繫人手機
@Column(name="lkm_email")
private String lkmEmail;//聯繫人郵箱
@Column(name="lkm_position")
private String lkmPosition;//聯繫人職位
@Column(name="lkm_memo")
private String lkmMemo;//聯繫人備註
//多對一映射
@ManyToOne(targetEntity=Customer.class)
@JoinColumn(name="lkm_cust_id", referencedColumnName="cust_id")
private Customer customer;//對應聯繫人表中的外鍵
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Long getLkmId() {
return lkmId;
}
public void setLkmId(Long lkmId) {
this.lkmId = lkmId;
}
public String getLkmName() {
return lkmName;
}
public void setLkmName(String lkmName) {
this.lkmName = lkmName;
}
public String getLkmGender() {
return lkmGender;
}
public void setLkmGender(String lkmGender) {
this.lkmGender = lkmGender;
}
public String getLkmPhone() {
return lkmPhone;
}
public void setLkmPhone(String lkmPhone) {
this.lkmPhone = lkmPhone;
}
public String getLkmMobile() {
return lkmMobile;
}
public void setLkmMobile(String lkmMobile) {
this.lkmMobile = lkmMobile;
}
public String getLkmEmail() {
return lkmEmail;
}
public void setLkmEmail(String lkmEmail) {
this.lkmEmail = lkmEmail;
}
public String getLkmPosition() {
return lkmPosition;
}
public void setLkmPosition(String lkmPosition) {
this.lkmPosition = lkmPosition;
}
public String getLkmMemo() {
return lkmMemo;
}
public void setLkmMemo(String lkmMemo) {
this.lkmMemo = lkmMemo;
}
@Override
public String toString() {
return "LinkMan [lkmId=" + lkmId + ", lkmName=" + lkmName
+ ", lkmGender=" + lkmGender + ", lkmPhone=" + lkmPhone
+ ", lkmMobile=" + lkmMobile + ", lkmEmail=" + lkmEmail
+ ", lkmPosition=" + lkmPosition + ", lkmMemo=" + lkmMemo + "]";
}
}
3.3 增刪改操作
1.保存操作
@Test
public void testOneToMany() {
// 創建客戶對象
Customer c = new Customer();// 瞬時態
c.setCustName("TBD雲集中心");
c.setCustLevel("VIP客戶");
c.setCustSource("網絡");
c.setCustIndustry("商業辦公");
c.setCustAddress("金沙江路1340弄");
c.setCustPhone("021-84389340");
// 創建聯繫人對象
LinkMan lkm = new LinkMan();// 瞬時態
lkm.setLkmName("TBD聯繫人");
lkm.setLkmGender("男");
lkm.setLkmMobile("13811111111");
lkm.setLkmPhone("021-34785348");
lkm.setLkmEmail("[email protected]");
lkm.setLkmPosition("業務經理");
lkm.setLkmMemo("做事比較保守");
lkm.setCustomer(c);//建立單向關係 (沒有建立雙向關係)
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
em = JPAUtil.getEntityManager();//獲取JPA操作對象
tx = em.getTransaction();//獲取JPA事務對象(事務未開啓)
tx.begin();//開啓事務
//保存操作 先保存客戶,再保存聯繫人
em.persist(c);//保存客戶
em.persist(lkm);//保存聯繫人
//提交事務
tx.commit();
} catch (Exception e) {
tx.rollback();//事務回滾
e.printStackTrace();
}finally{
em.close();//釋放資源
}
}
執行程序後:
數據庫:
案例:使用雙向關係的保存客戶代碼:
package cn.itcast.test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import org.junit.Test;
import cn.itcast.domain.Customer;
import cn.itcast.domain.LinkMan;
import cn.itcast.util.JPAUtil;
/**
* 測試類 用於測試JPA一對多映射關係
*
* @author Administrator
*/
public class JPATest2 {
/**
* 保存客戶 (一對多映射關係)
* 保存一個客戶、一個聯繫人
*/
@Test
public void testOneToManySave() {
// 創建客戶對象
Customer c = new Customer();// 瞬時態
c.setCustName("TBD雲集中心2");
c.setCustLevel("VIP客戶");
c.setCustSource("網絡");
c.setCustIndustry("商業辦公");
c.setCustAddress("金沙江路1340弄");
c.setCustPhone("021-84389340");
// 創建聯繫人對象
LinkMan lkm = new LinkMan();// 瞬時態
lkm.setLkmName("TBD聯繫人2");
lkm.setLkmGender("女");
lkm.setLkmMobile("13800000000");
lkm.setLkmPhone("021-34785348");
lkm.setLkmEmail("[email protected]");
lkm.setLkmPosition("業務經理");
lkm.setLkmMemo("沒有什麼好說的");
//建立一對多雙向關聯關係
lkm.setCustomer(c);
c.getLinkmans().add(lkm);
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
em = JPAUtil.getEntityManager();//獲取JPA操作對象
tx = em.getTransaction();//獲取JPA事務對象(事務未開啓)
tx.begin();//開啓事務
//保存操作 先保存客戶,再保存聯繫人
em.persist(c);//保存客戶
em.persist(lkm);//保存聯繫人
//提交事務
tx.commit();
} catch (Exception e) {
tx.rollback();//事務回滾
e.printStackTrace();
}finally{
em.close();//釋放資源
}
}
}
執行程序後:
JPA註解的配置方式:不涉及多一條update語句的問題
結論:在JPA環境下,雙向關聯後,進行保存時,不會出現多餘update語句
2.修改操作
@Test
public void testOnToManyUpdate(){
// 創建聯繫人對象
LinkMan lkm = new LinkMan();// 瞬時態
lkm.setLkmName("TBD聯繫人3");
lkm.setLkmGender("男");
lkm.setLkmMobile("13800138000");
lkm.setLkmPhone("021-34785348");
lkm.setLkmEmail("[email protected]");
lkm.setLkmPosition("業務員");
lkm.setLkmMemo("還可以");
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
em = JPAUtil.getEntityManager();//獲取JPA操作對象
tx = em.getTransaction();//獲取JPA事務對象(事務未開啓)
tx.begin();//開啓事務
//根據id查詢客戶
Customer c1 = em.find(Customer.class, 5L);
//建立雙向關聯關係
c1.getLinkmans().add(lkm);
lkm.setCustomer(c1);
//更新操作有兩種方案:
//第一種使用JPA提交事務自動更新機制(JPA中的快照)
//第二種使用EntityManager對象中的merge()方法
//此處使用提交事務自動更新機制
tx.commit();
} catch (Exception e) {
tx.rollback();//事務回滾
e.printStackTrace();
}finally{
em.close();//釋放資源
}
}
執行程序後,發現沒有更新成功。
解決方案:設置級聯更新
案例:使用單向關聯實現客戶更新
@Test
public void testOnToManyUpdate(){
// 創建聯繫人對象
LinkMan lkm = new LinkMan();// 瞬時態
lkm.setLkmName("TBD聯繫人3");
lkm.setLkmGender("男");
lkm.setLkmMobile("13800138000");
lkm.setLkmPhone("021-34785348");
lkm.setLkmEmail("[email protected]");
lkm.setLkmPosition("業務員");
lkm.setLkmMemo("還可以");
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
em = JPAUtil.getEntityManager();//獲取JPA操作對象
tx = em.getTransaction();//獲取JPA事務對象(事務未開啓)
tx.begin();//開啓事務
//根據id查詢客戶
Customer c1 = em.find(Customer.class, 5L);
//建立單向關聯關係
lkm.setCustomer(c1);
//更新操作有兩種方案:
//第一種使用JPA提交事務自動更新機制(JPA中的快照)
//第二種使用EntityManager對象中的merge()方法
em.merge(lkm);//此處使用merge()方法
tx.commit();
} catch (Exception e) {
tx.rollback();//事務回滾
e.printStackTrace();
}finally{
em.close();//釋放資源
}
}
不需要設置級聯,因爲我們只需要操作linkman表,不會去改動客戶表
3.刪除操作
需求:刪除客戶
/**
* 刪除客戶
*
* 刪除操作
* 刪除從表數據:可以隨時任意刪除。
* 刪除主表數據:
* 有從表數據引用
* 1、不能刪除
* 2、如果還想刪除,使用級聯刪除
* 沒有從表數據引用:隨便刪
*
* 注意:在實際開發中請慎用級聯刪除
*/
@Test
public void testOneToManyDelete(){
EntityManager em = null;//JPA數據庫操作對象
EntityTransaction tx = null;//JPA事務對象
try {
em = JPAUtil.getEntityManager();//獲取JPA操作對象
tx = em.getTransaction();//獲取JPA事務對象(事務未開啓)
tx.begin();//開啓事務
//查詢客戶
Customer c = em.find(Customer.class,5L);
em.remove(c);
tx.commit();//提交事務
}catch(Exception e){
tx.rollback();//事務回滾
e.printStackTrace();
}finally{
em.close();//釋放資源
}
}
執行程序後: 在未設置級聯的情況下,僅刪除了客戶表中的數據
數據庫: 主表中數據已刪除,從表(外鍵表)中的數據未刪除
案例:使用級聯刪除
4.JPA多對多操作
4.1 常用註解
- @ManyToMany
作用:用於映射多對多關係
屬性:- mappedBy:
- cascade:配置級聯操作。
- fetch:配置是否採用延遲加載。
- targetEntity:配置目標的實體類。映射多對多的時候不用寫。
- @JoinTable
作用:針對中間表的配置
屬性:- name:配置中間表的名稱
- joinColumns:中間表的外鍵字段關聯當前實體類所對應表的主鍵字段
- inverseJoinColumn:中間表的外鍵字段關聯對方表的主鍵字段
- @JoinColumn
作用:用於定義主鍵字段和外鍵字段的對應關係。
屬性:- name:指定外鍵字段的名稱
- referencedColumnName:指定引用主表的主鍵字段名稱
- unique:是否唯一。默認值不唯一
- nullable:是否允許爲空。默認值允許。
- insertable:是否允許插入。默認值允許。
- updatable:是否允許更新。默認值允許。
- columnDefinition:列的定義信息。