Spring?Boot?整合JPA?數(shù)據(jù)模型關(guān)聯(lián)使用操作(一對(duì)一、一對(duì)多、多對(duì)多)
表關(guān)聯(lián)
上一篇介紹了JPA的簡(jiǎn)單使用,這一篇介紹JPA在表關(guān)聯(lián)上的使用
一對(duì)一
配置參數(shù)
JPA對(duì)于數(shù)據(jù)實(shí)體一對(duì)一映射使用的是@OneToOne注解。
代碼
User用戶表
/**
* 用戶信息
* @author daify
**/
@Data
@Entity
@Table(name = "cascade_user")
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
private Double point;
/**
* CascadeType包含的類別 級(jí)聯(lián):給當(dāng)前設(shè)置的實(shí)體操作另一個(gè)實(shí)體的權(quán)限
* CascadeType.ALL 級(jí)聯(lián)所有操作
* CascadeType.PERSIST 級(jí)聯(lián)持久化(保存)操作
* CascadeType.MERGE 級(jí)聯(lián)更新(合并)操作
* CascadeType.REMOVE 級(jí)聯(lián)刪除操作
* CascadeType.REFRESH 級(jí)聯(lián)刷新操作
* CascadeType.DETACH 級(jí)聯(lián)分離操作,如果你要?jiǎng)h除一個(gè)實(shí)體,但是它有外鍵無(wú)法刪除,這個(gè)級(jí)聯(lián)權(quán)限會(huì)撤銷所有相關(guān)的外鍵關(guān)聯(lián)。
*/
@OneToOne(targetEntity = UserCart.class,
cascade = CascadeType.ALL,
mappedBy = "user")
private UserCart userCart;
@OneToOne(targetEntity = UserInfo.class,
cascade = CascadeType.ALL)
@JoinTable(name = "user_info_table",
joinColumns = @JoinColumn(name="user_id"),
inverseJoinColumns = @JoinColumn(name = "info_id"))
private UserInfo userInfo;
}
UserCart用戶購(gòu)物車表
@Data
@Entity
@Table(name = "cascade_user_cart")
public class UserCart {
@Id
@GeneratedValue
private Long id;
private Integer count;
private Double totalMoney;
private Date updateTime;
/**
* CascadeType包含的類別 級(jí)聯(lián):給當(dāng)前設(shè)置的實(shí)體操作另一個(gè)實(shí)體的權(quán)限
* CascadeType.ALL 級(jí)聯(lián)所有操作
* CascadeType.PERSIST 級(jí)聯(lián)持久化(保存)操作
* CascadeType.MERGE 級(jí)聯(lián)更新(合并)操作
* CascadeType.REMOVE 級(jí)聯(lián)刪除操作
* CascadeType.REFRESH 級(jí)聯(lián)刷新操作
* CascadeType.DETACH 級(jí)聯(lián)分離操作,如果你要?jiǎng)h除一個(gè)實(shí)體,但是它有外鍵無(wú)法刪除,這個(gè)級(jí)聯(lián)權(quán)限會(huì)撤銷所有相關(guān)的外鍵關(guān)聯(lián)。
*/
@OneToOne(targetEntity = User.class,
cascade = {},
fetch = FetchType.LAZY)
private User user;
}
用戶信息
@Data
@Entity
@Table(name = "cascade_user_info")
public class UserInfo {
@Id
@GeneratedValue
private Long id;
private String userInfo;
private String config;
}上面例子中嘗試使用了兩種方式來(lái)維護(hù)一對(duì)一的關(guān)系,首先在User實(shí)體中同樣標(biāo)注了@OneToOne但是配置了mappedBy,
這樣生成的表數(shù)據(jù)中,User和UserCart的關(guān)系將由UserCart負(fù)責(zé)維護(hù)。User表中并不會(huì)維護(hù)UserCart的信息。
而在User和UserInfo的關(guān)系中使用了中間表user_info_table來(lái)維護(hù)雙方的關(guān)系,UserInfo實(shí)體中并沒(méi)有保存任何User的信息。
權(quán)限
在User、UserCart、UserInfo三者中User為數(shù)據(jù)存在的主體,其他兩個(gè)實(shí)體都是依托于User數(shù)據(jù)的存在而存在。
所以在權(quán)限中給User實(shí)體提供了全部全部權(quán)限。
注解
@OneToOne主要提供了下面的參數(shù)內(nèi)容
| 注解 | 參數(shù) | 描述 |
|---|---|---|
| @OneToOne | 描述一個(gè) 一對(duì)一的關(guān)聯(lián) | |
| targetEntity | 目標(biāo)類的實(shí)體 | |
| cascade | 關(guān)聯(lián)到目標(biāo)的操作 | |
| fetch | 是否使用延遲加載 | |
| mappedBy | 反向關(guān)聯(lián) |
測(cè)試
因?yàn)樯厦嬉粚?duì)一的例子中權(quán)限被賦予給User表中,UserCart并沒(méi)有賦予任何權(quán)限,所以保存用戶的時(shí)候可以關(guān)聯(lián)保存用戶購(gòu)物車,刪除購(gòu)物車的時(shí)候并不會(huì)刪除用戶,但是刪除用戶的時(shí)候會(huì)刪除購(gòu)物車
通過(guò)保存用戶關(guān)聯(lián)保存購(gòu)物車
@Test
public void testUserSave() {
User defUser1 = UserMock.getDefUser1();
UserCart defUserCart1 = UserCartMock.getDefUserCart1();
defUser1.setUserCart(defUserCart1);
defUserCart1.setUser(defUser1);
// 此時(shí)保存用戶,用戶購(gòu)物車應(yīng)該也被保存
userRepository.save(defUser1);
List <UserCart> all = userCartRepository.findAll();
Assert.assertNotNull(all);
Assert.assertTrue(all.size() == 1);
}
刪除用戶購(gòu)物車的時(shí)候,用戶不會(huì)被刪除
@Test
public void testUserCartDelete() {
User defUser1 = UserMock.getDefUser1();
UserCart defUserCart1 = UserCartMock.getDefUserCart1();
defUser1.setUserCart(defUserCart1);
// 此時(shí)保存用戶,用戶購(gòu)物車應(yīng)該也被保存
userRepository.save(defUser1);
// 此時(shí)刪除用戶購(gòu)物車并不會(huì)刪除用戶
userCartRepository.deleteAll();
List <User> all1 = userRepository.findAll();
Assert.assertNotNull(all1);
Assert.assertTrue(all1.size() == 1);
}刪除用戶的時(shí)候,購(gòu)物車會(huì)被刪除
@Test
public void testUserDelete() {
User defUser1 = UserMock.getDefUser1();
UserCart defUserCart1 = UserCartMock.getDefUserCart1();
defUser1.setUserCart(defUserCart1);
// 此時(shí)保存用戶,用戶購(gòu)物車應(yīng)該也被保存
userRepository.save(defUser1);
// 此時(shí)刪除用戶購(gòu)物車并不會(huì)刪除用戶
userRepository.delete(defUser1);
List <UserCart> all = userCartRepository.findAll();
Assert.assertTrue(all.size() == 0);
}
一對(duì)多和多對(duì)一
通過(guò)@OneToMany和@ManyToOne的組合我們可以實(shí)現(xiàn)雙向關(guān)聯(lián)。根據(jù)JPA規(guī)范,我們使用多方來(lái)維護(hù)關(guān)系。
通過(guò)在多方維護(hù)@JoinColumn來(lái)注釋屬性。
代碼
訂單表
@Data
@Entity
@Table(name = "cascade_order")
public class Order {
@Id
@GeneratedValue
private Long id;
private String orderNo;
private Integer count;
private Double money;
@OneToMany(mappedBy = "order",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER)
private List<OrderItem> orderItems;
/**
* 重寫(xiě)toString防止死循環(huán)
* @return
*/
@Override
public String toString() {
return "OrderItem{" + "id=" + id + ", productId='" + productId + '\'' + ", money=" + money
+ ", count=" + count + ", productName='" + productName + '\'' + ", order=" + order
+ '}';
}
}
訂單子項(xiàng)
@Data
@Entity
@Table(name = "cascade_order_item")
public class OrderItem {
@Id
@GeneratedValue
private Long id;
private String productId;
private Double money;
private Integer count;
private String productName;
@ManyToOne(targetEntity = Order.class,
cascade = {},
fetch = FetchType.EAGER)
@JoinColumn(
// 設(shè)置在OrderItem表中的關(guān)聯(lián)字段(外鍵)
name="order_id"
)
// 防止json序列化死循環(huán)問(wèn)題解決
@JsonBackReference
private Order order;
}
上門的例子中,訂單方為一端,訂單子項(xiàng)為多端,在多端除了使用了@ManyToOne
還使用了@JoinColumn注解來(lái)標(biāo)識(shí)Order主鍵創(chuàng)建到OrderItem表的列的名稱,
當(dāng)然沒(méi)有此注解的時(shí)候JPA會(huì)根據(jù)默認(rèn)規(guī)則生成一個(gè)列名稱。
權(quán)限
根據(jù)JPA規(guī)范一對(duì)多時(shí)候,關(guān)系的維護(hù)交給了多方來(lái)進(jìn)行,但是很多時(shí)候多方的存在是依靠一方的。
比如(訂單、訂單子項(xiàng))所以更新刪除的權(quán)限需要授權(quán)給一方(Order)。
@ManyToOne,@OneToMany
| 注解 | 參數(shù) | 描述 |
|---|---|---|
| @ManyToOne | 一個(gè)多對(duì)一的映射,該注解標(biāo)注的屬性通常是數(shù)據(jù)庫(kù)表的外鍵. | |
| targetEntity | 目標(biāo)類的實(shí)體 | |
| cascade | 關(guān)聯(lián)到目標(biāo)的操作 | |
| fetch | 是否使用延遲加載 | |
| @OneToMany | 一個(gè) 一對(duì)多的關(guān)聯(lián),該屬性應(yīng)該為集體類型,在數(shù)據(jù)庫(kù)中并沒(méi)有實(shí)際字段 | |
| targetEntity | 目標(biāo)類的實(shí)體 | |
| cascade | 關(guān)聯(lián)到目標(biāo)的操作 | |
| fetch | 是否使用延遲加載 | |
| mappedBy | 反向關(guān)聯(lián) | |
| orphanRemoval | 講移除操作級(jí)聯(lián)到關(guān)聯(lián)的實(shí)體中 |
測(cè)試
一對(duì)多的時(shí)候雖然多方維持了兩者關(guān)系,但是我們把權(quán)限賦予了一方所以,刪除多方并不會(huì)級(jí)聯(lián)操作,刪除一方可以移除多方數(shù)據(jù)。所以下的測(cè)試可以通過(guò)
@Test
public void testOrder() {
Order defOrder1 = OrderMock.getDefOrder1();
List <OrderItem> allDefOrder = OrderItemMock.getAllDefOrder();
// order 維持orderItem關(guān)系
defOrder1.setOrderItems(allDefOrder);
// orderItem維持order關(guān)系
allDefOrder.forEach(o -> o.setOrder(defOrder1));
Order save = orderRepository.save(defOrder1);
List <OrderItem> all = orderItemRepository.findAll();
Assert.assertTrue(all.size() == allDefOrder.size());
orderItemRepository.delete(all.get(0));
Order order = orderRepository.findById(save.getId()).get();
Assert.assertNotNull(order);
orderRepository.deleteById(order.getId());
List <OrderItem> all1 = orderItemRepository.findAll();
Assert.assertTrue(all1.size() == 0);
}多對(duì)多
配置方法
和一對(duì)多、一對(duì)一不同,多對(duì)多沒(méi)法只使用兩個(gè)數(shù)據(jù)實(shí)體完成相互之間的關(guān)系維護(hù),這個(gè)時(shí)候需要一個(gè)關(guān)聯(lián)的中間表來(lái)維護(hù)他們之間的關(guān)系。
對(duì)于中間表的配置,你大可不去進(jìn)行額外的配置讓JPA自動(dòng)生成,當(dāng)然你也可以使用之前介紹的@JoinTable注解進(jìn)行配置。
權(quán)限
和一對(duì)多、一對(duì)一不同,多對(duì)多一般是沒(méi)有辦法設(shè)置級(jí)聯(lián)操作的。因?yàn)殡p方對(duì)應(yīng)的都是集合對(duì)象,
而雙方某一條數(shù)據(jù)都可能被對(duì)方多條數(shù)據(jù)關(guān)聯(lián)。
代碼
用戶表
@Data
@Entity
@Table(name = "cascade_user")
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
private Double point;
@ManyToMany(targetEntity = UserTag.class,
cascade = {},
fetch = FetchType.LAZY)
// 假如不定義,jpa會(huì)生成“表名1”+“_”+“表名2”的中間表
@JoinTable(
// 定義中間表的名稱
name = "cascade_user_tag_table",
// 定義中間表中關(guān)聯(lián)User表的外鍵名
joinColumns = {
@JoinColumn(name = "user_id",
referencedColumnName = "id")
},
// 定義中間表中關(guān)聯(lián)UserTag表的外鍵名
inverseJoinColumns = {
@JoinColumn(name = "tags_id",
referencedColumnName = "id")
}
)
private Set<UserTag> tags;
}
用戶標(biāo)簽表
@Data
@Entity
@Table(name = "cascade_user_tag")
public class UserTag {
@Id
@GeneratedValue
private Long id;
private String tagName;
@ManyToMany(mappedBy = "tags",
cascade = {},
fetch = FetchType.LAZY)
private List<User> userList;
}
@ManyToMany
下面是@OneToOne以及與其配合的@JoinTable提供的注解參數(shù)
| 注解 | 參數(shù) | 描述 |
|---|---|---|
| @ManyToMany | 描述一個(gè)多對(duì)多的關(guān)聯(lián).多對(duì)多關(guān)聯(lián)上是兩個(gè)一對(duì)多關(guān)聯(lián),但是在ManyToMany描述中,中間表是由ORM框架自動(dòng)處理 | |
| targetEntity | 目標(biāo)類的實(shí)體 | |
| cascade | 關(guān)聯(lián)到目標(biāo)的操作 | |
| fetch | 是否使用延遲加載 | |
| mappedBy | 反向關(guān)聯(lián) | |
| @JoinTable | JoinTable在many-to-many關(guān)系的所有者一邊定義。如果沒(méi)有定義JoinTable,使用JoinTable的默認(rèn)值。 | |
| catalog | 表的目錄 | |
| foreignKey | 外鍵約束,創(chuàng)建表的時(shí)候使用 | |
| indexes | 表的索引,在生成表的時(shí)候使用 | |
| @JoinColumns | 關(guān)系存在多個(gè)JoinColumn,用JoinColumns定義多個(gè)JoinColumn的屬性。 | |
| foreignKey | 此列的外鍵約束 | |
| value | JoinColumn的集合 | |
| inverseJoinColumns | 聯(lián)接表的外鍵列,該列引用不擁有關(guān)聯(lián)的實(shí)體的主表 | |
| joinColumns | 聯(lián)接表的外鍵列,該列引用擁有關(guān)聯(lián)的實(shí)體的主表 | |
| name | 進(jìn)行連接的表名稱 | |
| schema | 表的命名空間 |
測(cè)試
多對(duì)多的情況下,我們使用雖然User和UserTag的關(guān)系由中間表維護(hù),但是我們?cè)赨ser中配置了中間表的關(guān)系維護(hù),所以此時(shí)刪除用戶的時(shí)候可以成功刪除,且可以成功移除中間表數(shù)據(jù),但是這個(gè)時(shí)候移除UserTag數(shù)據(jù)的時(shí)候,卻會(huì)拋出DataIntegrityViolationException異常。只能通過(guò)User移除中間關(guān)系。
@Test
public void testUserTag() {
User defUser1 = UserMock.getDefUser1();
UserTag defTag1 = UserTagMock.getDefTag1();
UserTag defTag2 = UserTagMock.getDefTag2();
defUser1 = userRepository.save(defUser1);
defTag1 = userTagRepository.save(defTag1);
defTag2 = userTagRepository.save(defTag2);
defUser1.getTags().add(defTag1);
defUser1.getTags().add(defTag2);
defUser1 = userRepository.save(defUser1);
// 此時(shí)會(huì)報(bào)錯(cuò),因?yàn)橹虚g表關(guān)系為User維護(hù)
try {
userTagRepository.delete(defTag2);
} catch (Exception e) {
log.info(e.getCause().getMessage());
Assert.assertTrue(e instanceof DataIntegrityViolationException);
}
// 修改User表中關(guān)系
defUser1.setTags(new HashSet <>());
defUser1.getTags().add(defTag1);
defUser1 = userRepository.save(defUser1);
// 此時(shí)可以刪除
userTagRepository.delete(defTag2);
// 直接刪除用戶沒(méi)問(wèn)題
userRepository.delete(defUser1);
}
本篇文章涉及的源碼下載地址:https://gitee.com/daifyutils/springboot-samples
到此這篇關(guān)于Spring Boot 整合JPA 數(shù)據(jù)模型關(guān)聯(lián)操作(一對(duì)一、一對(duì)多、多對(duì)多)的文章就介紹到這了,更多相關(guān)Spring Boot 整合JPA 數(shù)據(jù)模型關(guān)聯(lián)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot + jpa實(shí)現(xiàn)刪除數(shù)據(jù)的操作代碼
- SpringDataJPA詳解增刪改查操作方法
- Spring?Data?JPA映射自定義實(shí)體類操作
- SpringDataJpa多表操作的實(shí)現(xiàn)
- Springboot使用Spring Data JPA實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作
- SpringBoot集成JPA持久層框架,簡(jiǎn)化數(shù)據(jù)庫(kù)操作
- springboot-jpa的實(shí)現(xiàn)操作
- springboot 之jpa高級(jí)查詢操作
- Springboot JPA級(jí)聯(lián)操作的實(shí)現(xiàn)(一對(duì)一、一對(duì)多、多對(duì)多)
相關(guān)文章
SpringBoot中的6種API請(qǐng)求參數(shù)讀取方式總結(jié)
使用Spring Boot開(kāi)發(fā)API的時(shí)候,讀取請(qǐng)求參數(shù)是服務(wù)端編碼中最基本的一項(xiàng)操作,Spring Boot中也提供了多種機(jī)制來(lái)滿足不同的API設(shè)計(jì)要求,通過(guò)本文,為大家總結(jié)6種常用的請(qǐng)求參數(shù)讀取方式,需要的朋友可以參考下2024-07-07
使用Eclipse開(kāi)發(fā)工具如何解決Java Compiler中Annotation Processin不出現(xiàn)的問(wèn)題
這篇文章主要介紹了使用Eclipse開(kāi)發(fā)工具如何解決Java Compiler中Annotation Processin不出現(xiàn)的相關(guān)資料,需要的朋友可以參考下2015-11-11
Java虛擬機(jī)JVM類加載機(jī)制(從類文件到虛擬機(jī))
所謂的類加載機(jī)制就是虛擬機(jī)將class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行驗(yàn)證,轉(zhuǎn)換解析和初始化,形成虛擬機(jī)可以直接使用的java類型,本文給大家介紹類加載機(jī)制過(guò)程從類文件到虛擬機(jī)的詳細(xì)說(shuō)明,感興趣的朋友跟隨小編一起看看吧2021-06-06
Java Date類常用示例_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
在JDK1.0中,Date類是唯一的一個(gè)代表時(shí)間的類,但是由于Date類不便于實(shí)現(xiàn)國(guó)際化,所以從JDK1.1版本開(kāi)始,推薦使用Calendar類進(jìn)行時(shí)間和日期處理。這里簡(jiǎn)單介紹一下Date類的使用,需要的朋友可以參考下2017-05-05
解決使用gateway后靜態(tài)資源失效的問(wèn)題
這篇文章主要介紹了解決使用gateway后靜態(tài)資源失效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
關(guān)于@GetMapping和@GetMapping(value=““)的區(qū)別
這篇文章主要介紹了關(guān)于@GetMapping和@GetMapping(value=““)的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
java并發(fā)編程專題(十)----(JUC原子類)基本類型詳解
這篇文章主要介紹了java JUC原子類基本類型詳解的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07

