二、JPA中的第一個程序java
三、JPA中經常使用的註解(重點)mysql
四、JPA中的CRUDsql
五、JPA中的多表映射的配置數據庫
六、JPA中多表的增刪改緩存
七、對象導航查詢網絡
何時JPA?JPA它是一套Java持久化規範。那麼JPA和咱們前面三天所學的Hibernate有什麼關係呢?session
今天咱們主要學習Hibernate中實現了JPA規範的操做數據庫的方式。app
a、JPA是一套ORM規範,hibernate實現了JPA規範dom
b、hibernate中有本身的獨立ORM操做數據庫方式,也有JPA規範實現的操做數據庫方式。ide
c、在數據庫增刪改查操做中,咱們hibernate和JPA的操做都要會。
要搭建一個JPA的開發環境,除了須要導入前面三天所用的Hibernate的jar包之外,還須要導入hibernate-entitymanager.jar包,該jar包能夠從以下路徑找到:
完整的jar包以下圖所示:
注意:今天咱們主要學習JPA規範,可是僅僅只有規範是不行的,還得有規範的實現才行,因此才須要導入Hibernate相關的jar包,由於Hibernate就是JPA規範的實現。
建立Customer實體類:
在實體類上加上JPA註解,取代傳統的hbm.xml文件的功能。須要使用到的註解有:
/**
* 客戶實體類
* @author kevin
*/
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name="cust_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
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 + "]";
}
}
1、在src下新建META-INF文件夾,新建persistence.xml文件。
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 name="myPersistUnit">
<!-- 指定JPA規範的實現:Hibernate的實現 (可省略)-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 指定要被掃描到的實體類 (可省略)-->
<class>cn.itcast.domain.Customer</class>
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/hibernate_itheima20"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="123456"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"></property>
<property name="hibernate.c3p0.max_size" value="20"></property>
<property name="hibernate.c3p0.min_size" value="5"></property>
<property name="hibernate.c3p0.timeout" value="5000"></property>
<property name="hibernate.c3p0.idle_test_period" value="3000"></property>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
建立JPAUtils工具類,封裝獲取EntityManager的方法,等價於封裝HibernateUtils工具類用於獲取Session:
JPAUtils的內容以下:
/**
* 用於獲取JPA操做數據庫對象的工具類
* @author kevin
*/
public class JPAUtils {
//JPA的實體管理器工廠:至關於Hibernte的SessionFactory
private static final EntityManagerFactory em;
//使用靜態代碼塊賦值
static{
//建立JPA的實體管理器工廠:該方法參數必須和persistence.xml中persistence-unit標籤的name屬性一致
em = Persistence.createEntityManagerFactory("myPersistUnit");
}
/**
* 獲取實例管理器的工具方法
* @return
*/
public static EntityManager getEntityManager(){
return em.createEntityManager();
}
}
用到的API的解釋:
EntityManagerFactory:它是JPA規範中生產EntityManager的工廠,要保證一個工程中只有一個EntityManagerFactory。做用等價於SessionFactory;
EntityManager:實體管理器,用來作CRUD的,做用等價於Session;
Persistence:JPA規範中的工具類,用於獲取EntityManagerFactory的;
建立單元測試類TestJPA:
TestJPA內容以下:
/**
* 測試JPA註解裏的保存
*/
@Test
public void test1(){
Customer customer = new Customer();
customer.setCustName("蔚來汽車");
customer.setCustIndustry("人工智能");
customer.setCustSource("網絡營銷");
customer.setCustLevel("VIP客戶");
customer.setCustAddress("北京");
customer.setCustPhone("0108650123");
//獲取JPA中實體管理器
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
//保存對象
em.persist(customer);
//提交事務
tx.commit();
//釋放資源
em.close();
}
用到的API的解釋:
EntityTransaction:JPA規範中的事務接口,用來管理JPA規範中的事務,做用等價於Transaction;
EntityManager.persist():用來保存對象的;做用等價於session.save();
@Entity
做用:指定當前類是實體類。寫上此註解用於在建立SessionFactory/EntityManager時,加載映射配置。
@Table
做用:指定實體類和表之間的對應關係。
屬性:
name:指定數據庫表的名稱
@Id
做用:指定當前字段是主鍵。
@GeneratedValue
做用:指定主鍵的生成方式。JPA的主鍵生成方式詳解見2.4小節的說明。
屬性:
strategy :指定主鍵生成策略。JPA支持四種生成策略,具體介紹看3.2小節。
@Column
做用:指定實體類屬性和數據庫表裏列之間的對應關係
屬性:
name:指定數據庫表的列名稱。
unique:是否惟一
nullable:是否能夠爲空
inserttable:是否能夠插入
updateable:是否能夠更新
columnDefinition: 定義建表時建立此列的DDL
secondaryTable: 從表名。若是此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字。
基於annotation的hibernate主鍵標識爲@Id,
其生成規則由@GeneratedValue設定的.這裏的@id和@GeneratedValue都是JPA的標準用法。
JPA提供的四種標準用法爲TABLE,SEQUENCE,IDENTITY,AUTO。具體說明以下:
用法:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long custId;
用法:
@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。
若是底層數據庫是mysql,JPA是採用一個特定的表來保存主鍵。
用法:
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
把Customer的主鍵生成策略改成AUTO,內容以下:
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name="cust_id")
@GeneratedValue(strategy=GenerationType.AUTO)
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 + "]";
}
}
此時,向客戶表中保存一個對象:Hibernate會自動建立一張hibernate_sequence表,用於存儲下一個主鍵,每次須要獲取主鍵時,就查詢hibernate_sequence,把查詢到的結果做爲主鍵插入到表中,再把hibernate_sequence表中的主鍵值+1,測試結果以下:
用法:
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
把Customer的主鍵生成策略改成TABLE,內容以下:
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name="cust_id")
@GeneratedValue(strategy=GenerationType.TABLE)
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 + "]";
}
}
此時,向客戶表中保存一個對象:Hibernate會自動建立一張hibernate_sequences表,存儲下一個主鍵值,當初次獲取主鍵值時,先查詢hibernate_sequences,此時該表中沒有數據,因而,向該表中插入一行初始數據,值爲1,再把該表中的主鍵值+1.
@Test
public void test1(){
Customer customer = new Customer();
customer.setCustName("蔚來汽車");
//獲取JPA中實體管理器
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
//保存對象
em.persist(customer);
//提交事務
tx.commit();
//釋放資源
em.close();
}
/**
* 查詢一個
*/
@Test
public void test2(){
//獲取JPA中實體管理器
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
Customer customer = em.find(Customer.class, 1L);
System.out.println(customer);
//提交事務
tx.commit();
//釋放資源
em.close();
}
JPA也是有緩存的:
/**
* 查詢一個:緩存問題
*/
@Test
public void test2Cache(){
//獲取JPA中實體管理器
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
Customer customer1 = em.find(Customer.class, 1L);
Customer customer2 = em.find(Customer.class, 1L);
System.out.println(customer1==customer2);
//提交事務
tx.commit();
//釋放資源
em.close();
}
JPA中也是有延遲加載的:
/**
* 查詢一個:延遲加載的問題
*/
@Test
public void test2Lazy(){
//獲取JPA中實體管理器
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
Customer customer = em.getReference(Customer.class, 1L);//不發sql語句
System.out.println(customer);//真正要用的時候,才發送select語句
//提交事務
tx.commit();
//釋放資源
em.close();
}
/**
* 查詢全部:利用JPQL來查詢
* JPQL是Java Persistence Query Language,中文含義是Java持久化查詢語言
sql中能夠給表起別名,例如:select * from cst_customer c
JPQL語法規定:要給類加別名,例如select c from Customer c,表示查詢表裏有數的數據
*
*/
@Test
public void test3(){
//獲取JPA中實體管理器
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
Query query = em.createQuery("select c from Customer c where c.custName like ?");
query.setParameter(1, "%張%");
List<Customer> list = query.getResultList();
for(Customer c:list){
System.out.println(c);
}
//提交事務
tx.commit();
//釋放資源
em.close();
}
細節:JPA中的Query設置參數的位置是從1開始的。
/**
* 修改
*/
@Test
public void update(){
//獲取JPA中實體管理器
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
Customer customer = em.find(Customer.class, 1L);
customer.setCustName("騰訊科技");
//提交事務
tx.commit();//使用JPA中快照機制實現更新
//釋放資源
em.close();
}
/**
* 刪除
*/
@Test
public void delete(){
//獲取JPA中實體管理器
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開啓事務
tx.begin();
Customer customer = em.find(Customer.class, 2L);
em.remove(customer);
//提交事務
tx.commit();//使用JPA中快照機制實現更新
//釋放資源
em.close();
}
做用:
創建一對多的關係映射
屬性:
targetEntityClass:指定多的多方的類的字節碼
mappedBy:指定從表實體類中引用主表對象的名稱。讓主表(一方)放棄外鍵維護。
cascade:指定要使用的級聯操做
fetch:指定是否採用延遲加載
orphanRemoval:是否使用孤兒刪除
做用:
創建多對一的關係
屬性:
targetEntityClass:指定一的一方實體類字節碼
cascade:指定要使用的級聯操做
fetch:指定是否採用延遲加載
optional:關聯是否可選。若是設置爲false,則必須始終存在非空關係。
做用:
用於定義主鍵字段和外鍵字段的對應關係。
屬性:
name:指定外鍵字段的名稱
referencedColumnName:指定引用主表的主鍵字段名稱
unique:是否惟一。默認值不惟一
nullable:是否容許爲空。默認值容許。
insertable:是否容許插入。默認值容許。
updatable:是否容許更新。默認值容許。
columnDefinition:列的定義信息。
建立客戶和聯繫人實體類,實體類的設計同以前的代碼:
/**
* 客戶實體類
* @author kevin
*/
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name="cust_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
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;
//mappedBy屬性:表示由多方(聯繫人)來維護主鍵,值就是多方(聯繫人)中一方的屬性名
@OneToMany(mappedBy="customer")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
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;
}
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
@Override
public String toString() {
return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource
+ ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress
+ ", custPhone=" + custPhone + "]";
}
}
/**
* 聯繫人實體類
* @author kevin
*/
@Entity
@Table(name="cst_linkman")
public class LinkMan implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="lkm_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
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
@JoinColumn(name="lkm_customer_id")
private 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;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
@Override
public String toString() {
return "LinkMan [lkmId=" + lkmId + ", lkmName=" + lkmName + ", lkmGender=" + lkmGender + ", lkmPhone="
+ lkmPhone + ", lkmMobile=" + lkmMobile + ", lkmEmail=" + lkmEmail + ", lkmPosition=" + lkmPosition
+ ", lkmMemo=" + lkmMemo + "]";
}
}
在測試以前,先修改persistence.xml中的hibernte.hbm2ddl.auto的值爲create;
在TestJPA中新建單元測試方法以下:
@Test
public void test2(){
EntityManager em = JPAUtils.getEntityManager();
}
建立用戶和角色實體類,實體類的設計同以前的代碼:
做用:
用於映射多對多關係
屬性:
cascade:配置級聯操做。
fetch:配置是否採用延遲加載。
targetEntity:配置目標的實體類。映射多對多的時候不用寫。
做用:
針對中間表的配置
屬性:
nam:配置中間表的名稱
joinColumns:中間表的外鍵字段關聯當前實體類所對應表的主鍵字段
inverseJoinColumn:中間表的外鍵字段關聯對方表的主鍵字段
做用:
用於定義主鍵字段和外鍵字段的對應關係。
屬性:
name:指定外鍵字段的名稱
referencedColumnName:指定引用主表的主鍵字段名稱
unique:是否惟一。默認值不惟一
nullable:是否容許爲空。默認值容許。
insertable:是否容許插入。默認值容許。
updatable:是否容許更新。默認值容許。
columnDefinition:列的定義信息。
/**
* 用戶實體類
* @author kevin
*/
@Entity
@Table(name="sys_user")
public class SysUser implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="user_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userId;
@Column(name="user_name")
private String userName;
@Column(name="user_memo")
private String userMemo;
//多對多關係映射
@ManyToMany
@JoinTable(name="user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))
private Set<SysRole> roles = new HashSet<SysRole>();
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserMemo() {
return userMemo;
}
public void setUserMemo(String userMemo) {
this.userMemo = userMemo;
}
public Set<SysRole> getRoles() {
return roles;
}
public void setRoles(Set<SysRole> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "SysUser [userId=" + userId + ", userName=" + userName + ", userMemo=" + userMemo + "]";
}
}
/**
* 角色實體類
* @author kevin
*/
@Entity
@Table(name="sys_role")
public class SysRole implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="role_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long roleId;
@Column(name="role_name")
private String roleName;
@Column(name="role_memo")
private String roleMemo;
//一個角色對應多個用戶
@ManyToMany(mappedBy="roles")
private Set<SysUser> users = new HashSet<SysUser>();
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleMemo() {
return roleMemo;
}
public void setRoleMemo(String roleMemo) {
this.roleMemo = roleMemo;
}
public Set<SysUser> getUsers() {
return users;
}
public void setUsers(Set<SysUser> users) {
this.users = users;
}
@Override
public String toString() {
return "SysRole [roleId=" + roleId + ", roleName=" + roleName + ", roleMemo=" + roleMemo + "]";
}
}
在測試以前,先修改persistence.xml中的hibernte.hbm2ddl.auto的值爲create;
在TestJPA中新建單元測試方法以下:
@Test
public void test2(){
EntityManager em = JPAUtils.getEntityManager();
}
小結:
@OneToMany
@ManyToOne
@JoinColumn
@ManyToMany
@JoinTable
咱們先看一對多中的普通保存:
/**
* 一對多保存
* 創建雙向關係
*/
@Test
public void test1(){
Customer customer = new Customer();
customer.setCustName("客戶1");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("聯繫人1");
//創建客戶和聯繫人的雙向關係,不會有多餘的update語句,由於咱們在一方(客戶)加了mappedBy,放棄了外鍵維護權
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(customer);
em.persist(linkMan);
tx.commit();
em.close();
}
若是想級聯保存,在Customer實體類的@OneToMany註解中加上級聯保存屬性:
//一個客戶有多個聯繫人
//mappedBy屬性:表示由多方(聯繫人)來維護主鍵,值就是多方(聯繫人)中一方的屬性名
@OneToMany(mappedBy="customer",cascade={CascadeType.PERSIST})
修改Customer實體類代碼以下:
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name="cust_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
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;
//mappedBy屬性:表示由多方(聯繫人)來維護主鍵,值就是多方(聯繫人)中一方的屬性名
@OneToMany(mappedBy="customer",cascade=CascadeType.PERSIST)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
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;
}
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
@Override
public String toString() {
return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource
+ ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress
+ ", custPhone=" + custPhone + "]";
}
}
修改保存測試代碼:只須要保存客戶便可:
/**
* 一對多級聯保存:保存客戶級聯保存聯繫人
* 在Customer實體類的OneToMany註解中加cascade=CascadeType.PERSIST
*
*/
@Test
public void test2(){
Customer customer = new Customer();
customer.setCustName("客戶1");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("聯繫人1");
//創建客戶和聯繫人的雙向關係,不會有多餘的update語句,由於咱們在一方(客戶)加了mappedBy,放棄了外鍵維護權
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(customer);
tx.commit();
em.close();
}
這裏直接演示刪除客戶級聯刪除聯繫人。
若是想級聯刪除,能夠在Customer實體類這邊配置級聯刪除
提示:若是想配置多個級聯操做,能夠這樣來配置:cascade={CascadeType.PERSIST,CascadeType.REMOVE}
修改Customer實體類以下:
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name="cust_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
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;
//mappedBy:指定一方放棄外鍵維護權
@OneToMany(mappedBy="customer",cascade={CascadeType.PERSIST,CascadeType.REMOVE})
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
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;
}
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
@Override
public String toString() {
return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource
+ ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress
+ ", custPhone=" + custPhone + "]";
}
}
刪除測試代碼以下:
/**
* 一對多級聯刪除:刪除客戶,級聯刪除聯繫人
*
*/
@Test
public void test3(){
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = em.find(Customer.class, 1L);
em.remove(customer);
tx.commit();
em.close();
}
/**
* 一對多的更新
* 需求:讓2號聯繫人屬於2號客戶
*/
@Test
public void test6(){
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//先查詢id爲2的聯繫人
LinkMan linkMan = em.find(LinkMan.class, 2L);
//再查詢id爲2的客戶
Customer customer = em.find(Customer.class, 2L);
//從新創建關係
linkMan.setCustomer(customer);
tx.commit();
em.close();
}
先看多對多中的普通保存:
@Test
public void test1(){
SysUser user1 = new SysUser();
user1.setUserName("張三");
SysUser user2 = new SysUser();
user2.setUserName("李四");
SysRole role1 = new SysRole();
role1.setRoleName("角色1");
SysRole role2 = new SysRole();
role2.setRoleName("角色2");
SysRole role3 = new SysRole();
role3.setRoleName("角色3");
user1.getRoles().add(role1);
user1.getRoles().add(role2);
role1.getUsers().add(user1);
role2.getUsers().add(user1);
user2.getRoles().add(role2);
user2.getRoles().add(role3);
role2.getUsers().add(user2);
role3.getUsers().add(user2);
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(user1);
em.persist(user2);
em.persist(role1);
em.persist(role2);
em.persist(role3);
tx.commit();
em.close();
}
若是想級聯保存,在User實體類中設置級聯保存:
@Entity
@Table(name = "sys_user")
public class SysUser implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="user_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userId;
@Column(name="user_name")
private String userName;
@Column(name="user_memo")
private String userMemo;
//多對多關係映射
@ManyToMany(cascade={CascadeType.PERSIST})
@JoinTable(name="user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))
private Set<SysRole> roles = new HashSet<SysRole>();
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserMemo() {
return userMemo;
}
public void setUserMemo(String userMemo) {
this.userMemo = userMemo;
}
public Set<SysRole> getRoles() {
return roles;
}
public void setRoles(Set<SysRole> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "SysUser [userId=" + userId + ", userName=" + userName + ", userMemo=" + userMemo + "]";
}
}
由於設置了級聯保存,因此只須要保存用戶便可:
@Test
public void test1(){
SysUser user1 = new SysUser();
user1.setUserName("用戶1");
SysUser user2 = new SysUser();
user2.setUserName("用戶2");
SysRole role1 = new SysRole();
role1.setRoleName("角色1");
SysRole role2 = new SysRole();
role2.setRoleName("角色2");
SysRole role3 = new SysRole();
role3.setRoleName("角色3");
user1.getRoles().add(role1);
user1.getRoles().add(role2);
user2.getRoles().add(role2);
user2.getRoles().add(role3);
role1.getUsers().add(user1);
role2.getUsers().add(user1);
role2.getUsers().add(user2);
role3.getUsers().add(user2);
//獲取EntityManager
EntityManager em = JPAUtils.getEntityManager();
//獲取事務
EntityTransaction tx = em.getTransaction();
//開始事務
tx.begin();
//保存操做
em.persist(user1);
em.persist(user2);
/*em.persist(role1);
em.persist(role2);
em.persist(role3);*/
//提交事務
tx.commit();
//釋放資源
em.close();
}
先看直接刪除用戶的狀況:
/**
* 測試刪除:直接刪除用戶
* 結果:先刪除中間表中與該用戶相關的信息,再刪除用戶
*/
@Test
public void test2(){
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
SysUser user = em.find(SysUser.class, 1L);
em.remove(user);
tx.commit();
em.close();
}
若是想級聯刪除,能夠在用戶一方配置級聯刪除:
//一個用戶對應多個角色
@ManyToMany(cascade=CascadeType.REMOVE)
@JoinTable(name="user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))
private Set<SysRole> roles = new HashSet<SysRole>();
/**
* 測試多對多級聯刪除:刪除用戶級聯刪除角色
*/
@Test
public void test3(){
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
SysUser user = em.find(SysUser.class, 1L);
em.remove(user);
tx.commit();
em.close();
}
測試發現:報錯,緣由同xml配置。因此,在多對多中通常不配置級聯刪除。
/**
* 多對多的更新
* 需求:把用戶1中的角色1換成角色3
*/
@Test
public void test5(){
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//先查詢id爲1的用戶
SysUser user = em.find(SysUser.class, 1L);
//再查詢id爲1的角色
SysRole role1 = em.find(SysRole.class, 1L);
//再查詢id爲3的角色
SysRole role3 = em.find(SysRole.class, 3L);
//先移除角色1
user.getRoles().remove(role1);
//在添加角色3
user.getRoles().add(role3);
tx.commit();
em.close();
}
明確:導航查詢的意義和xml的意義同樣
/**
* 查詢客戶,也要把客戶對應的聯繫人查詢出來
*/
@Test
public void test1(){
//獲取EntityManager
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//查詢客戶時,延遲加載聯繫人
Customer customer = em.find(Customer.class, 1L);
System.out.println(customer.getCustName());
Set<LinkMan> linkMans = customer.getLinkMans();
for (LinkMan linkMan : linkMans) {
System.out.println(linkMan);
}
tx.commit();
em.close();
}
觀察測試結果發現,查詢一方延遲加載多方數據,這點和xml配置是同樣的。測試結果以下:
/**
* 查詢聯繫人,也要把聯繫人所屬的客戶查詢出來
*/
@Test
public void test2(){
//獲取EntityManager
EntityManager em = JPAUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//查詢聯繫人時,會當即加載客戶
LinkMan linkMan = em.find(LinkMan.class, 1L);
System.out.println(linkMan.getLkmName());
Customer customer = linkMan.getCustomer();
System.out.println(customer);
tx.commit();
em.close();
}
觀察測試結果發現:查詢多方當即加載一方數據,這點和xml正好是相反的。咱們也不須要改變這種狀況,由於咱們就是須要查詢多方當即加載一方數據。測試結果以下:
提示:若是想改變默認的抓取策略,能夠給OneToMany、ManyToOne註解加fetch屬性:
fetch屬性能夠取以下兩個值:
1、EAGER:表示當即加載;
2、LAZY:表示延遲加載;
在註解裏,默認狀況下,查詢一方是延遲加載多方,若是想改變這種狀況,能夠在OneToMany中加fetch屬性,以下:
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name="cust_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
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;
//mappedBy:指定一方放棄外鍵維護權
@OneToMany(mappedBy="customer",cascade={CascadeType.PERSIST,CascadeType.REMOVE},fetch=FetchType.EAGER)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
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;
}
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
@Override
public String toString() {
return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource
+ ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress
+ ", custPhone=" + custPhone + "]";
}
}
修改完以後,再次運行以下代碼:
/**
* 在JPA中:查詢一方延遲加載多方
*/
@Test
public void test1() {
// 獲取EntityManager
EntityManager em = JPAUtils.getEntityManager();
// 獲取事務
EntityTransaction tx = em.getTransaction();
// 開啓事務
tx.begin();
Customer customer = em.find(Customer.class, 1L);
System.out.println(customer.getCustName());
Set<LinkMan> linkMans = customer.getLinkMans();
for (LinkMan linkMan : linkMans) {
System.out.println(linkMan);
}
// 提交事務
tx.commit();
// 釋放資源
em.close();
}
測試結果代表:查詢一方會;當即加載多方數據。可是,通常也沒有改變fetch的默認值。測試結果以下:
多對多:當作兩個一對多的組合