<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
首先,實體與實體之間的關聯關係一共分為四種,分別為OneToOne、OneToMany、ManyToOne和ManyToMany;而實體之間的關聯關係又分為雙向和單向。實體之間的關聯關係是在JPA使用中最容易發生問題的地方。
@OneToOne一般表示物件之間一對一的關聯關係,它可以放在field上面,也可以放在get/set方法上面。其中JPA協定有規定,如果設定雙向關聯,維護關聯關係的是擁有外來鍵的一方,而另一方必須設定mappedBy;如果是單項關聯,直接設定在擁有外來鍵的一方即可。
舉例說明:
user表是使用者的主資訊,user_info是使用者的拓展資訊,兩者之間是一對一的關係。user_info表裡面有一個user_id作為關聯關係的外來鍵,如果是單項關聯,我們的寫法如下:
@Data @Entity @NoArgsConstructor @AllArgsConstructor @Builder public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private String email; private String sex; private String address; }
我們只需要在擁有外來鍵的一方設定@OneToOne註解就可以了
@Entity @Data @Builder @AllArgsConstructor @NoArgsConstructor @ToString(exclude = "user") public class UserInfo { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private Integer ages; private String telephone; @OneToOne private User user; }
這就是單向關聯關係,那麼如何設定雙向關聯關係呢? 我們保持UserInfo不變,在User實體物件裡面新增一段程式碼即可
@OneToOne(mappedBy = "user") private UserInfo userInfo; @Data @Entity @NoArgsConstructor @AllArgsConstructor @Builder public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; private String name; private String email; private String sex; private String address; @OneToOne(mappedBy = "user") private UserInfo userInfo; }
public @interface OneToOne { Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default EAGER; boolean optional() default true; String mappedBy() default ""; boolean orphanRemoval() default false; }
targetEntity:作為關聯目標的實體類。
cascade:級聯操作策略,就是我們常說的級聯操作。
fetch:資料獲取方式EAGER(立即載入)/LAZY(延遲載入) optional:表示關聯的實體是否能夠存在null值 mappedBy:關聯關係被誰維護的一方物件裡面的屬性名字,雙向關聯的時候必填。
在CascadeType的用法中,CascadeType的列舉值只有5個,分別如下:
測試級聯新建和級聯刪除:
第一步: 在@OneToOne上面新增 cascade = {CascadeType.PERSIST,CascadeType.REMOVE},程式碼如下所示:
@Entity @Data @Builder @AllArgsConstructor @NoArgsConstructor @ToString(exclude = "user") public class UserInfo { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private Integer ages; private String telephone; @OneToOne(cascade = {CascadeType.PERSIST,CascadeType.REMOVE}) private User user; }
新增測試方法:
@Test public void tesyPersistAndRemove(){ User user = User.builder() .name("jackxx") .email("123456@126.com") .build(); UserInfo userInfo = UserInfo.builder() .ages(12) .user(user) .telephone("12345678") .build(); // 新建UserInfo,級聯新建User userInfoRepo.save(userInfo); // 刪除UserInfo,級聯刪除User userInfoRepo.delete(userInfo); }
執行SQL如下所示:
從上面執行結果中可以看到,執行insert的時候,會先插入user表,再插入user_info表。 執行delete的時候,先刪除user_info表中資料,再刪除user表中的資料。
上面只是講述級聯刪除的場景,下面我們再說一下關聯關係的刪除場景該怎麼做?
orphanRemoval表示當關聯關係被刪除的時候,是否應用級聯刪除。
首先我們,沿用上面的例子,當我們刪除userinfo的時候,把user置空
userInfo.setUser(null); userInfoRepo.delete(userInfo);
再看執行結果
Hibernate: delete from user_info where id=?
我們只刪除了UserInfo的資料,沒有刪除user的資料,說明沒有進行級聯刪除,我們將orphanRemoval屬性設定為true
@OneToOne(cascade = {CascadeType.PERSIST},orphanRemoval = true) private User user;
測試程式碼:
@Test public void testRemove(){ User user = User.builder() .name("jackxx") .email("123456@126.com") .build(); UserInfo userInfo = UserInfo.builder() .ages(12) .user(user) .telephone("12345678") .build(); // 新建UserInfo,級聯新建User userInfoRepo.save(userInfo); userInfo.setUser(null); // 刪除UserInfo,級聯刪除User userInfoRepo.delete(userInfo); }
執行結果如下所示:
在執行結果中多了一條update語句,是因為去掉了CascadeType.REMOVE,這個時候不會進行級聯刪除了。當我們把user物件更新為null的時候,就會執行一個update語句把關聯關係去掉。
這兩個註解是集合關係,他們可以同時使用,@JoinColumn表示單欄位,@JoinColumns表示多個@JoinColumn
@JoinColumn原始碼
public @interface JoinColumn { String name() default ""; String referencedColumnName() default ""; boolean unique() default false; boolean nullable() default true; boolean insertable() default true; boolean updatable() default true; String columnDefinition() default ""; String table() default ""; ForeignKey foreignKey() default @ForeignKey(PROVIDER_DEFAULT); }
// 外來鍵策略 public enum ConstraintMode { // 建立外來鍵約束 CONSTRAINT, // 不建立外來鍵約束 NO_CONSTRAINT, // 採用預設行為 PROVIDER_DEFAULT }
foreignKey的用法:
@OneToOne(cascade = {CascadeType.PERSIST},orphanRemoval = true) @JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), name = "user_id") private User user;
JoinColumns的用法:
@OneToOne(cascade = {CascadeType.PERSIST},orphanRemoval = true) @JoinColumns({ @JoinColumn(name = "user_id",referencedColumnName = "ID"), @JoinColumn(name = "user_ZIP",referencedColumnName = "ZIP") }) private User user;
@ManyToOne代表多對一的關聯關係,而@OneToMany代表一對多,一般兩個成對使用表示雙向關聯關係。在JPA協定中也是明確規定:維護關聯關係的是擁有外來鍵的一方,而另一方必須設定mappedBy
public @interface OneToMany { Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default LAZY; String mappedBy() default ""; boolean orphanRemoval() default false; } public @interface ManyToOne { Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default EAGER; boolean optional() default true; }
使用這兩個欄位,需要注意以下幾點:
舉例說明 : 假設User有多個地址Address
@Data @Entity @NoArgsConstructor @AllArgsConstructor @Builder public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; private String name; private String email; private String sex; @OneToMany(mappedBy = "user",fetch = FetchType.LAZY) private List<UserAddress> address; }
@OneToMany 雙向關聯並且採用LAZY的機制
@Entity @Data @Builder @AllArgsConstructor @NoArgsConstructor public class UserAddress { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String address; @ManyToOne(cascade = CascadeType.ALL) private User user; }
測試程式碼 :
@Test @Transactional public void testUserAddress(){ User user = User.builder() .name("jackxx") .email("123456@126.com") .build(); UserAddress userAddress = UserAddress.builder() .address("shanghai1") .user(user) .build(); UserAddress userAddress1 = UserAddress.builder() .address("shanghai2") .user(user) .build(); addressRepo.saveAll(Lists.newArrayList(userAddress,userAddress1)); User u = userRepo.findById(1).get(); System.out.println(u.getName()); System.out.println(u.getAddress()); }
執行結果如下所示:
可以看到當我們想要輸出Address資訊的時候,才會載入Addres的資訊
@ManyToMany代表多對多的關聯關係、這種關聯關係任何一方都可以維護關聯關係。
我們假設user表和room表是多對多的關係,如下所示:
@Data @Entity @NoArgsConstructor @AllArgsConstructor @Builder public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; private String name; @ManyToMany(mappedBy = "users") private List<Room> rooms; }
@Entity @Data @Builder @AllArgsConstructor @NoArgsConstructor @ToString public class Room { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; @ManyToMany private List<User> users; }
這種方法實不可取,當用到@ManyToMany的時候一定是三張表,不要想著建兩張表,兩張表肯定是違背表的原則
改進方法:建立中間表 修改Romm裡面的內容
@Entity @Data @Builder @AllArgsConstructor @NoArgsConstructor @ToString public class Room { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; @ManyToMany @JoinTable(name = "user_room", joinColumns = @JoinColumn(name = "room_id"), inverseJoinColumns = @JoinColumn(name = "user_id")) private List<User> users; }
可以看到我們通過@JoinTable註解建立一張中間表,並且新增了兩個設定的外來鍵,我們來看看@JoinTable的原始碼:
public @interface JoinTable { String name() default ""; String catalog() default ""; String schema() default ""; JoinColumn[] joinColumns() default {}; JoinColumn[] inverseJoinColumns() default {}; ForeignKey foreignKey() default @ForeignKey(PROVIDER_DEFAULT); ForeignKey inverseForeignKey() default @ForeignKey(PROVIDER_DEFAULT); UniqueConstraint[] uniqueConstraints() default {}; Index[] indexes() default {}; }
在現實開發中,@ManyToMany註解用的比較少,一般都會使用成對的@ManyToOne 和 @OneToMany代替,因為我們的中間表可能還有一些約定的公共欄位,如ID,update_time,create_time等其他欄位
在上面的Demo中,我們稍作修改,新建一張user_room 中間表來儲存雙方的關聯關係和額外欄位
如下所示: user_room中間表
@Entity @Data @Builder @AllArgsConstructor @NoArgsConstructor public class user_room { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Date createTime; private Date updateTime; @ManyToOne private User user; @ManyToOne private Room room; }
user表
@Data @Entity @NoArgsConstructor @AllArgsConstructor @Builder public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; @OneToMany(mappedBy = "user") private List<user_room> userRoomList; }
room表
@Entity @Data @Builder @AllArgsConstructor @NoArgsConstructor @ToString public class Room { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @OneToMany(mappedBy = "room") private List<user_room> roomList; }
以上就是Spring Data JPA 註解Entity關聯關係使用詳解的詳細內容,更多關於Spring Data JPA Entity的資料請關注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