javascript
jpa 定义中间表实体_Spring Data JPA实体详解
1. Spring Data JPA實(shí)體概述
JPA提供了一種簡(jiǎn)單高效的方式來(lái)管理Java對(duì)象(POJO)到關(guān)系數(shù)據(jù)庫(kù)的映射,此類Java對(duì)象稱為JPA實(shí)體或簡(jiǎn)稱實(shí)體。實(shí)體通常與底層數(shù)據(jù)庫(kù)中的單個(gè)關(guān)系表相關(guān)聯(lián),每個(gè)實(shí)體的實(shí)例表示數(shù)據(jù)庫(kù)表格中的某一行。
2.?Spring Data JPA實(shí)體管理器
2.1 實(shí)體管理器概述
實(shí)體管理器(EntityManager)用于管理系統(tǒng)中的實(shí)體,它是實(shí)體與數(shù)據(jù)庫(kù)之間的橋梁,通過(guò)調(diào)用實(shí)體管理器的相關(guān)方法可以把實(shí)體持久化到數(shù)據(jù)庫(kù)中,同時(shí)也可以把數(shù)據(jù)庫(kù)中的記錄打包成實(shí)體對(duì)象。
2.2 實(shí)體管理器的常用方法
2.2.1 實(shí)體的四種狀態(tài)
在此之前我們要先了解實(shí)體的狀態(tài)及其轉(zhuǎn)換,見下圖
JPA實(shí)體生命周期有四種狀態(tài)
新建狀態(tài)(New):對(duì)象在保存進(jìn)數(shù)據(jù)庫(kù)之前為臨時(shí)狀態(tài)。此時(shí)數(shù)據(jù)庫(kù)中沒(méi)有該對(duì)象的信息,該對(duì)象的ID屬性也為空。如果沒(méi)有被持久化,程序退出時(shí)臨時(shí)狀態(tài)的對(duì)象信息將丟失。
托管狀態(tài)(Managed):對(duì)象在保存進(jìn)數(shù)據(jù)庫(kù)后或者從數(shù)據(jù)庫(kù)中加載后、并且沒(méi)有脫離Session時(shí)為持久化狀態(tài)。這時(shí)候數(shù)據(jù)庫(kù)中有對(duì)象的信息,改對(duì)象的id為數(shù)據(jù)庫(kù)中對(duì)應(yīng)記錄的主鍵值。由于還在Session中,持久化狀態(tài)的對(duì)象可以執(zhí)行任何有關(guān)數(shù)據(jù)庫(kù)的操作,例如獲取集合屬性的值等。
游離狀態(tài)(Datached):是對(duì)象曾經(jīng)處于持久化狀態(tài)、但是現(xiàn)在已經(jīng)離開Session了。雖然分離狀態(tài)的對(duì)象有id值,有對(duì)應(yīng)的數(shù)據(jù)庫(kù)記錄,但是已經(jīng)無(wú)法執(zhí)行有關(guān)數(shù)據(jù)庫(kù)的操作。例如,讀取延遲加載的集合屬性,可能會(huì)拋出延遲加載異常。
刪除狀態(tài)(Removed):刪除的對(duì)象,有id值,尚且和Persistence Context有關(guān)聯(lián),但是已經(jīng)準(zhǔn)備好從數(shù)據(jù)庫(kù)中刪除。
狀態(tài)名
作為java對(duì)象存在
在實(shí)體管理器中存在
在數(shù)據(jù)庫(kù)存在
New
Y
N
N
Managed
Y
Y
Y
Datached
N
N
N
Removed
Y
Y
N
用一段程序來(lái)示范
@Transactionalpublic voidsave(){//New 狀態(tài)
Task t = newTask();
t.setTaskName("task" + newDate().getTime());
t.setCreateTime(newDate());//Managed狀態(tài)
em.persist(t); //實(shí)體類t已經(jīng)有id t.getId();
t.setTaskName("kkk"); //更新任務(wù)名稱,這時(shí),如果提交事務(wù),則直接將kkk更新到數(shù)據(jù)庫(kù)//Detached狀態(tài) 事務(wù)提交或者調(diào)用em.clear都直接將實(shí)體任務(wù)狀態(tài)變?yōu)镈etached
em.clear();
t.setTaskName("kkk"); //更新數(shù)據(jù)不會(huì)更新到數(shù)據(jù)庫(kù)//Removed狀態(tài)
em.remove(t);
}
2.2.2??實(shí)體管理器的常用方法
對(duì)應(yīng)于實(shí)體的四種狀態(tài),實(shí)體管理器有四種常用的方法,分別是:persist / merge / clear / remove,結(jié)合狀態(tài)圖,可以判斷,對(duì)于不同狀態(tài)下的實(shí)體,各個(gè)方法操作結(jié)果會(huì)有不同:
對(duì)于不同狀態(tài)下的實(shí)體,persist 操作結(jié)果如下:
新建狀態(tài):實(shí)體狀態(tài)遷移到托管狀態(tài)
托管狀態(tài):實(shí)體狀態(tài)不發(fā)生改變,但會(huì)執(zhí)行數(shù)據(jù)庫(kù)的insert操作
游離狀態(tài):方法的調(diào)用將會(huì)拋出異常信息
刪除狀態(tài):實(shí)體將重返托管狀態(tài)
對(duì)于不同狀態(tài)下的實(shí)體,merge操作結(jié)果如下:
新建狀態(tài):系統(tǒng)會(huì)執(zhí)行數(shù)據(jù)庫(kù)insert操作,同時(shí)返回一個(gè)托管狀態(tài)的實(shí)體
托管狀態(tài):實(shí)體狀態(tài)不發(fā)生改變
游離狀態(tài):系統(tǒng)將實(shí)體的修改保存到數(shù)據(jù)庫(kù),同時(shí)返會(huì)一個(gè)托管狀態(tài)的實(shí)體
刪除狀態(tài):方法調(diào)用將拋出異常
對(duì)于不同狀態(tài)下的實(shí)體,refresh 操作結(jié)果如下:
新建狀態(tài):系統(tǒng)會(huì)執(zhí)行數(shù)據(jù)庫(kù)insert操作,同時(shí)返回一個(gè)托管狀態(tài)的實(shí)體
托管狀態(tài):實(shí)體狀態(tài)不發(fā)生改變,但會(huì)執(zhí)行數(shù)據(jù)庫(kù)的update操作
游離狀態(tài):實(shí)體狀態(tài)將返回托管狀態(tài)
刪除狀態(tài):方法調(diào)用將拋出異常
對(duì)于不同狀態(tài)下的實(shí)體,remove 操作結(jié)果如下:
新建狀態(tài):方法調(diào)用將拋出異常
托管狀態(tài):實(shí)體狀態(tài)變成刪除狀態(tài)
分離狀態(tài):方法調(diào)用將拋出異常
刪除狀態(tài):不發(fā)生任何操作
2.2.3?利用實(shí)體管理器管理實(shí)體(實(shí)現(xiàn)實(shí)體的CURD)
public classUserRepositoryImpl {
@PersistenceContextprivateEntityManager entityManager;
@Transactionalpublic voidadd(User user) {
entityManager.persist(user);
}
@TransactionalpublicUser update(User user) {
User userUpdate= entityManager.find(User.class, user.getId());
userUpdate.setAddress(user.getAddress());
userUpdate.setName(user.getName());
userUpdate.setPhone(user.getPhone());returnuserUpdate;
}
@TransactionalpublicUser addOrUpdate(User user) {returnentityManager.merge(user);
}
@Transactionalpublic voiddelete(User user) {
entityManager.remove(user);
}publicUser findOne(Integer id) {return entityManager.find(User.class, id);
}public ListfindAll() {
String queryString= "select u from User u";
Query query=entityManager.createQuery(queryString);returnquery.getResultList();
}
}
3. Spring Data JPA實(shí)體基礎(chǔ)映射
3.1 表映射
@Entity //表示該類為JPA實(shí)體類
@Table(name="t_user") //對(duì)應(yīng)數(shù)據(jù)庫(kù)中哪張表
public classUser {
@Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") //對(duì)應(yīng)數(shù)據(jù)庫(kù)表中哪個(gè)列字段及對(duì)該字段的自定義
private String phone;
3.2 主鍵映射
@Id //標(biāo)明主鍵
@GeneratedValue //主鍵生成策略
@Column(name="id_")private Integer id;
更多的主鍵生成策略,詳見3.6 的總體代碼
3.3 字段映射和約束條件
@Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") //對(duì)應(yīng)數(shù)據(jù)庫(kù)中哪個(gè)列及對(duì)該字段的自定義
private String phone;
3.4 單實(shí)體多表格存儲(chǔ)
通常一個(gè)實(shí)體對(duì)應(yīng)于一個(gè)表格,即表格中的所有的實(shí)體屬性都存放于一張表,如果將實(shí)體的屬性分配到多個(gè)表格存放,就涉及到單實(shí)體多表格存儲(chǔ)
@Entity
@Table(name="t_user",catalog="",schema="")
@SecondaryTables({//指明存放的第二張表
@SecondaryTable(name = "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id"))
})public classUser {
@Column(name="name_", length=60, nullable=false,unique=true,insertable=false)privateString name;//分表存儲(chǔ)
@Column(table = "t_address", name="street_", length = 100)private String street;
3.5 內(nèi)嵌實(shí)體
在定義實(shí)體時(shí)可能需要將某幾個(gè)的屬性剝離出放到另外一個(gè)實(shí)體中,以使程序更有層次感,并且當(dāng)其他實(shí)體也需要這幾個(gè)屬性時(shí),我們也不需要再定義這幾個(gè)屬性,把存放這幾個(gè)屬性的實(shí)體重新引用即可,操作方法如下:
@Embeddable //標(biāo)識(shí)該實(shí)體可嵌入到其他實(shí)體中
public classComment {
@Column(name="title_",length=100)
String title;
@Column(name="content_")
String content;
/*//被剝離出的屬性
@Column(name="title_",length=100)
String title;
@Column(name="content_")
String content;*/@Embedded//引入該實(shí)體
@AttributeOverrides({ //羅列出所有需要重新命名的屬性
@AttributeOverride(name = "title", column = @Column(name = "user_title")),
@AttributeOverride(name= "content", column = @Column(name = "user_content"))
})privateComment comment;
內(nèi)嵌實(shí)體在數(shù)據(jù)庫(kù)中不會(huì)一點(diǎn)單獨(dú)的表格存放,而是跟數(shù)組實(shí)體存放于同一表格中。
3.6 實(shí)體類代碼
importjava.math.BigDecimal;importjava.util.Date;import javax.persistence.*;importorg.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="t_user",catalog="",schema="")
@SecondaryTables({
@SecondaryTable(name= "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id"))
})public classUser {
@Id//標(biāo)明主鍵
@GeneratedValue //主鍵生成策略
@Column(name="id_")privateInteger id;/*@Id
@GeneratedValue(generator="uuidGenerator")
@GenericGenerator(name="uuidGenerator",strategy="uuid")
@Column(name="id_",length=32)
private String id;*/
/*@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id_")
private Integer id;*/
/**
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id_")
private Integer id;*/
/*@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idGenerator")
@SequenceGenerator(name = "idGenerator",sequenceName="mySeq",allocationSize=1)
@Column(name="id_")
private Integer id;*/
/*@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "userGenerator")
@TableGenerator(name = "userGenerator",table="pk_generator",
pkColumnName="gen_name",
valueColumnName="gen_value",
pkColumnValue="user_pk",
initialValue=0,
allocationSize=1)
@Column(name="id_")
private Integer id;*/@Column(name="name_", length=60, nullable=false,unique=true,insertable=false)privateString name;
@Column(name="address_", length=60, nullable=false)privateString address;
@Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'")privateString phone;
@Column(name="inCome_", precision=12, scale=2)privateBigDecimal inCome;
@Temporal(TemporalType.DATE)privateDate birthday;//Date 日期型,精確到年月日,例如“2008-08-08”//Time 時(shí)間型,精確到時(shí)分秒,例如“20:00:00”//Timestamp 時(shí)間戳,精確到納秒,例如“2008-08-08 20:00:00.000000001”
@Lob
@Column(name="pic_")
@Basic(fetch=FetchType.LAZY)private byte[] pic;
@Lob
@Column(name="note_")
@Basic(fetch=FetchType.LAZY)privateString note;//分表存儲(chǔ)
@Column(table = "t_address", name="street_", length = 100)privateString street;//分表存儲(chǔ)
@Column(table = "t_address", name="city_")privateString city;//分表存儲(chǔ)
@Column(table = "t_address", name="conutry_",length = 20)privateString conutry;
@Column(name="title_",length=100)
String title;
@Column(name="content_")
String content;/*@Embedded //引入該實(shí)體
@AttributeOverrides({ //羅列出所有需要重新命名的屬性
@AttributeOverride(name = "title", column = @Column(name = "user_title")),
@AttributeOverride(name = "content", column = @Column(name = "user_content"))
})
private Comment comment;*/
//省略get/set方法
}
4. Spring Data JPA實(shí)體高級(jí)映射
4.1 一對(duì)一實(shí)體映射的概念和實(shí)現(xiàn)方法
如下例,人員表(person)和地址表(adddress),person表是關(guān)系的擁有者,表中的address_id字段關(guān)聯(lián)著address表的主鍵id。
@Entitypublic classPerson {//略
@OneToOne
@JoinColumn(name="address_id",referencedColumnName="aid")//name:主表的外鍵字段; referencedColumnName:從表的主鍵//如果關(guān)聯(lián)的字段有多個(gè),采用如下注解//@JoinColumns(value={@JoinColumn(name="address_id",referencedColumnName="aid"),@JoinColumn(name="address_id2",referencedColumnName="aid2")})
private Address address;
4.2 一對(duì)多實(shí)體映射的概念和實(shí)現(xiàn)方法
部門表(depart)和員工表(employee),一個(gè)部門可以有多個(gè)員工,一對(duì)多關(guān)系可以采用如下兩種實(shí)現(xiàn)方法。
4.2.1 中間表方式
創(chuàng)建中間表(depart_employee),表中存放兩個(gè)表的主鍵。通過(guò)部門id可查詢關(guān)聯(lián)員工的id,三張表存在兩個(gè)主外鍵關(guān)系。
@Entitypublic classDepart {//略
@OneToMany
@JoinTable(name= "depart_employee", //name:關(guān)聯(lián)表
joinColumns = @JoinColumn(name = "depart_id",referencedColumnName="did"), //joinColumns:關(guān)系的擁有者與關(guān)聯(lián)表的關(guān)系
inverseJoinColumns = @JoinColumn(name = "employee_id",referencedColumnName="eid"))//inverseJoinColumns:關(guān)系的被擁有者與關(guān)聯(lián)表的關(guān)系
private List employees;
4.2.2 從表增加外鍵方式
在員工表(employee2)中添加一個(gè)depart_id字段,它作為外鍵關(guān)聯(lián)部門表(depart2)的主鍵id。
@Entitypublic classDepart2 {//略
@OneToMany
@JoinColumn(name="depart_id",referencedColumnName="id")private List employee2s;
4.3 多對(duì)多實(shí)體映射的概念的實(shí)現(xiàn)方法
多對(duì)多的實(shí)現(xiàn)也是通過(guò)中間表,方法同一對(duì)多的中間表實(shí)現(xiàn)方式。
@Entitypublic classTeacher {//略
@ManyToMany
@JoinTable(name= "teacher_student",
joinColumns= @JoinColumn(name = "teacher_id",referencedColumnName="tid"),
inverseJoinColumns= @JoinColumn(name = "student_id",referencedColumnName="sid"))private Liststudents;
@Entitypublic classStudent {//略
@ManyToMany(mappedBy= "students")private List teachers;
4.4 級(jí)聯(lián)策略和懶加載
以@OneToOne為例,當(dāng)我希望刪除人員信息時(shí),也將其地址信息刪除,則可使用級(jí)聯(lián)策略;當(dāng)我想要查詢?nèi)藛T信息(主實(shí)體)時(shí),并不想同時(shí)查詢出其地址信息(子實(shí)體),可以設(shè)置懶加載。
@Entitypublic classPerson {
@OneToOne(cascade={CascadeType.REFRESH,CascadeType.REMOVE},fetch=FetchType.LAZY)//@JoinColumn(name="address_id",referencedColumnName="aid")
private Address address;
5. Spring Data JPA實(shí)體繼承
5.1 實(shí)體繼承的概念
繼承[extends]想必已不陌生,對(duì)于JPA來(lái)說(shuō),我們不但要考慮如何實(shí)現(xiàn)Java端的繼承關(guān)系,還要考慮如何持久化到數(shù)據(jù)庫(kù)中。JPA為此提供了三種策略,如下:
5.2 實(shí)體繼承策略
繼承關(guān)系如圖,繼承策略的注解主要應(yīng)用于父類Item。
5.2.1 繼承策略之單一表策略
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)public class Item {
執(zhí)行單一表策略會(huì)將所有實(shí)體的信息存放于一張表中,它的優(yōu)點(diǎn)是信息存放于一張表,查詢效率較高,缺點(diǎn)是大量字段為空,浪費(fèi)存儲(chǔ)空間。
如果類名過(guò)長(zhǎng)或需要更改鑒別字段的名稱,可對(duì)鑒別字段及可選值自定義:
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="ITYPE",discriminatorType=discriminatorType.CHAR) //聲明鑒別字段的字段名,類型
@DiscriminatorValue("I") //該表在鑒別字段列顯示的值
public class Item {
@Entity
@DiscriminatorValue("P")public class Phone extends Item {
@Entity
@DiscriminatorValue("B")public class Book extends Item {
效果如下
5.2.2 繼承策略之連接表策略
@Entity
@Inheritance(strategy=InheritanceType.JOINED)public class Item {
連接表策略會(huì)生成三張表,通過(guò)共享主鍵彼此關(guān)聯(lián)。
這種策略避免了空字段的浪費(fèi),但由于采用表關(guān)聯(lián)查詢,當(dāng)數(shù)據(jù)量過(guò)大時(shí),查詢效率較低。
5.2.3 繼承策略之每個(gè)類策略
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)public classItem {/*@Id
@GeneratedValue(strategy = GenerationType.AUTO)*/@Id
@GeneratedValue(strategy= GenerationType.TABLE, generator = "ItemGenerator")
@TableGenerator(name= "ItemGenerator",table="pk_generator",
pkColumnName="gen_name",
valueColumnName="gen_value",
pkColumnValue="item_pk",
initialValue=0,
allocationSize=1)private Long id;
每個(gè)類策略實(shí)際上是每個(gè)類一個(gè)表策略,這種策略要求主鍵不能使用自增的方式,如上面的代碼,采用表中獲取的方式。
三張表各自存放自己的完整信息,表之間沒(méi)有任何的關(guān)聯(lián)關(guān)系。雖然他們各自存放各自的數(shù)據(jù),但主鍵是連續(xù)的。即三個(gè)表共用一套主鍵生成策略(三個(gè)表的主鍵都從另一個(gè)表中獲取)。
這種策略查詢效率高,同時(shí)也不存在大量空字段的浪費(fèi)。
總結(jié)
以上是生活随笔為你收集整理的jpa 定义中间表实体_Spring Data JPA实体详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《蜘蛛侠:英雄无归》北美票房大爆:首日票
- 下一篇: excel 如何替换带上标的文字_如何在