http://www.cnblogs.com/Kevin-ZhangCG/p/8996491.html
一、JPA概述以及它和Hibernate之間的關系
1.1.Hibernate 概述
JPA Java Persistence API,是EJB3規范中負責對象持久化的應用程序編程接口(ORM接口),它定義一系列的注釋。這些注釋大體可分為:類級別注釋、方法級別注釋、字段級別注釋。給實體類添加適當的注釋可以在程序運行時告訴Hibernate如何將一個實體類保存到數據庫中以及如何將數據以對象的形式從數據庫中讀取出來。
目前有兩種注釋方案可以確定對象與表格之間的對應關系:一種是注釋實體類的屬性字段(字段級別注釋),成為字段訪問方式(field access mode);另一種是注釋實體類的屬性訪問方法(方法級別注釋),稱為屬性訪問方式(property access mode)。
1.2 JPA與Hibernate 的區別
JPA和Hibernate之間的關系,可以簡單的理解為JPA是標准接口,Hibernate是實現。
那么Hibernate是如何實現與JPA的這種關系的呢。Hibernate主要是通過三個組件來實現的,及hibernate-annotation、hibernate-entitymanager和hibernate-core。
- hibernate-annotation:是Hibernate支持annotation方式配置的基礎,它包括了標准的JPA annotation以及Hibernate自身特殊功能的annotation。
- hibernate-core:是Hibernate的核心實現,提供了Hibernate所有的核心功能。
- hibernate-entitymanager:實現了標准的JPA,可以把它看成hibernate-core和JPA之間的適配器,它並不直接提供ORM的功能,而是對hibernate-core進行封裝,使得Hibernate符合JPA的規范。
二、JPA的環境搭建
2.1 主要配置文件
使用JPA可以省去配置每個實體類的.xml 文件,只需直接在實體類中用注解的方式直接說明即可。先在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">
<!-- 配置持久化單元
可以制定多個持久化單元,但名稱不可重復,name用於指定持久化單元名稱
transaction-type:指定事務的類型
RESCOURCE_LOCAL:指的是本地代碼事務
-->
<persistence-unit name="myJPAUnit" transaction-type="RESOURCE_LOCAL">
<!-- JPA規范的提供商 可以不寫 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 指定由JPA注解的實體類位置 可以不寫-->
<class>com.Kevin.domain.Customer</class>
<!-- 連接數據庫相關的一些配置,都是Hibernate的,所以只需要把之前Hibernate配置文件中的內容拷貝過來即可 -->
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql:///hibernateday4"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="admin"/>
<!-- Hibernate顯示SQL語句 -->
<property name="hibernate.show_sql" value="true"/>
<!-- Hibernate格式化SQL語句 -->
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
</properties>
</persistence-unit>
</persistence>
獲取JPA操作數據庫的對象
在src目錄下com.Kevin.utils包中創建JPAUtil類用來獲取數據庫操作對象,文件目錄如下圖:

獲取方式如下:
package com.Kevin.utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* JPA的工具類
* @author Kevin
*
*/
public class JPAUtil {
//相當於SessionFactory
private static EntityManagerFactory factory;
private static ThreadLocal<EntityManager> tl;
static{
factory=Persistence.createEntityManagerFactory("myJPAUnit"); //名稱要與persistence.xml文件中的持久化單元name一致
tl=new ThreadLocal<EntityManager>();
}
/**
* 獲取JPA操作數據庫的對象
*/
// public static EntityManager createEntityManager(){
// return factory.createEntityManager();
// }
/**
* 獲取EntityManager對象
* @return
*/
public static EntityManager createEntityManager(){
//從當前線程上獲取EntityManager對象
EntityManager em=tl.get();
if(em==null){
em=factory.createEntityManager();
tl.set(em);
}
return tl.get();
}
public static void main(String[]args){
createEntityManager();
}
}
2.2 基本映射注釋簡介
2.2.1 @Entity:映射實體類(必須)
@Entity(name="EntityName") ,name 為可選 , 對應數據庫中一的個表。
2.2.2 @Table:映射數據庫表名(可選)
@Table(name="",catalog="",schema="") , 通常和 @Entity 配合使用 , 只能標注在實體的 class 定義處 , 表示實體對應的數據庫表的信息。
name: 可選 , 表示表的名稱 . 默認地 , 表名和實體名稱一致 , 只有在不一致的情況下才需要指定表名。
catalog: 可選 , 表示 Catalog 名稱 , 默認為 Catalog(""). schema: 可選 , 表示 Schema 名稱 , 默認為 Schema(“”)。
2.2.3 @Id:映射生成主鍵(必選)
@id 定義了映射到數據庫表的主鍵的屬性 , 一個實體只能有一個屬性被映射為主鍵 。置於 getXxxx() 前 。
@GeneratedValue(strategy=GenerationType,generator="") 可選 strategy: 表示主鍵生成策略 , 有 AUTO,INDENTITY,SEQUENCE 和 TABLE 4 種 , 分別表示讓 ORM 框架自動選擇。根據數據庫的 Identity 字段生成 , 根據數據庫表的 Sequence 字段生成 , 以有根據一個額外的表生成主鍵 , 默認為 AUTO 。
generator: 表示主鍵生成器的名稱 , 這個屬性通常和 ORM 框架相關 , 例如 ,Hibernate 可以指定 uuid 等主鍵生成方式。
2.2.4 @Column:映射表格列(可選)
@Column 描述了數據庫表中該字段的詳細定義 , 這對於根據 JPA 注解生成數據庫表結構的工具非常有作用 。
name: 表示數據庫表中該字段的名稱 , 默認情形屬性名稱一致 。nullable: 表示該字段是否允許為 null, 默認為 true。
unique: 表示該字段是否是唯一標識 , 默認為 false length: 表示該字段的大小 , 僅對 String 類型的字段有效。
insertable: 表示在 ORM 框架執行插入操作時 , 該字段是否應出現 INSETRT 語句中 , 默認為 true。
updateable: 表示在 ORM 框架執行更新操作時 , 該字段是否應該出現在 UPDATE 語句中 , 默認為 true. 對於一經創建就不可以更改的字段 , 該屬性非常有用 , 如對於 birthday 字段 。
columnDefinition: 表示該字段在數據庫中的實際類型 . 通常 ORM 框架可以根據屬性類型自動判斷數據庫中字段的類型 , 但是對於 Date 類型仍無法確定數據庫中字段類型究竟是 DATE,TIME 還是 TIMESTAMP. 此外 ,String 的默認映射類型為 VARCHAR, 如果要將 String 類型映射到特定數據庫的 BLOB 或 TEXT 字段類型 , 該屬性非常有用 .。
2.2.5 @Transient:定義暫態屬性(可選)
@Transient 表示該屬性並非一個到數據庫表的字段的映射 ,ORM 框架將忽略該屬性。如果一個屬性並非數據庫表的字段映射 , 就務必將其標示為 @Transient, 否則 ,ORM 框架默認其注解為 @Basic。
2.3 關聯類映射注釋簡介
2.3.1 @ManyToOne(可選)
@ManyToOne(fetch=FetchType,cascade=CascadeType )
@ManyToOne 表示一個多對一的映射 , 該注解標注的屬性通常是數據庫表的外鍵 。
optional: 是否允許該字段為 null, 該屬性應該根據數據庫表的外鍵約束來確定 , 默認為 true 。
fetch: 表示抓取策略 , 默認為 FetchType.EAGER cascade: 表示默認的級聯操作策略 , 可以指定為 ALL,PERSIST,MERGE,REFRESH 和 REMOVE 中的若干組合 , 默認為無級聯操作 。
targetEntity: 表示該屬性關聯的實體類型 . 該屬性通常不必指定 ,ORM 框架根據屬性類型自動判斷 targetEntity。
2.3.2 @JoinColumn(可選)
@JoinColumn 和 @Column 類似 , 介量描述的不是一個簡單字段 , 而一一個關聯字段 , 例如 . 描述一個 @ManyToOne 的字段 。
name: 該字段的名稱 . 由於 @JoinColumn 描述的是一個關聯字段 , 如 ManyToOne, 則默認的名稱由其關聯的實體決定。例如 , 實體 Order 有一個 user 屬性來關聯實體 User, 則 Order 的 user 屬性為一個外鍵 , 其默認的名稱為實體 User 的名稱 + 下划線 + 實體 User 的主鍵名稱
2.3.3 @OneToMany
@OneToMany(fetch=FetchType,cascade=CascadeType)
@OneToMany 描述一個一對多的關聯 , 該屬性應該為集體類型 , 在數據庫中並沒有實際字段 。
fetch: 表示抓取策略 , 默認為 FetchType.LAZY, 因為關聯的多個對象通常不必從數據庫預先讀取到內存。
cascade: 表示級聯操作策略 , 對於 OneToMany 類型的關聯非常重要 , 通常該實體更新或刪除時 , 其關聯的實體也應當被更新或刪除 例如 : 實體 User 和 Order 是 OneToMany 的關系 , 則實體 User 被刪除時 , 其關聯的實體 Order 也應該被全部刪除
2.3.4 @OneToOne(可選)
@OneToOne(fetch=FetchType,cascade=CascadeType)
@OneToOne 描述一個一對一的關聯 。
fetch: 表示抓取策略 , 默認為 FetchType.LAZY 。
cascade: 表示級聯操作策略。
2.3.5 @ ManyToMany(可選)
@ManyToMany 描述一個多對多的關聯 . 多對多關聯上是兩個一對多關聯 , 但是在 ManyToMany 描述中 , 中間表是由 ORM 框架自動處理。
targetEntity: 表示多對多關聯的另一個實體類的全名 , 例如 :package.Book.class。
mappedBy: 表示多對多關聯的另一個實體類的對應集合屬性名稱。
三、JPA入門案例和CRUD操作(單表)
3.1 創建客戶實體類
package com.Kevin.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 客戶的實體類
* @author Kevin
* 使用的注解都是JPA規范,所以導包要導入javax.persistence包下
*
*/
@Entity //表名該類是一個實體類
@Table(name="cst_customer") //建立當前類和數據庫的對應關系
public class Customer{
@Id //
@Column(name="cust_id") //表明對應數據庫的主鍵字段是cust_id
@GeneratedValue(strategy=GenerationType.IDENTITY) //指定主鍵生成策略,strategy:使用JPA中提供的主鍵生成策略,此屬性是用不了;generator屬性可以使用hibernate主鍵生成策略
private Long cust_id;
@Column(name="cust_name")
private String cust_name;
@Column(name="cust_address")
private String cust_address;
@Column(name="cust_industry")
private String cust_industry;
@Column(name="cust_level")
private String cust_level;
@Column(name="cust_phone")
private String cust_phone;
@Column(name="cust_mobile")
private String cust_mobile;
public Long getCust_id() {
return cust_id;
}
public void setCust_id(Long cust_id) {
this.cust_id = cust_id;
}
public String getCust_name() {
return cust_name;
}
public void setCust_name(String cust_name) {
this.cust_name = cust_name;
}
public String getCust_address() {
return cust_address;
}
public void setCust_address(String cust_address) {
this.cust_address = cust_address;
}
public String getCust_industry() {
return cust_industry;
}
public void setCust_industry(String cust_industry) {
this.cust_industry = cust_industry;
}
public String getCust_level() {
return cust_level;
}
public void setCust_level(String cust_level) {
this.cust_level = cust_level;
}
public String getCust_phone() {
return cust_phone;
}
public void setCust_phone(String cust_phone) {
this.cust_phone = cust_phone;
}
public String getCust_mobile() {
return cust_mobile;
}
public void setCust_mobile(String cust_mobile) {
this.cust_mobile = cust_mobile;
}
@Override
public String toString() {
return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_address=" + cust_address
+ ", cust_industry=" + cust_industry + ", cust_level=" + cust_level + ", cust_phone=" + cust_phone
+ ", cust_mobile=" + cust_mobile + "]";
}
}
3.2 編寫CRUD測試代碼
3.2.1 保存操作
//保存操作
@Test
public void test1(){
//創建客戶對象
Customer c=new Customer();
c.setCust_name("Kevin");
//1.獲取EntityManager
EntityManager em=JPAUtil.createEntityManager();
//2.獲取事務對象,並開啟事務
EntityTransaction tx=em.getTransaction();
tx.begin();
//3.執行保存操作
em.persist(c);
//4.提交事務
tx.commit();
//5.關閉資源
em.close();
}
3.2.2 更新操作(兩種方式)
第一種:正常更新方式(update)
@Test
public void test3(){
//1.獲取EntityManager
EntityManager em=JPAUtil.createEntityManager();
//2.獲取事務對象,並開啟事務
EntityTransaction tx=em.getTransaction();
tx.begin();
//3.執行更新操作(將需要更新的對象查詢出來)
Customer c=em.find(Customer.class, 1l);
//更新客戶地址
c.setCust_address("China");
//4.提交事務
tx.commit();
//5.關閉資源
em.close();
}
第二種:合並方式保存(merge,將兩個實體合並)
@Test
public void test4(){
//1.獲取EntityManager
EntityManager em=JPAUtil.createEntityManager();
//2.獲取事務對象,並開啟事務
EntityTransaction tx=em.getTransaction();
tx.begin();
//3.執行更新操作(將需要更新的對象查詢出來)
Customer c=em.find(Customer.class, 1l);
//更新客戶地址
c.setCust_mobile("66666666");;
em.merge(c);
//4.提交事務
tx.commit();
//5.關閉資源
em.close();
}
3.2.3 刪除操作
//刪除
@Test
public void test5(){
//獲取EntityManager對象
EntityManager em=JPAUtil.createEntityManager();
//獲取事務對象,並開啟事務
EntityTransaction tx=em.getTransaction();
tx.begin();
//獲取操作對象
Customer c=em.find(Customer.class, 1l);
//刪除對象
em.remove(c);
//提交事務
tx.commit();
//關閉資源
em.close();
}
3.2.4 基本查詢
//查詢所有
/**
* 涉及的對象:
* JPA的Query:
* 如何獲取對象:EntityManager的createQuery(String sql)
* 參數含義:JPAL:Java Persistence Query Language
* 寫法與HQL很相似,也是把表名換成類名,把字段名換成屬性名稱
* 在寫查詢所有時,不能直接用 from 類
* 需要使用select關鍵字
*/
@Test
public void test6(){
//獲取EntityManager對象
EntityManager em=JPAUtil.createEntityManager();
//獲取事務對象,並開啟事務
EntityTransaction tx=em.getTransaction();
tx.begin();
//獲取JPA的查詢對象Query
Query query=em.createQuery("select c from Customer c"); //c是一個別名
//執行方法獲取結果集
List list=query.getResultList();
for(Object o:list)
System.out.println(o);
//提交事務
tx.commit();
//關閉資源
em.close();
}
3.2.5 條件查詢
@Test
public void test7(){
//獲取EntityManager對象
EntityManager em=JPAUtil.createEntityManager();
//獲取事務對象,並開啟事務
EntityTransaction tx=em.getTransaction();
tx.begin();
//獲取JPA的查詢對象Query
Query query=em.createQuery("select c from Customer c where cust_name like ?");
//給占位符賦值
query.setParameter(1, "%k%");
//執行方法獲取結果集
List list=query.getResultList();
for(Object o:list)
System.out.println(o);
//提交事務
tx.commit();
//關閉資源
em.close();
}
//多條件查詢
@Test
public void test8(){
//獲取EntityManager對象
EntityManager em=JPAUtil.createEntityManager();
//獲取事務對象,並開啟事務
EntityTransaction tx=em.getTransaction();
tx.begin();
//獲取JPA的查詢對象Query
Query query=em.createQuery("select c from Customer c where cust_name like ? and cust_level = ? ");
//給占位符賦值
query.setParameter(1, "%K%");
query.setParameter(2, "5");
//執行方法獲取結果集
List list=query.getResultList();
for(Object o:list)
System.out.println(o);
//提交事務
tx.commit();
//關閉資源
em.close();
}
TIps: 1. persist()方法相當於是save()操作;
2. remove()對應的是delete();
3. find()方法對應get()方法;
4. getReference()是延遲加載;
5. find()是立即加載;
6. uniqueResult()對應getSingleResult(),返回唯一的結果。
7. merge()和update()相似,但是merge干的活update有些不能干;
3.2.6 區別merge和update
當查詢了一個對象后,關閉session,再次查詢了該對象,並且修改了該對象。此時如果使用update方法時會報錯,因為第一次查完后關閉了session,對象的狀態轉變成了托管態,而此時查詢該對象,修改的時候是持久態,對象的狀態是不一樣的,在一級緩存外邊還有一個修改對象。此時更新的話,由於兩個對象的OID是一樣的,但是卻發生了修改,使用update的話,兩個對象是不能合並的,只能用merge()方法將其更新,即將兩個對象合並。
@Test
public void test10(){
/**
* 查詢ID為1的客戶
* 關閉EntityManager(清空了一級緩存和快照)
* 修改id為1的客戶的地址為America
* 在此獲取EntityManager
* 再次查詢ID為1的客戶
* 更新剛才修改的客戶
*/
//獲取EntityManager對象
EntityManager em=JPAUtil.createEntityManager();
//獲取事務對象,並開啟事務
EntityTransaction tx=em.getTransaction();
tx.begin();
Customer c=em.find(Customer.class, 1L);//持久態
tx.commit();
em.close();
//修改客戶信息
c.setCust_address("America");//托管態
//獲取EntityManager對象
EntityManager em1=JPAUtil.createEntityManager();
//獲取事務對象,並開啟事務
EntityTransaction tx1=em.getTransaction();
tx1.begin();
//再次查詢
Customer c1=em.find(Customer.class, 1L);//持久態
//更新操作
em1.persist(c);//將托管態轉換為持久態,update(persist)方法是不行的,必須要用到merge方法才可以的
em1.merge(c);
tx1.commit();
em1.close();
}
四、 JPA中實體一對多映射配置及操作
4.1 一對多實體類注解編寫
客戶實體類:
package com.Kevin.domain;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
* 客戶的實體類
* @author Kevin
* 使用的注解都是JPA規范,所以導包要導入javax.persistence包下
*
*/
@Entity //表情該類是一個實體類
@Table(name="cst_customer") //建立當前類和數據庫的對應關系
public class Customer{
@Id //
@Column(name="cust_id") //表明對應數據庫的主鍵字段是cust_id
@GeneratedValue(strategy=GenerationType.IDENTITY) //指定主鍵生成策略,strategy:使用JPA中提供的主鍵生成策略,此屬性是用不了;generator屬性可以使用hibernate主鍵生成策略
private Long cust_id;
@Column(name="cust_name")
private String cust_name;
@Column(name="cust_address")
private String cust_address;
@Column(name="cust_industry")
private String cust_industry;
@Column(name="cust_level")
private String cust_level;
@Column(name="cust_phone")
private String cust_phone;
@Column(name="cust_mobile")
private String cust_mobile;
//一對多關系映射:一個客戶可以有多個聯系人
//targetEntity=Linkman.class 可以不寫
@OneToMany(targetEntity=Linkman.class,mappedBy="customer",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Set<Linkman> linkmans=new HashSet<Linkman>(0);
public Long getCust_id() {
return cust_id;
}
public void setCust_id(Long cust_id) {
this.cust_id = cust_id;
}
public String getCust_name() {
return cust_name;
}
public void setCust_name(String cust_name) {
this.cust_name = cust_name;
}
public String getCust_address() {
return cust_address;
}
public void setCust_address(String cust_address) {
this.cust_address = cust_address;
}
public String getCust_industry() {
return cust_industry;
}
public void setCust_industry(String cust_industry) {
this.cust_industry = cust_industry;
}
public String getCust_level() {
return cust_level;
}
public void setCust_level(String cust_level) {
this.cust_level = cust_level;
}
public String getCust_phone() {
return cust_phone;
}
public void setCust_phone(String cust_phone) {
this.cust_phone = cust_phone;
}
public String getCust_mobile() {
return cust_mobile;
}
public void setCust_mobile(String cust_mobile) {
this.cust_mobile = cust_mobile;
}
public Set<Linkman> getLinkmans() {
return linkmans;
}
public void setLinkmans(Set<Linkman> linkmans) {
this.linkmans = linkmans;
}
@Override
public String toString() {
return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_address=" + cust_address
+ ", cust_industry=" + cust_industry + ", cust_level=" + cust_level + ", cust_phone=" + cust_phone
+ ", cust_mobile=" + cust_mobile + "]";
}
}
聯系人實體類:
package com.Kevin.domain;
/**
* 創建聯系人實體類
* `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '聯系人編號(主鍵)',
`lkm_name` varchar(16) DEFAULT NULL COMMENT '聯系人姓名',
`lkm_cust_id` bigint(32) NOT NULL COMMENT '客戶id',
`lkm_gender` char(1) DEFAULT NULL COMMENT '聯系人性別',
`lkm_phone` varchar(16) DEFAULT NULL COMMENT '聯系人辦公電話',
`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '聯系人手機',
`lkm_email` varchar(64) DEFAULT NULL COMMENT '聯系人郵箱',
`lkm_qq` varchar(16) DEFAULT NULL COMMENT '聯系人qq',
`lkm_position` varchar(16) DEFAULT NULL COMMENT '聯系人職位',
`lkm_memo` varchar(512) DEFAULT NULL COMMENT '聯系人備注',
*/
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name="cst_linkman")
public class Linkman implements Serializable {
@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_mobile")
private String lkm_mobile;
@Column(name="lkm_phone")
private String lkm_phone;
@Column(name="lkm_email")
private String lkm_email;
@Column(name="lkm_qq")
private String lkm_qq;
@Column(name="lkm_position")
private String lkm_position;
@Column(name="lkm_memo")
private String lkm_memo;
//一對多關系影射
//從表實體包含主表實體的對象引用
@ManyToOne(targetEntity=Customer.class,cascade=CascadeType.ALL)
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
private Customer customer;
public Long getLkm_id() {
return lkm_id;
}
public void setLkm_id(Long lkm_id) {
this.lkm_id = lkm_id;
}
public String getLkm_name() {
return lkm_name;
}
public void setLkm_name(String lkm_name) {
this.lkm_name = lkm_name;
}
public String getLkm_gender() {
return lkm_gender;
}
public void setLkm_gender(String lkm_gender) {
this.lkm_gender = lkm_gender;
}
public String getLkm_mobile() {
return lkm_mobile;
}
public void setLkm_mobile(String lkm_mobile) {
this.lkm_mobile = lkm_mobile;
}
public String getLkm_phone() {
return lkm_phone;
}
public void setLkm_phone(String lkm_phone) {
this.lkm_phone = lkm_phone;
}
public String getLkm_email() {
return lkm_email;
}
public void setLkm_email(String lkm_email) {
this.lkm_email = lkm_email;
}
public String getLkm_qq() {
return lkm_qq;
}
public void setLkm_qq(String lkm_qq) {
this.lkm_qq = lkm_qq;
}
public String getLkm_position() {
return lkm_position;
}
public void setLkm_position(String lkm_position) {
this.lkm_position = lkm_position;
}
public String getLkm_memo() {
return lkm_memo;
}
public void setLkm_memo(String lkm_memo) {
this.lkm_memo = lkm_memo;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
@Override
public String toString() {
return "Linkman [lkm_id=" + lkm_id + ", lkm_name=" + lkm_name + ", lkm_gender=" + lkm_gender + ", lkm_mobile="
+ lkm_mobile + ", lkm_phone=" + lkm_phone + ", lkm_email=" + lkm_email + ", lkm_qq=" + lkm_qq
+ ", lkm_position=" + lkm_position + ", lkm_memo=" + lkm_memo + "]";
}
}
4.2 一對多關聯關系的相關操作
4.2.1 保存操作
/**
* 保存操作
* 創建一個客戶和一個聯系人
* 建立客戶和聯系人的雙向關聯關系
* 先保存客戶,在保存聯系人
*/
@Test
public void test1(){
Customer c=new Customer();
c.setCust_name("Kevin_one2many");
Linkman lkm=new Linkman();
lkm.setLkm_name("Kevin_onw2many");
c.getLinkmans().add(lkm);
lkm.setCustomer(c);
EntityManager em=JPAUtil.creatEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
em.persist(c);
em.persist(lkm);
tx.commit();
em.close();
}
4.2.2 更新操作
此時需要配置級聯操作:要級聯操作哪一方就應該在那一方的上邊進行注解配置cascade=CsacadeType.PERSIST屬性,即保存或者更新客戶的同時保存聯系人,但時cascade=CsacadeType.PERSIST只是級聯更新。

其中mappedBy是映射來自,相當於inverse,即主表不在關心從表的信息,讓聯系人去維護
/**
* 更新操作
* 創建一個聯系人
* 查詢id為*客戶
* 為該客戶分配該聯系人
* 更新客戶
*/
@Test
public void test2(){
Linkman lkm=new Linkman();
lkm.setLkm_name("Kevin_onw2many3");
EntityManager em=JPAUtil.creatEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
Customer c=em.find(Customer.class, 7l);
c.getLinkmans().add(lkm);
lkm.setCustomer(c);
//em.merge(c);
tx.commit();
em.close();
}
4.2.3 刪除操作
刪除主表:若在客戶上邊配置了放棄維護,即mappedBy="customer",直接刪除指標會報錯,若此時還想要刪除的話,需要配置cascade=CascadeType.DELET或者cascade=CascadeType.ALL就可以刪除。

需要注意的是:聯系人(從表)也可以配置cascade=CascadeType.ALL來進行操作。
/**
* 刪除操作
*/
@Test
public void test3(){
EntityManager em=JPAUtil.creatEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
Customer c=em.find(Customer.class, 6l);
em.remove(c);
tx.commit();
em.close();
}
4.2.4 查詢操作
JPA中也可以使用對象導航查詢,也可以設置查詢的時機。
延遲加載的特點:真正用到該對象的時候才開始查詢改對象的屬性。
如果是立即加載,需要在Customer的set集合的注解中加入下邊的語句:fetch=FetchType.EAGER,如下圖:

其原理是利用了左外連接查詢的方式實現了立即加載。沒寫是EAGER,即默認是EAGER。LinkMan中也可是設置成立即加載。
mappedBy是映射來自,相當於inverse,即主表不在關心從表的信息,讓聯系人去維護。
//根據客戶查詢聯系人
@Test
public void test1(){
EntityManager em=JPAUtil.creatEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
//查詢id為7的客戶
Customer c=em.find(Customer.class, 7l);
System.out.println(c);
//查詢當前客戶下的聯系人
Set<Linkman> linkmans=c.getLinkmans();
System.out.println(linkmans);
tx.commit();
em.close();
}
//根據聯系人查詢客戶
@Test
public void test2(){
EntityManager em=JPAUtil.creatEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
//查詢id為7的客戶
Linkman lkm=em.find(Linkman.class, 4l);
System.out.println(lkm);
//查詢當前客戶下的聯系人
Customer c=lkm.getCustomer();
System.out.println(c);
tx.commit();
em.close();
}
五、 JPA中實體多對多映射配置及操作
5.1 多對多的實體類注解編寫
在角色實體對象中,如果配置了中間表的表名和在中間表中的列明,則在另外多的一方中只需要配置@ManyToMany(mappedBy="users"),如下圖:

哪一方寫mappedBy哪一方就不在關心創建中間表了,即讓另外一方不在關心創建中間表。
用戶實體類:
package com.Kevin.domain;
/**
* 用戶實體類
*/
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="sys_user")
public class SysUser implements Serializable {
@Id
@Column(name="user_id")
@GenericGenerator(name="uuid",strategy="uuid")
@GeneratedValue(generator="uuid")
private String userId;
@Column(name="user_name")
private String userName;
@Column(name="user_password")
private String userPassword;
@Column(name="user_state")
private Integer userState;
//多對多關系映射:
@ManyToMany(mappedBy="users",cascade=CascadeType.ALL)
private Set<SysRole> roles=new HashSet<SysRole>(0);
public Set<SysRole> getRoles() {
return roles;
}
public void setRoles(Set<SysRole> roles) {
this.roles = roles;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
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 Integer getUserState() {
return userState;
}
public void setUserState(Integer userState) {
this.userState = userState;
}
@Override
public String toString() {
return "SysUser [userId=" + userId + ", userName=" + userName + ", userPassword=" + userPassword
+ ", userState=" + userState + "]";
}
}
角色實體類:
package com.Kevin.domain;
/**
* 角色的實體類
*/
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="sys_role")
public class SysRole implements Serializable {
@Id
@Column(name="role_id")
@GenericGenerator(name="uuid",strategy="uuid") //聲明一個主鍵生成器,name:給生成器起名;strategy:指定的是hibernate中包含的生成策略
@GeneratedValue(generator="uuid")
private String roleId;
@Column(name="role_name")
private String roleName;
@Column(name="role_memo")
private String roleMemo;
//多對多關系映射:一個角色可以賦予多個用戶
@ManyToMany(cascade=CascadeType.ALL)
//加入一張表
@JoinTable(name="user_role_ref",joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")}, //寫的是當前實體在中間表的外鍵字段
inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")} //寫的是對方實體在中間表的外鍵字段
)
private Set<SysUser> users=new HashSet<SysUser>(0);
public Set<SysUser> getUsers() {
return users;
}
public void setUsers(Set<SysUser> users) {
this.users = users;
}
public String getRoleId() {
return roleId;
}
public void setRoleId(String 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;
}
@Override
public String toString() {
return "SysRole [roleId=" + roleId + ", roleName=" + roleName + ", roleMemo=" + roleMemo + "]";
}
}
5.2 多對多關聯關系的相關操作
5.2.1 一般保存
/**
* 保存操作
* 創建2個用戶,3個角色
* 讓1號用戶具備1、2號角色
* 讓2號用戶具備2、3號角色
* 保存用戶和角色
* 正常保存
*/
@Test
public void test1(){
SysUser u1=new SysUser();
SysUser u2=new SysUser();
u1.setUserName("JPA u1");
u2.setUserName("JPA u2");
SysRole r1=new SysRole();
SysRole r2=new SysRole();
SysRole r3=new SysRole();
r1.setRoleName("JPA r1");
r2.setRoleName("JPA r2");
r3.setRoleName("JPA r3");
//建立用戶和角色的關聯關系
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r2);
u2.getRoles().add(r3);
r1.getUsers().add(u1);
r2.getUsers().add(u2);
r2.getUsers().add(u1);
r3.getUsers().add(u2);
EntityManager em=JPAUtil.creatEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
em.persist(u1);
em.persist(u2);
em.persist(r1);
em.persist(r2);
em.persist(r3);
tx.commit();
em.close();
}
5.2.2 級聯保存
級聯操作是指當主控方執行保存、更新或者刪除操作時,其關聯對象(被控方)也執行相同的操作。在映射文件中通過對 cascade屬性的設置來控制是否對關聯對象采用級聯操作,級聯操作對各種關聯關系都是有效的。
在JPA的多對多關聯關系中中只需設置一方的級聯保存屬性即可,本文中以用戶為例,實現如下:

編寫測試代碼:
//級聯保存
@Test
public void test2(){
SysUser u1=new SysUser();
SysUser u2=new SysUser();
u1.setUserName("JPA u1");
u2.setUserName("JPA u2");
SysRole r1=new SysRole();
SysRole r2=new SysRole();
SysRole r3=new SysRole();
r1.setRoleName("JPA r1");
r2.setRoleName("JPA r2");
r3.setRoleName("JPA r3");
//建立用戶和角色的關聯關系
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r2);
u2.getRoles().add(r3);
r1.getUsers().add(u1);
r2.getUsers().add(u2);
r2.getUsers().add(u1);
r3.getUsers().add(u2);
EntityManager em=JPAUtil.creatEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
em.persist(u1);
tx.commit();
em.close();
}
5.2.3 刪除操作(禁用級聯刪除)
/**
* 刪除操作
* 雙向級聯刪除:不管是在JPA還是hibernate,多對多中都禁止使用
*/
@Test
public void test3(){
EntityManager em=JPAUtil.creatEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
SysUser user=em.find(SysUser.class, "402881ea632f00cf01632f00e6550000");
em.remove(user);
tx.commit();
em.close();
}
六、JPA的c3p0連接池操作
首先需要導入c3p0的JAR包,而且要在配置文件里面協商c3p0的供應商,如圖:

如果要從JPA中得到Session對象的話,需要對EntityManager進行unwrap()操作(解包裝),就可以得到Session對象,而且用doWork()方法可以得到Connection對象,可以對它進行操作。
/**
* 驗證c3p0是否連接成功
*/
@Test
public void test1(){
// 獲取JPA中的操作對象
EntityManager em=JPAUtil.createEntityManager();
// 獲取session
Session session=em.unwrap(Session.class);
//執行session的doWork方法
session.doWork(new Work() {
@Override
public void execute(Connection conn) throws SQLException {
// TODO Auto-generated method stub
System.out.println(conn.getClass().getName());
}
});
}
七、JPA中的單線程使用
手動編寫代碼:
package com.Kevin.utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* JPA的工具類
* @author Kevin
*
*/
public class JPAUtil {
//相當於SessionFactory
private static EntityManagerFactory factory;
private static ThreadLocal<EntityManager> tl;
static{
factory=Persistence.createEntityManagerFactory("myJPAUnit");
tl=new ThreadLocal<EntityManager>();
}
/**
* 獲取EntityManager對象
* @return
*/
public static EntityManager createEntityManager(){
//從當前線程上獲取EntityManager對象
EntityManager em=tl.get();
if(em==null){
em=factory.createEntityManager();
tl.set(em);
}
return tl.get();
}
public static void main(String[]args){
createEntityManager();
}
}
測試代碼:
@Test
public void test2(){
EntityManager em1=JPAUtil.createEntityManager();
EntityManager em2=JPAUtil.createEntityManager();
System.out.println(em1==em2);
}
八、在JPA中用Hibernate的方式進行CRUD操作
配置文件的寫法和實體類的創建和普通的JPA是一樣的,可以參考第三部分的入門案例,此處演示也僅作單表演示,奪標操作類似。
8.1 保存操作
/**
* 保存操作
*/
@Test
public void test1(){
Customer c=new Customer();
c.setCust_name("Kevin");
Session s=HibernateUtil.getCurrentSession();
Transaction tx=s.beginTransaction();
s.save(c);
tx.commit();
}
8.2 查詢操作
/**
* 查詢一個客戶
*/
@Test
public void test2(){
Session s=HibernateUtil.getCurrentSession();
Transaction tx=s.beginTransaction();
Customer c=s.get(Customer.class, 1l);
System.out.println(c);
tx.commit();
}
8.3 更新操作
/**
* 修改一個客戶
*/
@Test
public void test3(){
Session s=HibernateUtil.getCurrentSession();
Transaction tx=s.beginTransaction();
Customer c=s.get(Customer.class, 1l);
c.setCust_source("China");
s.update(c);
tx.commit();
}
@Test
public void test4(){
Session s=HibernateUtil.getCurrentSession();
Transaction tx=s.beginTransaction();
Customer c=s.get(Customer.class, 1l);
c.setCust_source("America");
tx.commit(); //快照機制自動更新
}
8.4 刪除操作
//刪除操作
@Test
public void test5(){
Session s=HibernateUtil.getCurrentSession();
Transaction tx=s.beginTransaction();
Customer c=s.get(Customer.class, 2l);
s.delete(c);
tx.commit(); //快照機制自動更新
}

