Hibernate JPA

一、JPA概述

        

二、JPA中的第一個程序java

 

三、JPA中經常使用的註解(重點)mysql

                  

四、JPA中的CRUDsql

        

五、JPA中的多表映射的配置數據庫

        

六、JPA中多表的增刪改緩存

 

七、對象導航查詢網絡

  1. JPA相關概念
    1. JPA概述

 

何時JPA?JPA它是一套Java持久化規範。那麼JPA和咱們前面三天所學的Hibernate有什麼關係呢?session

  1. JPA是一套Java持久化規範;而Hibernate實現了JPA規範。
  2. 在Hibernate中有兩套操做數據庫的方式,一套是Hibernate本身特有的的方式,前面三天已經學過了;還有一套是實現了JPA規範的方式。

今天咱們主要學習Hibernate中實現了JPA規範的操做數據庫的方式。app

    1. JPA優點

    1. 學習JPA要明確的

      a、JPA是一套ORM規範,hibernate實現了JPA規範dom

         b、hibernate中有本身的獨立ORM操做數據庫方式,也有JPA規範實現的操做數據庫方式。ide

         c、在數據庫增刪改查操做中,咱們hibernateJPA的操做都要會。

  1. JPA入門
    1. 第一步:建立工程,引入jar包

要搭建一個JPA的開發環境,除了須要導入前面三天所用的Hibernate的jar包之外,還須要導入hibernate-entitymanager.jar包,該jar包能夠從以下路徑找到:

完整的jar包以下圖所示:

注意:今天咱們主要學習JPA規範,可是僅僅只有規範是不行的,還得有規範的實現才行,因此才須要導入Hibernate相關的jar包,由於Hibernate就是JPA規範的實現。

    1. 第二步:建立實體類

建立Customer實體類:

 

在實體類上加上JPA註解,取代傳統的hbm.xml文件的功能。須要使用到的註解有:

  1. @Entity:若是一個類加上此註解,表示該類是一個實體類。
  2. @Table:指定該類所映射的表,name屬性表示該類所映射的表名。若是類名與表名一致,此註解能夠省略。
  3. @Id:表示與主鍵列對應的屬性。
  4. @GeneratedValue:表示主鍵如何生成。strategy屬性表示主鍵的生成策略。GenerationType.IDENTITY表示自動增加。

/**

 * 客戶實體類

 * @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. 第三步:建立配置文件

1、在src下新建META-INF文件夾,新建persistence.xml文件。

  1. 引入schema約束,能夠從hibernate-entitymanager-5.0.7.Final.jar/org/hibernate/jpa/persistence_2_0.xsd中拷貝

  1. 配置提示

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>

    1. 第四步:編寫工具類,用於獲取JPA操做數據庫的對象

建立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

PersistenceJPA規範中的工具類,用於獲取EntityManagerFactory的;

    1. 第五步:編寫測試類

建立單元測試類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的解釋:

EntityTransactionJPA規範中的事務接口,用來管理JPA規範中的事務,做用等價於Transaction;

EntityManager.persist():用來保存對象的;做用等價於session.save();

  1. 經常使用註解及主鍵生成策略
    1. 經常使用註解說明

@Entity

    做用:指定當前類是實體類。寫上此註解用於在建立SessionFactory/EntityManager時,加載映射配置。

@Table

    做用:指定實體類和表之間的對應關係。

    屬性:

        name:指定數據庫表的名稱

@Id

    做用:指定當前字段是主鍵。

@GeneratedValue

    做用:指定主鍵的生成方式。JPA的主鍵生成方式詳解見2.4小節的說明。

    屬性:

        strategy :指定主鍵生成策略。JPA支持四種生成策略,具體介紹看3.2小節。

@Column

    做用:指定實體類屬性和數據庫表裏列之間的對應關係

    屬性:

        name:指定數據庫表的列名稱。

        unique:是否惟一  
            nullable:是否能夠爲空  
            inserttable:是否能夠插入  
            updateable:是否能夠更新  
            columnDefinition: 定義建表時建立此列的DDL  
            secondaryTable: 從表名。若是此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字。  

    1. 主鍵生成策略

基於annotation的hibernate主鍵標識爲@Id, 
其生成規則由@GeneratedValue設定的.這裏的@id和@GeneratedValue都是JPA的標準用法。

JPA提供的四種標準用法爲TABLE,SEQUENCE,IDENTITY,AUTO。具體說明以下:

      1. IDENTITY:主鍵由數據庫自動生成(自動增加型) 

用法:

    @Id  

    @GeneratedValue(strategy = GenerationType.IDENTITY) 

    private Long custId;

      1. 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。

      1. AUTO:根據底層數據庫自動選擇一種策略

若是底層數據庫是mysqlJPA是採用一個特定的表來保存主鍵。

用法:

    @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,測試結果以下:

      1. TABLE:使用一個特定的數據庫表格來保存主鍵(瞭解)

用法:

@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.

  1. JPA註解的CRUD
    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();

}

    1. 查詢一個

/**

    * 查詢一個

    */

   @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();

   }

    1. 查詢全部

/**

    * 查詢全部:利用JPQL來查詢

 * JPQLJava 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開始的。

    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();

   }

 

    1. 刪除

/**

    * 刪除

    */

   @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();

   }

  1. JPA多表映射
    1. 一對多關係映射
      1. 經常使用註解
        1. @OneToMany

做用:

    創建一對多的關係映射

屬性:

    targetEntityClass:指定多的多方的類的字節碼

    mappedBy:指定從表實體類中引用主表對象的名稱。讓主表(一方)放棄外鍵維護

    cascade:指定要使用的級聯操做

    fetch:指定是否採用延遲加載

    orphanRemoval:是否使用孤兒刪除

 

        1. @ManyToOne

做用:

    創建多對一的關係

屬性:

    targetEntityClass:指定一的一方實體類字節碼

    cascade:指定要使用的級聯操做

    fetch:指定是否採用延遲加載

    optional:關聯是否可選。若是設置爲false,則必須始終存在非空關係。

        1. @JoinColumn

做用:

    用於定義主鍵字段和外鍵字段的對應關係。

屬性:

    name:指定外鍵字段的名稱

    referencedColumnName:指定引用主表的主鍵字段名稱

    unique:是否惟一。默認值不惟一

    nullable:是否容許爲空。默認值容許。

    insertable:是否容許插入。默認值容許。

    updatable:是否容許更新。默認值容許。

    columnDefinition:列的定義信息。

      1. 配置代碼

建立客戶和聯繫人實體類,實體類的設計同以前的代碼:

 

        1. 客戶實體類的配置

/**

 * 客戶實體類

 * @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 + "]";

   }

 

}

        1. 聯繫人實體類的配置

/**

 * 聯繫人實體類

 * @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 + "]";

   }

  

}

        1. 測試註解配置的正確性

在測試以前,先修改persistence.xml中的hibernte.hbm2ddl.auto的值爲create;

在TestJPA中新建單元測試方法以下:

@Test

   public void test2(){

      EntityManager em = JPAUtils.getEntityManager();

   }

    1. 多對多關係映射

建立用戶和角色實體類,實體類的設計同以前的代碼:

      1. 經常使用註解
        1. @ManyToMany

做用:

    用於映射多對多關係

屬性:

    cascade:配置級聯操做。

    fetch:配置是否採用延遲加載。

    targetEntity:配置目標的實體類。映射多對多的時候不用寫。

        1. @JoinTable

做用:

    針對中間表的配置

屬性:

    nam:配置中間表的名稱

    joinColumns:中間表的外鍵字段關聯當前實體類所對應表的主鍵字段                     

    inverseJoinColumn:中間表的外鍵字段關聯對方表的主鍵字段

        1. @JoinColumn

做用:

    用於定義主鍵字段和外鍵字段的對應關係。

屬性:

    name:指定外鍵字段的名稱

    referencedColumnName:指定引用主表的主鍵字段名稱

    unique:是否惟一。默認值不惟一

    nullable:是否容許爲空。默認值容許。

    insertable:是否容許插入。默認值容許。

    updatable:是否容許更新。默認值容許。

    columnDefinition:列的定義信息。

 

      1. 配置代碼
        1. 用戶實體類的配置

/**

 * 用戶實體類

 * @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 + "]";

   }

 

}

 

        1. 角色實體類的配置

/**

 * 角色實體類

 * @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 + "]";

   }

}

        1. 測試註解配置的正確性

在測試以前,先修改persistence.xml中的hibernte.hbm2ddl.auto的值爲create;

在TestJPA中新建單元測試方法以下:

@Test

   public void test2(){

      EntityManager em = JPAUtils.getEntityManager();

   }

 

 

小結:

@OneToMany

@ManyToOne

@JoinColumn

@ManyToMany

@JoinTable

 

  1. JPA多表操做
    1. 一對多關係的增刪改操做
      1. 保存操做

咱們先看一對多中的普通保存:

/**

    * 一對多保存

    * 創建雙向關係

    */

   @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();

   }

      1. 刪除操做

這裏直接演示刪除客戶級聯刪除聯繫人。

若是想級聯刪除,能夠在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();

}

      1. 修改操做

/**

    * 一對多的更新

    * 需求:讓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();

}

 

    1. 多對多關係的增刪改操做
      1. 保存操做

先看多對多中的普通保存:

@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 txem.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();

}

      1. 刪除操做

先看直接刪除用戶的狀況:

/**

    * 測試刪除:直接刪除用戶

    * 結果:先刪除中間表中與該用戶相關的信息,再刪除用戶

    */

   @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中的角色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();

   }

    1. JPA裏的對象導航查詢

明確:導航查詢的意義和xml的意義同樣

      1. 查詢一方時,延遲加載多方數據

/**

    * 查詢客戶,也要把客戶對應的聯繫人查詢出來

    */

   @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配置是同樣的。測試結果以下:

 

      1. 查詢多方,當即加載一方數據

/**

    * 查詢聯繫人,也要把聯繫人所屬的客戶查詢出來

    */

   @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的默認值。測試結果以下:

 

 

多對多:當作兩個一對多的組合

相關文章
相關標籤/搜索