<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
上一篇介紹了JPA的簡單使用,這一篇介紹JPA在表關聯上的使用
設定引數
JPA對於資料實體一對一對映使用的是@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包含的類別 級聯:給當前設定的實體操作另一個實體的許可權 * CascadeType.ALL 級聯所有操作 * CascadeType.PERSIST 級聯持久化(儲存)操作 * CascadeType.MERGE 級聯更新(合併)操作 * CascadeType.REMOVE 級聯刪除操作 * CascadeType.REFRESH 級聯重新整理操作 * CascadeType.DETACH 級聯分離操作,如果你要刪除一個實體,但是它有外來鍵無法刪除,這個級聯許可權會復原所有相關的外來鍵關聯。 */ @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使用者購物車表
@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包含的類別 級聯:給當前設定的實體操作另一個實體的許可權 * CascadeType.ALL 級聯所有操作 * CascadeType.PERSIST 級聯持久化(儲存)操作 * CascadeType.MERGE 級聯更新(合併)操作 * CascadeType.REMOVE 級聯刪除操作 * CascadeType.REFRESH 級聯重新整理操作 * CascadeType.DETACH 級聯分離操作,如果你要刪除一個實體,但是它有外來鍵無法刪除,這個級聯許可權會復原所有相關的外來鍵關聯。 */ @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; }
上面例子中嘗試使用了兩種方式來維護一對一的關係,首先在User實體中同樣標註了@OneToOne
但是設定了mappedBy,
這樣生成的表資料中,User和UserCart的關係將由UserCart負責維護。User表中並不會維護UserCart的資訊。
而在User和UserInfo的關係中使用了中間表user_info_table
來維護雙方的關係,UserInfo實體中並沒有儲存任何User的資訊。
許可權
在User、UserCart、UserInfo三者中User為資料存在的主體,其他兩個實體都是依託於User資料的存在而存在。
所以在許可權中給User實體提供了全部全部許可權。
註解
@OneToOne主要提供了下面的引數內容
註解 | 引數 | 描述 |
---|---|---|
@OneToOne | 描述一個 一對一的關聯 | |
targetEntity | 目標類的實體 | |
cascade | 關聯到目標的操作 | |
fetch | 是否使用延遲載入 | |
mappedBy | 反向關聯 |
測試
因為上面一對一的例子中許可權被賦予給User表中,UserCart並沒有賦予任何許可權,所以儲存使用者的時候可以關聯儲存使用者購物車,刪除購物車的時候並不會刪除使用者,但是刪除使用者的時候會刪除購物車
通過儲存使用者關聯儲存購物車
@Test public void testUserSave() { User defUser1 = UserMock.getDefUser1(); UserCart defUserCart1 = UserCartMock.getDefUserCart1(); defUser1.setUserCart(defUserCart1); defUserCart1.setUser(defUser1); // 此時儲存使用者,使用者購物車應該也被儲存 userRepository.save(defUser1); List <UserCart> all = userCartRepository.findAll(); Assert.assertNotNull(all); Assert.assertTrue(all.size() == 1); }
刪除使用者購物車的時候,使用者不會被刪除
@Test public void testUserCartDelete() { User defUser1 = UserMock.getDefUser1(); UserCart defUserCart1 = UserCartMock.getDefUserCart1(); defUser1.setUserCart(defUserCart1); // 此時儲存使用者,使用者購物車應該也被儲存 userRepository.save(defUser1); // 此時刪除使用者購物車並不會刪除使用者 userCartRepository.deleteAll(); List <User> all1 = userRepository.findAll(); Assert.assertNotNull(all1); Assert.assertTrue(all1.size() == 1); }
刪除使用者的時候,購物車會被刪除
@Test public void testUserDelete() { User defUser1 = UserMock.getDefUser1(); UserCart defUserCart1 = UserCartMock.getDefUserCart1(); defUser1.setUserCart(defUserCart1); // 此時儲存使用者,使用者購物車應該也被儲存 userRepository.save(defUser1); // 此時刪除使用者購物車並不會刪除使用者 userRepository.delete(defUser1); List <UserCart> all = userCartRepository.findAll(); Assert.assertTrue(all.size() == 0); }
通過@OneToMany和@ManyToOne的組合我們可以實現雙向關聯。根據JPA規範,我們使用多方來維護關係。
通過在多方維護@JoinColumn
來註釋屬性。
程式碼
訂單表
@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; /** * 重寫toString防止死迴圈 * @return */ @Override public String toString() { return "OrderItem{" + "id=" + id + ", productId='" + productId + ''' + ", money=" + money + ", count=" + count + ", productName='" + productName + ''' + ", order=" + order + '}'; } }
訂單子項
@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( // 設定在OrderItem表中的關聯欄位(外來鍵) name="order_id" ) // 防止json序列化死迴圈問題解決 @JsonBackReference private Order order; }
上門的例子中,訂單方為一端,訂單子項為多端,在多端除了使用了@ManyToOne
還使用了@JoinColumn
註解來標識Order主鍵建立到OrderItem表的列的名稱,
當然沒有此註解的時候JPA會根據預設規則生成一個列名稱。
許可權
根據JPA規範一對多時候,關係的維護交給了多方來進行,但是很多時候多方的存在是依靠一方的。
比如(訂單、訂單子項)所以更新刪除的許可權需要授權給一方(Order)。
@ManyToOne,@OneToMany
註解 | 引數 | 描述 |
---|---|---|
@ManyToOne | 一個多對一的對映,該註解標註的屬性通常是資料庫表的外來鍵. | |
targetEntity | 目標類的實體 | |
cascade | 關聯到目標的操作 | |
fetch | 是否使用延遲載入 | |
@OneToMany | 一個 一對多的關聯,該屬性應該為集體型別,在資料庫中並沒有實際欄位 | |
targetEntity | 目標類的實體 | |
cascade | 關聯到目標的操作 | |
fetch | 是否使用延遲載入 | |
mappedBy | 反向關聯 | |
orphanRemoval | 講移除操作級聯到關聯的實體中 |
測試
一對多的時候雖然多方維持了兩者關係,但是我們把許可權賦予了一方所以,刪除多方並不會級聯操作,刪除一方可以移除多方資料。所以下的測試可以通過
@Test public void testOrder() { Order defOrder1 = OrderMock.getDefOrder1(); List <OrderItem> allDefOrder = OrderItemMock.getAllDefOrder(); // order 維持orderItem關係 defOrder1.setOrderItems(allDefOrder); // orderItem維持order關係 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); }
設定方法
和一對多、一對一不同,多對多沒法只使用兩個資料實體完成相互之間的關係維護,這個時候需要一個關聯的中間表來維護他們之間的關係。
對於中間表的設定,你大可不去進行額外的設定讓JPA自動生成,當然你也可以使用之前介紹的@JoinTable
註解進行設定。
許可權
和一對多、一對一不同,多對多一般是沒有辦法設定級聯操作的。因為雙方對應的都是集合物件,
而雙方某一條資料都可能被對方多條資料關聯。
程式碼
使用者表
@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會生成「表名1」+「_」+「表名2」的中間表 @JoinTable( // 定義中間表的名稱 name = "cascade_user_tag_table", // 定義中間表中關聯User表的外來鍵名 joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") }, // 定義中間表中關聯UserTag表的外來鍵名 inverseJoinColumns = { @JoinColumn(name = "tags_id", referencedColumnName = "id") } ) private Set<UserTag> tags; }
使用者標籤表
@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
提供的註解引數
註解 | 引數 | 描述 |
---|---|---|
@ManyToMany | 描述一個多對多的關聯.多對多關聯上是兩個一對多關聯,但是在ManyToMany描述中,中間表是由ORM框架自動處理 | |
targetEntity | 目標類的實體 | |
cascade | 關聯到目標的操作 | |
fetch | 是否使用延遲載入 | |
mappedBy | 反向關聯 | |
@JoinTable | JoinTable在many-to-many關係的所有者一邊定義。如果沒有定義JoinTable,使用JoinTable的預設值。 | |
catalog | 表的目錄 | |
foreignKey | 外來鍵約束,建立表的時候使用 | |
indexes | 表的索引,在生成表的時候使用 | |
@JoinColumns | 關係存在多個JoinColumn,用JoinColumns定義多個JoinColumn的屬性。 | |
foreignKey | 此列的外來鍵約束 | |
value | JoinColumn的集合 | |
inverseJoinColumns | 聯接表的外來鍵列,該列參照不擁有關聯的實體的主表 | |
joinColumns | 聯接表的外來鍵列,該列參照擁有關聯的實體的主表 | |
name | 進行連線的表名稱 | |
schema | 表的名稱空間 |
測試
多對多的情況下,我們使用雖然User和UserTag的關係由中間表維護,但是我們在User中設定了中間表的關係維護,所以此時刪除使用者的時候可以成功刪除,且可以成功移除中間表資料,但是這個時候移除UserTag資料的時候,卻會丟擲DataIntegrityViolationException異常。只能通過User移除中間關係。
@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); // 此時會報錯,因為中間表關係為User維護 try { userTagRepository.delete(defTag2); } catch (Exception e) { log.info(e.getCause().getMessage()); Assert.assertTrue(e instanceof DataIntegrityViolationException); } // 修改User表中關係 defUser1.setTags(new HashSet <>()); defUser1.getTags().add(defTag1); defUser1 = userRepository.save(defUser1); // 此時可以刪除 userTagRepository.delete(defTag2); // 直接刪除使用者沒問題 userRepository.delete(defUser1); }
本篇文章涉及的原始碼下載地址:https://gitee.com/daifyutils/springboot-samples
到此這篇關於Spring Boot 整合JPA 資料模型關聯操作(一對一、一對多、多對多)的文章就介紹到這了,更多相關Spring Boot 整合JPA 資料模型關聯內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45