SSH框架之Hibernate第四篇
- 2019 年 10 月 5 日
- 筆記
版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/zhao1299002788/article/details/100623070
Hibernate中有兩套實現資料庫數據操作的方式 : hibernate前3天講解的都是 : hibernate自己的操作方式(純XML配置文件的方式) 另一種方式是基於JPA的操作方式(通過註解的方式替代之前的部分XML) JPA相關概念: 1.1JPA概述 : (java的持久化規範(規範即介面)) 全稱是 : Java Persistence API. 是SUN公司推出的一套基於ORM的規範.hibernate框架中提供了JPA的實現. JAP通過JDK5.0註解或XML描述對象-關係表的映射關係,並將運行期的實體對象持久化到資料庫中. 1.2JPA要明確的 a. JPA是一套ORM規範(算是一個介面),hibernate實現了JPA規範(算是一個實現類). b. hibernate中有自己的獨立ORM操作資料庫方式,也有JPA規範實現的操作資料庫方式. c. 在資料庫增刪改查操作中,我們hibernate和JPA的操作都要會. JPA和hibernate的關係? JPA是介面,hibernate是實現. 所有的ORM框架都可以去實現JPA介面,通過JPA提供的一些介面來操作資料庫的數據. JPA的使用 : JPA是通過註解的方式來描述,對象和表的映射關係. 之前的對象和表的映射關係配置是通過XML,今天要替換成註解的方式. 注釋 : 給程式設計師看的. 註解 : 給程式來使用的. 為什麼要出現註解? 一開始就是為了替代所有的XML配置文件. 工作中兩種方式結合使用 : 配置文件 + 註解 如果是需要常常改動的程式 : 用配置文件. 如果是定義好了不需要更改的程式 : 註解. 2.1 2 JPA的環境搭建 1 hibernate的環境(16個包) 2 JPA的環境(1個包) 2.2.2 創建配置文件 要求:在src下面的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_1_0.xsd" version="1.0"> <!--Name屬性用於定義持久化單元的名字 (name必選,空值也合法); transaction-type 指定事務類型(可選) 取值: JTA:默認值 RESOURCE_LOCAL --> <persistence-unit name="myPersistUnit" transaction-type="RESOURCE_LOCAL"> <properties> <!-- 生成DDL的策略 --> <property name="hibernate.hbm2ddl.auto" value="update" /> <!-- 資料庫的連接資訊 --> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/hibernate_jpa"/> <property name="hibernate.connection.username" value="root" /> <property name="hibernate.connection.password" value="1234" /> <!-- 指定方言 --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" /> <!-- 是否顯示SQL語句 --> <property name="hibernate.show_sql" value="false" /> <!-- 是否格式化SQL語句 --> <property name="hibernate.format_sql" value="true" /> </properties> </persistence-unit> </persistence> 2.2.3 編寫工具類,用於獲取JPA的操作資料庫對象 public class HibernateUtils { //JPA的實體管理器工廠 : 相當於Hibernate的SessionFactory private static EntityManagerFactory em; //使用靜態程式碼塊賦值 static { //載入一次配置文件 //注意 : 該方法參數必須和persistence.xml中persistence-unit標籤name屬性取值一致 em = Persistence.createEntityManagerFactory("aaa"); } /* * 使用管理器工廠生產一個管理器對象 */ //相當於獲取連接 public static EntityManager getEntityManager() { return em.createEntityManager(); } } jpa 的批量查詢方式 : 類似咋們之前學習的query方式查詢 : 1 : qr.getResultList() ; 類似之前的qr.list(); 2 : hibernate對於佔位符? 以前是從0開始,jpa是從1開始. 3 : 聚合 qr.getSingleResult(); 類似之前的uniqueResult(); 2.2.4 編寫實體類,並使用註解配置 //級聯保存 (保存客戶的同時把關聯的聯繫人給保存了) //jpa的註解裡面 @OneToMany 添加屬性cascade = CascadeType.PERSIST_STORE //根據一的一方保存多的一方的數據. // 級聯保存 (保存聯繫人的同時把關聯的客戶給保存了) // jpa的註解裡面 @ManyToOne 添加屬性cascade=CascadeType.PERSIST //jpa的一對多沒有普通刪除 // 級聯刪除 // jpa的註解裡面 @OneToMany 添加屬性cascade=CascadeType.REMOVE (All) /** * 客戶的實體類 */ @Entity @Table(name="cst_customer") public class Customer implements Serializable { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="cust_id") 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 + "]"; } } 2.3 常用註解說明 @Entity 作用 : 指定當前類是實體類.寫上此註解用於在創建SessionFactory/EntityManager時,載入映射配置. @Table 作用 : 指定實體類和表之間的對應關係. 屬性 : name : 指定資料庫表的名稱. @Id 作用 : 指定當前欄位是主鍵. @GeneratedValue 作用 : 指定主鍵的生成方式.JPA的主鍵生成方式 屬性 : strategy : 指定主鍵生成策略.JPA支援四種生成策略, @Column : 作用 : 指定實體類屬性和資料庫表之間的對應關係. 屬性 : name : 指定資料庫表的列名稱. unique : 是否唯一 . nullable : 是否可以為空 inserttable : 是否可以插入 updateable : 是否可以更新 columnDefinition : 定義建表時創建此列的DDL. secondaryTable : 從表名.如果此列不建在主表上(默認鍵在主表),該屬性定義該列所在從表的名字. 2.4主鍵生成策略 通過annotation(註解)來映射hibernate實體的,基於annotation的hibernate主鍵標識為@Id, 其生成規則由@GeneratedValue設定的.這裡的@id和@GeneratedValue都是JPA的標準用法。 JPA提供的四種標準用法為TABLE,SEQUENCE,IDENTITY,AUTO。具體說明如下: 2.4.1IDENTITY:主鍵由資料庫自動生成(主要是自動增長型) 用法: @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long custId; 2.4.2SEQUENCE:根據底層資料庫的序列來生成主鍵,條件是資料庫支援序列。 用法: @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。 2.4.3AUTO:主鍵由程式控制。 用法: @Id @GeneratedValue(strategy = GenerationType.AUTO) 2.5JPA的CRUD操作 2.5.1保存 /** * 保存一個實體 */ @Test public void testAdd(){ //定義對象 Customer c = new Customer(); c.setCustName("傳智學院"); c.setCustLevel("VIP客戶"); c.setCustSource("網路"); c.setCustIndustry("IT教育"); c.setCustAddress("昌平區北七家鎮"); c.setCustPhone("010-84389340"); EntityManager em=null; EntityTransaction tx=null; try{ //獲取實體管理對象 em=JPAUtil.getEntityManager(); //獲取事務對象 tx=em.getTransaction(); //開啟事務 tx.begin(); //執行操作 em.persist(c); //提交事務 tx.commit(); }catch(Exception e){ //回滾事務 tx.rollback(); e.printStackTrace(); }finally{ //釋放資源 em.close(); } } 2.5.2修改 /** * 修改 */ @Test public void testUpdate(){ //定義對象 EntityManager em=null; EntityTransaction tx=null; try{ //獲取實體管理對象 em=JPAUtil.getEntityManager(); //獲取事務對象 tx=em.getTransaction(); //開啟事務 tx.begin(); //執行操作 Customer c1 = em.find(Customer.class, 1L); c1.setCustName("江蘇傳智學院"); //提交事務 tx.commit(); //使用JPA中快照機制實現更新 }catch(Exception e){ //回滾事務 tx.rollback(); e.printStackTrace(); }finally{ //釋放資源 em.close(); } } merge方法實現修改 @Test public void testMerge(){ //定義對象 EntityManager em=null; EntityTransaction tx=null; try{ //獲取實體管理對象 em=JPAUtil.getEntityManager(); //獲取事務對象 tx=em.getTransaction(); //開啟事務 tx.begin(); //執行操作 Customer c1 = em.find(Customer.class, 6L); c1.setCustName("江蘇傳智學院"); em.clear();//把c1對象從快取中清除出去 em.merge(c1); //提交事務 tx.commit(); }catch(Exception e){ //回滾事務 tx.rollback(); e.printStackTrace(); }finally{ //釋放資源 em.close(); } } 2.5.3刪除 /** * 刪除 */ @Test public void testRemove(){ //定義對象 EntityManager em=null; EntityTransaction tx=null; try{ //獲取實體管理對象 em=JPAUtil.getEntityManager(); //獲取事務對象 tx=em.getTransaction(); //開啟事務 tx.begin(); //執行操作 Customer c1 = em.find(Customer.class, 6L); em.remove(c1); //提交事務 tx.commit(); }catch(Exception e){ //回滾事務 tx.rollback(); e.printStackTrace(); }finally{ //釋放資源 em.close(); } } 2.5.4查詢一個 /** * 查詢一個: * 使用立即載入的策略 */ @Test public void testGetOne(){ //定義對象 EntityManager em=null; EntityTransaction tx=null; try{ //獲取實體管理對象 em=JPAUtil.getEntityManager(); //獲取事務對象 tx=em.getTransaction(); //開啟事務 tx.begin(); //執行操作 Customer c1 = em.find(Customer.class, 1L); //提交事務 tx.commit(); System.out.println(c1); //輸出查詢對象 }catch(Exception e){ //回滾事務 tx.rollback(); e.printStackTrace(); }finally{ //釋放資源 em.close(); } } 查詢實體的快取問題 @Test public void testGetOne(){ //定義對象 EntityManager em=null; EntityTransaction tx=null; try{ //獲取實體管理對象 em=JPAUtil.getEntityManager(); //獲取事務對象 tx=em.getTransaction(); //開啟事務 tx.begin(); //執行操作 Customer c1 = em.find(Customer.class, 1L); Customer c2 = em.find(Customer.class, 1L); System.out.println(c1 == c2);//輸出結果是true,EntityManager也有快取 //提交事務 tx.commit(); System.out.println(c1); }catch(Exception e){ //回滾事務 tx.rollback(); e.printStackTrace(); }finally{ //釋放資源 em.close(); } } 延遲載入策略的方法: /** * 查詢一個: * 使用延遲載入策略 */ @Test public void testLoadOne(){ //定義對象 EntityManager em=null; EntityTransaction tx=null; try{ //獲取實體管理對象 em=JPAUtil.getEntityManager(); //獲取事務對象 tx=em.getTransaction(); //開啟事務 tx.begin(); //執行操作 Customer c1 = em.getReference(Customer.class, 1L); //提交事務 tx.commit(); System.out.println(c1); }catch(Exception e){ //回滾事務 tx.rollback(); e.printStackTrace(); }finally{ //釋放資源 em.close(); } } 2.5.5查詢所有 /** * 查詢所有 * 涉及的對象: * Query(注意:不是Hibernate的Query) * 如何獲取: * 使用EntityManager的createQuery(String JPQL)方法; * 參數的含義 * JPQL:jpa query language * JPQL的寫法: * 表名使用實體類名稱替代 * 列名使用實體類屬性名稱替代 * 不能使用*號。查詢所有,需要在from關鍵字後面的類名上加別名 * 例如: select c from Customer c * 查詢條件可以使用?作為參數佔位符。 * 給佔位符賦值時,佔位符索引位置從1開始 * 獲取結果集的方法 * getResultList():查詢結果是一個List集合 * getSingleResult():查詢結果是一個對象 */ @Test public void testFindAll(){ //定義對象 EntityManager em=null; EntityTransaction tx=null; try{ //獲取實體管理對象 em=JPAUtil.getEntityManager(); //獲取事務對象 tx=em.getTransaction(); //開啟事務 tx.begin(); //執行操作 Query query = em.createQuery("select c from Customer c where custName like ? "); query.setParameter(1,"%學院%"); List list = query.getResultList(); //提交事務 tx.commit(); for(Object o : list){ System.out.println(o); } }catch(Exception e){ //回滾事務 tx.rollback(); e.printStackTrace(); }finally{ //釋放資源 em.close(); } } 3.1一對多關係映射 3.1.1常用註解 3.1.1.1@OneToMany: 作用: 建立一對多的關係映射 屬性: targetEntityClass:指定多的方的類的位元組碼 mappedBy:指定從表實體類中引用主表對象的名稱。 cascade:指定要使用的級聯操作 fetch:指定是否採用延遲載入 orphanRemoval:是否使用孤兒刪除 3.1.1.2@ManyToOne 作用: 建立多對一的關係 屬性: targetEntityClass:指定一的一方實體類位元組碼 cascade:指定要使用的級聯操作 fetch:指定是否採用延遲載入 optional:關聯是否可選。如果設置為false,則必須始終存在非空關係。 3.1.1.3@JoinColumn 作用: 用於定義主鍵欄位和外鍵欄位的對應關係。 屬性: name:指定外鍵欄位的名稱 referencedColumnName:指定引用主表的主鍵欄位名稱 unique:是否唯一。默認值不唯一 nullable:是否允許為空。默認值允許。 insertable:是否允許插入。默認值允許。 updatable:是否允許更新。默認值允許。 columnDefinition:列的定義資訊。 3.1.2配置程式碼 3.1.2.1客戶配置 /** * 客戶的實體類 * 明確使用的註解都是JPA規範的 * 所以導包都要導入javax.persistence包下的 * */ @Entity//表示當前類是一個實體類 @Table(name="cst_customer")//建立當前實體類和表之間的對應關係 public class Customer implements Serializable { @Id//表明當前私有屬性是主鍵 @GeneratedValue(strategy=GenerationType.IDENTITY)//指定主鍵的生成策略 @Column(name="cust_id")//指定和資料庫表中的cust_id列對應 private Long custId; @Column(name="cust_name")//指定和資料庫表中的cust_name列對應 private String custName; @Column(name="cust_source")//指定和資料庫表中的cust_source列對應 private String custSource; @Column(name="cust_industry")//指定和資料庫表中的cust_industry列對應 private String custIndustry; @Column(name="cust_level")//指定和資料庫表中的cust_level列對應 private String custLevel; @Column(name="cust_address")//指定和資料庫表中的cust_address列對應 private String custAddress; @Column(name="cust_phone")//指定和資料庫表中的cust_phone列對應 private String custPhone; @OneToMany(targetEntity=LinkMan.class,mappedBy="customer") private Set<LinkMan> linkmans = new HashSet<LinkMan>(0); 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 + "]"; } } 3.1.2.2聯繫人配置 /** * 聯繫人的實體類(數據模型) */ @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 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 + "]"; } } 3.2多對多關係映射 3.2.1常用註解: 3.2.1.1@ManyToMany 作用: 用於映射多對多關係 屬性: cascade:配置級聯操作。 fetch:配置是否採用延遲載入。 targetEntity:配置目標的實體類。映射多對多的時候不用寫。 3.2.1.2@JoinTable 作用: 針對中間表的配置 屬性: name:配置中間表的名稱 joinColumns:中間表的外鍵欄位關聯當前實體類所對應表的主鍵欄位 inverseJoinColumn:中間表的外鍵欄位關聯對方表的主鍵欄位 3.2.1.3@JoinColumn 作用: 用於定義主鍵欄位和外鍵欄位的對應關係。 屬性: name:指定外鍵欄位的名稱 referencedColumnName:指定引用主表的主鍵欄位名稱 unique:是否唯一。默認值不唯一 nullable:是否允許為空。默認值允許。 insertable:是否允許插入。默認值允許。 updatable:是否允許更新。默認值允許。 columnDefinition:列的定義資訊。 3.2.2配置程式碼 3.2.2.1用戶配置 /** * 用戶的數據模型 */ @Entity @Table(name="sys_user") public class SysUser implements Serializable { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="user_id") private Long userId; @Column(name="user_code") private String userCode; @Column(name="user_name") private String userName; @Column(name="user_password") private String userPassword; @Column(name="user_state") private String userState; //多對多關係映射 @ManyToMany(mappedBy="users") private Set<SysRole> roles = new HashSet<SysRole>(0); public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getUserCode() { return userCode; } public void setUserCode(String userCode) { this.userCode = userCode; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } public String getUserState() { return userState; } public void setUserState(String userState) { this.userState = userState; } public Set<SysRole> getRoles() { return roles; } public void setRoles(Set<SysRole> roles) { this.roles = roles; } @Override public String toString() { return "SysUser [userId=" + userId + ", userCode=" + userCode + ", userName=" + userName + ", userPassword=" + userPassword + ", userState=" + userState + "]"; } } 3.2.2.2角色配置 /** * 角色的數據模型 */ @Entity @Table(name="sys_role") public class SysRole implements Serializable { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="role_id") private Long roleId; @Column(name="role_name") private String roleName; @Column(name="role_memo") private String roleMemo; //多對多關係映射 @ManyToMany @JoinTable(name="user_role_rel",//中間表的名稱 //中間表user_role_rel欄位關聯sys_role表的主鍵欄位role_id joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")}, //中間表user_role_rel的欄位關聯sys_user表的主鍵user_id inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")} ) private Set<SysUser> users = new HashSet<SysUser>(0); 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 + "]"; } } 第4章JPA的多表操作 4.1一對多關係的增刪改操作 4.1.1保存操作 保存原則:先保存主表,再保存從表。 /** * 保存操作 * 需求: * 保存一個客戶和一個聯繫人 * 要求: * 創建一個客戶對象和一個聯繫人對象 * 建立客戶和聯繫人之間關聯關係(雙向一對多的關聯關係) * 先保存客戶,再保存聯繫人 */ @Test public void test1(){ //創建客戶和聯繫人對象 Customer c = new Customer();//瞬時態 c.setCustName("TBD雲集中心"); c.setCustLevel("VIP客戶"); c.setCustSource("網路"); c.setCustIndustry("商業辦公"); c.setCustAddress("昌平區北七家鎮"); c.setCustPhone("010-84389340"); LinkMan l = new LinkMan();//瞬時態 l.setLkmName("TBD聯繫人"); l.setLkmGender("male"); l.setLkmMobile("13811111111"); l.setLkmPhone("010-34785348"); l.setLkmEmail("[email protected]"); l.setLkmPosition("老師"); l.setLkmMemo("還行吧"); //建立他們的雙向一對多關聯關係 l.setCustomer(c); c.getLinkmans().add(l); //獲取JPA操作對照 EntityManager em = JPAUtil.getEntityManager(); //獲取JPA事務對象 EntityTransaction tx= em.getTransaction(); //開啟事務 tx.begin(); //按照要求:先保存客戶,再保存聯繫人(此時符合保存原則:先保存主表,再保存從表) em.persist(c); em.persist(l); tx.commit(); } JPA註解的配置方式:不涉及多一條update語句的問題 4.1.2刪除操作 /** * 刪除操作 * 刪除從表數據:可以隨時任意刪除。 * 刪除主表數據: * 有從表數據引用 * 1、不能刪除 * 2、如果還想刪除,使用級聯刪除 * 沒有從表數據引用:隨便刪 * * 在實際開發中,級聯刪除請慎用!(在一對多的情況下) */ @Test public void test3(){ //獲取JPA操作對照 EntityManager em = JPAUtil.getEntityManager(); //獲取JPA事務對象 EntityTransaction tx= em.getTransaction(); //開啟事務 tx.begin(); //查詢id為1的客戶 Customer c1 = em.find(Customer.class, 2L); //刪除id為1的客戶 em.remove(c1); tx.commit(); } 級聯刪除的配置: @OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.ALL) //用CascadeType.REMOVE也可以 private Set<LinkMan> linkmans = new HashSet<LinkMan>(0); 4.2多對多關係的增刪操作 4.2.1保存操作 /** * 需求: * 保存用戶和角色 * 要求: * 創建2個用戶和3個角色 * 讓1號用戶具有1號和2號角色(雙向的) * 讓2號用戶具有2號和3號角色(雙向的) * 保存用戶和角色 */ @Test public void test1(){ //創建對象 SysUser u1 = new SysUser(); u1.setUserName("用戶1"); SysUser u2 = new SysUser(); u2.setUserName("用戶2"); SysRole r1 = new SysRole(); r1.setRoleName("角色1"); SysRole r2 = new SysRole(); r2.setRoleName("角色2"); SysRole r3 = new SysRole(); r3.setRoleName("角色3"); //建立關聯關係 u1.getRoles().add(r1); u1.getRoles().add(r2); r1.getUsers().add(u1); r2.getUsers().add(u1); u2.getRoles().add(r2); u2.getRoles().add(r3); r2.getUsers().add(u2); r3.getUsers().add(u2); //獲取JPA操作對照 EntityManager em = JPAUtil.getEntityManager(); //獲取JPA事務對象 EntityTransaction tx= em.getTransaction(); //開啟事務 tx.begin(); em.persist(u1); em.persist(u2); em.persist(r1); em.persist(r2); em.persist(r3); tx.commit(); } JPA註解的配置方式:不涉及保存失敗的問題: 4.2.2刪除操作 /** * 刪除操作 * 在多對多的刪除時,雙向級聯刪除根本不能配置 * 禁用 * 如果配了的話,如果數據之間有相互引用關係,可能會清空所有數據 */ @Test public void test2(){ //獲取JPA操作對照 EntityManager em = JPAUtil.getEntityManager(); //獲取JPA事務對象 EntityTransaction tx= em.getTransaction(); //開啟事務 tx.begin(); SysUser u1 = em.find(SysUser.class,3L); em.remove(u1); tx.commit(); } 在多對多映射配置中不能出現雙向級聯刪除的配置,無論註解還是XML配置 5.2JPA和hibernate中操作數據的方法對照 操作 Hibernate中的方法 JPA中的方法 說明 保存操作 save(Object entity) persist(Object entity) 共同點:都是把臨時態對象轉成了持久態。 區別: 提供者不一樣: save方法是hibernate提供的。 persist方法是JPA規範提供的。 在沒有事務的情況下: save會去資料庫中保存,hibernate提供了一個內置的事務來執行。 persist什麼都不會做。 更新操作 update (Object entity) merge (Object entity) Hibernate和jpa都可以利用快照機制,不調用任何方法去更新。 Update方法在更新時,如果遇到一級快取已經包含了一個相同OID的對象會報錯。merge則可以執行成功。 刪除操作 delete (Object entity) remove (Object entity) 都是刪除一個實體 查詢一個操作 get (Class clazz,Serializable id) load(Class clazz,Serializable id) find(Class clazz,Object id) getReerence(Class clazz,Object id) get和find都是立即載入。load和getReference一樣都是延遲載入。 查詢所有操作 Query:使用HQL語句查詢 Query:使用JPQL查詢 查詢語句的形式不一樣。 查詢返回唯一結果操作 uniqueResult() getSingleResult() 查詢都是返回一個唯一的結果。 JPA一對多的註解配置: 案例: // ps: jpa提供的註解都在:javax.persistence包下 @Entity @Table(name="cst_customer") public class Customer { @Id @Column(name="cust_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long cust_id; // '客戶編號(主鍵)', @Column(name="cust_name") private String cust_name; // '客戶名稱(公司名稱)', @Column(name="cust_source") private String cust_source; // '客戶資訊來源', @Column(name="cust_industry") private String cust_industry; // '客戶所屬行業', @Column(name="cust_level") private String cust_level; // '客戶級別', @Column(name="cust_address") private String cust_address; // '客戶聯繫地址', @Column(name="cust_phone") private String cust_phone; // '客戶聯繫電話 // 有多的一方的集合 /*targetEntity:對方的類型 * mappedBy:自己在對方里的屬性名稱 (mappedBy寫在哪方,哪方意味著放棄外鍵的維護) * * */ @OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.REMOVE) private Set<LinkMan> linkmans=new HashSet(); @Entity @Table(name="cst_linkman") public class LinkMan { @Id @Column(name="lkm_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long lkm_id;// '聯繫人編號(主鍵)', @Column(name="lkm_name") private String lkm_name;// '聯繫人姓名', @Column(name="lkm_gender") private String lkm_gender;// '聯繫人性別', @Column(name="lkm_phone") private String lkm_phone;// '聯繫人辦公電話', @Column(name="lkm_mobile") private String lkm_mobile;// '聯繫人手機', @Column(name="lkm_email") private String lkm_email;// '聯繫人郵箱', @Column(name="lkm_qq") private String lkm_qq;// '聯繫人qq', @Column(name="lkm_position") private String lkm_position;// '聯繫人職位', @Column(name="lkm_memo") private String lkm_memo;// '聯繫人備註', // 有一的一方的對象 PERSIST @ManyToOne(targetEntity=Customer.class,cascade=CascadeType.PERSIST) /*name:代表著外鍵欄位的名稱*/ /*referencedColumnName:指向的主鍵欄位的命名稱*/ @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") private Customer customer; 多表對多表的註解配置: @Entity @Table(name="sys_user") public class User { @Id @Column(name="user_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long user_id;// '用戶id', @Column(name="user_code") private String user_code;// '用戶帳號', @Column(name="user_name") private String user_name;// '用戶名稱', @Column(name="user_password") private String user_password;// '用戶密碼', @Column(name="user_state") private String user_state;// '1:正常,0:暫停', // 有角色的集合 /*targetEntity:對方的類型 * * * */ @ManyToMany(targetEntity=Role.class,cascade=CascadeType.ALL) /*name: 中間表的名稱 joinColumns:自己在中間表的一些配置 inverseJoinColumns:對方在中間表的一些配置*/ @JoinTable(name="sys_user_role", joinColumns={ /*name:自己在中間表的外鍵欄位名稱 referencedColumnName:指向自己的主鍵欄位名*/ @JoinColumn(name="user_id",referencedColumnName="user_id") }, inverseJoinColumns={ /*name:對方在中間表的外鍵欄位名稱 referencedColumnName:指向的對方的主鍵欄位名稱*/ @JoinColumn(name="role_id",referencedColumnName="role_id") }) private Set<Role> roles=new HashSet(); @Entity @Table(name="sys_role") public class Role { @Id @Column(name="role_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long role_id;// 主鍵id @Column(name="role_name") private String role_name;// '角色名稱', @Column(name="role_memo") private String role_memo;// '備註', // 有用戶的集合 /*targetEntity:對方的類型 * mappedBy:自己在對方的屬性名 * */ @ManyToMany(targetEntity=User.class,mappedBy="roles") private Set<User> users=new HashSet(); JPA需要在項目src下新建一個META-INF文件夾在文件夾裡面配置以下資訊: 例如 : <?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"> <!-- ps:當前根目錄下必須得有一個持久化單元(至少要有一個資料庫的連接資訊配置) --> <persistence-unit name="aaa"> <!-- 資料庫的連接資訊 --> <properties> <!-- 必選5項 --> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.url" value="jdbc:mysql:///hibernate3"></property> <property name="hibernate.connection.username" value="root"></property> <property name="hibernate.connection.password" value="1234"></property> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"></property> <!-- 可選的 --> <property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"></property> <property name="hibernate.show_sql" value="true"></property> <property name="hibernate.format_sql" value="true"></property> <property name="hibernate.hbm2ddl.auto" value="update"></property> </properties> </persistence-unit> </persistence> JPA單表操作 : 例如 : package cn.baidu.demo; public class Demo1 { @Test //需求:保存一個客戶 public void t1() { // 載入配置文件--返回一個類似Sessionfactory的對象 EntityManagerFactory factory = Persistence.createEntityManagerFactory("aaa"); // 類似session EntityManager em = factory.createEntityManager(); // 獲取事務 EntityTransaction tx = em.getTransaction(); // 事務未開啟 // 開啟事務 tx.begin(); // 保存操作 Customer ct = new Customer(); ct.setCust_name("李冰冰"); em.persist(ct); // 類似save() // 手動提交 tx.commit(); // 釋放資源 em.close(); } @Test //查詢1 public void t2() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("aaa"); EntityManager em = factory.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 查詢 Customer ct = em.find(Customer.class, 1L); // 立即載入 類似get() System.out.println(ct); tx.commit(); em.close(); } @Test //查詢2 public void t3() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("aaa"); EntityManager em = factory.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 查詢2 Customer ct = em.getReference(Customer.class, 1L); //延遲載入 類似load() System.out.println(ct); tx.commit(); em.close(); } @Test //修改 public void t4() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 修改 Customer ct = em.find(Customer.class, 1L); ct.setCust_name("rose"); em.merge(ct); //類似update tx.commit(); em.close(); } @Test //修改 jpa支援一級快取? 支援 public void t5() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 修改 Customer ct = em.find(Customer.class, 1L); ct.setCust_name("rose123"); tx.commit(); em.close(); } @Test //刪除 public void t6() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); //刪除 Customer ct = em.find(Customer.class, 1L); em.remove(ct); // 類似之前的delete() tx.commit(); em.close(); } /// jpa的批量查詢方式 // 類似咱們之前學習的query方式 // 1 qr.getResultList(); //類似之前的 qr.list() // 2 hibernate對於佔位符?是從0開始 jpa是從1開始 // 3 聚合 qr.getSingleResult(); //類似之前的 qr.uniqueResult(); @Test public void t7() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 全查 Query qr = em.createQuery("from Customer"); List<Customer> list = qr.getResultList(); //類似之前的 qr.list() for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t8() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 條件 Query qr = em.createQuery("from Customer where cust_name like ?"); qr.setParameter(1, "b%"); // 注意:hibernate對於佔位符?是從0開始 jpa是從1開始 List<Customer> list = qr.getResultList(); for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t9() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 分頁 Query qr = em.createQuery("from Customer"); qr.setFirstResult(1); qr.setMaxResults(3); List<Customer> list = qr.getResultList(); for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t10() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 單列 Query qr = em.createQuery("select cust_name from Customer"); List<Object> list = qr.getResultList(); for (Object object : list) { System.out.println(object); } tx.commit(); em.close(); } @Test public void t11() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 多列 Query qr = em.createQuery("select cust_id,cust_name from Customer"); List<Object[]> list = qr.getResultList(); for (Object[] object : list) { System.out.println(Arrays.toString(object)); } tx.commit(); em.close(); } @Test public void t12() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 投影 Query qr = em.createQuery("select new Customer(cust_id,cust_name) from Customer"); List<Customer> list = qr.getResultList(); for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t13() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 排序 Query qr = em.createQuery("from Customer order by cust_id desc"); List<Customer> list = qr.getResultList(); for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t14() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 聚合 Query qr = em.createQuery("select avg(cust_id) from Customer"); Object obj = qr.getSingleResult(); System.out.println(obj); tx.commit(); em.close(); } } JPA一對多表的操作 : /* 一對多的操作*/ public class Demo2 { @Test // 保存一的客戶3個聯繫人 public void t1() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = new Customer(); ct.setCust_name("馬總"); LinkMan l1 = new LinkMan(); l1.setLkm_name("大秘"); LinkMan l2 = new LinkMan(); l2.setLkm_name("中秘"); LinkMan l3 = new LinkMan(); l3.setLkm_name("小秘"); // 雙向關聯 ct.getLinkmans().add(l1); ct.getLinkmans().add(l2); ct.getLinkmans().add(l3); l1.setCustomer(ct); l2.setCustomer(ct); l3.setCustomer(ct); //保存 em.persist(ct); em.persist(l1); em.persist(l2); em.persist(l3); tx.commit(); em.close(); } @Test // 級聯保存 (保存客戶的同時把關聯的聯繫人給保存了) // jpa的註解裡面 @OneToMany 添加屬性cascade=CascadeType.PERSIST public void t2() // 根據一的一方保存多的一方的數據(掌握) { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = new Customer(); ct.setCust_name("馬總"); LinkMan l1 = new LinkMan(); l1.setLkm_name("大秘"); LinkMan l2 = new LinkMan(); l2.setLkm_name("中秘"); LinkMan l3 = new LinkMan(); l3.setLkm_name("小秘"); // 雙向關聯 ct.getLinkmans().add(l1); ct.getLinkmans().add(l2); ct.getLinkmans().add(l3); l1.setCustomer(ct); l2.setCustomer(ct); l3.setCustomer(ct); //保存 em.persist(ct); /*em.persist(l1); em.persist(l2); em.persist(l3);*/ tx.commit(); em.close(); } @Test // 級聯保存 (保存聯繫人的同時把關聯的客戶給保存了) // jpa的註解裡面 @ManyToOne 添加屬性cascade=CascadeType.PERSIST public void t3() // 根據多的一方保存一的一方的數據(不常用) { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = new Customer(); ct.setCust_name("馬總"); LinkMan l1 = new LinkMan(); l1.setLkm_name("大秘"); LinkMan l2 = new LinkMan(); l2.setLkm_name("中秘"); LinkMan l3 = new LinkMan(); l3.setLkm_name("小秘"); // 雙向關聯 ct.getLinkmans().add(l1); ct.getLinkmans().add(l2); ct.getLinkmans().add(l3); l1.setCustomer(ct); l2.setCustomer(ct); l3.setCustomer(ct); //保存 /*em.persist(ct);*/ em.persist(l1); em.persist(l2); em.persist(l3); tx.commit(); em.close(); } @Test // 普通刪除 public void t4() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = em.find(Customer.class, 4L); em.remove(ct); tx.commit(); em.close(); } @Test // 級聯刪除 // jpa的註解裡面 @OneToMany 添加屬性cascade=CascadeType.REMOVE (All) public void t5() //根據一的一方刪除關聯的多的一方的所有數據(掌握) { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = em.find(Customer.class, 4L); em.remove(ct); tx.commit(); em.close(); } } JPA多表對多表的操作 : package cn.baidu.demo; import javax.persistence.CascadeType; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import javax.persistence.ManyToMany; import org.junit.Test; import cn.baidu.domain.Role; import cn.baidu.domain.User; import cn.baidu.utils.JPAutils; /* 多對多的操作*/ public class Demo3 { @Test //普通保存 保存2個用戶 3個角色 public void t1() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user1 = new User(); user1.setUser_name("jack"); User user2 = new User(); user2.setUser_name("rose"); Role r1 = new Role(); r1.setRole_name("員工"); Role r2 = new Role(); r2.setRole_name("班主任"); Role r3 = new Role(); r3.setRole_name("助教"); // 雙向關聯 user1.getRoles().add(r1); user1.getRoles().add(r3); user2.getRoles().add(r1); user2.getRoles().add(r2); r1.getUsers().add(user1); r1.getUsers().add(user2); r2.getUsers().add(user2); r3.getUsers().add(user1); // 保存 em.persist(user1); em.persist(user2); em.persist(r1); em.persist(r2); em.persist(r3); tx.commit(); em.close(); } // jpa多對多的級聯操作 // 級聯保存: 保存用戶的同時把關聯的角色給保存了(不用) // @ManyToMany 添加cascade=cascade=CascadeType.PERSIST @Test public void t2() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user1 = new User(); user1.setUser_name("jack"); User user2 = new User(); user2.setUser_name("rose"); Role r1 = new Role(); r1.setRole_name("員工"); Role r2 = new Role(); r2.setRole_name("班主任"); Role r3 = new Role(); r3.setRole_name("助教"); // 雙向關聯 user1.getRoles().add(r1); user1.getRoles().add(r3); user2.getRoles().add(r1); user2.getRoles().add(r2); r1.getUsers().add(user1); r1.getUsers().add(user2); r2.getUsers().add(user2); r3.getUsers().add(user1); // 保存 em.persist(user1); em.persist(user2); /*em.persist(r1); em.persist(r2); em.persist(r3);*/ tx.commit(); em.close(); } @Test // 普通刪除(常用) public void t3() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = em.find(User.class, 1L); em.remove(user); tx.commit(); em.close(); } @Test // 級聯刪除(避免去使用) public void t4() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = em.find(User.class, 2L); em.remove(user); tx.commit(); em.close(); } // jpa的用戶角色分配 // 添加角色 // 刪除角色 // 修改角色 @Test public void t5() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 獲取用戶 User user = em.find(User.class, 3L); // 獲取班主任 Role rl = em.find(Role.class, 6L); //給用戶添加 user.getRoles().add(rl); tx.commit(); em.close(); } } 總結: JPA的作用? 給所有的orm框架提供了一套介面 好處: 所有的ORM框架只要實現了這個JPA介面,用來操作資料庫數據的方式和方法以及註解都一致了 jpa的環境搭建: 在hibernate的環境基礎上多加一個包--hibernate-entitymanager-5.0.7.Final.jar 單表的映射 @Entity 實體類 @Table(name="cst_customer") 與表的映射 @Id 指定OID屬性 @Column(name="cust_id") 指定映射的欄位 @GeneratedValue(strategy=GenerationType.IDENTITY) 指定主鍵的生成策略 crud: persist() ----保存 find() : 立即載入 getReference():延遲載入 -----單條數據的oid查詢 merge() ---修改 remove() ---刪除 批量查詢: 類似之前的query方式 一對多: 一: @OneToMany(targetEntity=LinkMan.class,mappedBy="customer") 多: @ManyToOne(targetEntity=LinkMan.class) 一對多的關係配置: @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") 多對多: 多(被動): @ManyToMany(targetEntity=User.class,mappedBy="roles") 多(主動): @ManyToMany(targetEntity=Role.class) 多對多的關係配置: @JoinTable(name="中間表的名稱",joinColumns="自己在中間表的配置(數組)" inverseJoinColumns="對方在中間表的配置(數組)") 級聯: cascade=CascadeType.ALL 做級聯保存以及級聯刪除 cascade=CascadeType.PERSIST 只做級聯保存 cascade=CascadeType.REMOVE 只做級聯刪除