最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Data?JPA?注解Entity關(guān)聯(lián)關(guān)系使用詳解

 更新時(shí)間:2022年09月28日 16:00:25   作者:AKone  
這篇文章主要為大家介紹了Spring?Data?JPA?注解Entity關(guān)聯(lián)關(guān)系使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

首先,實(shí)體與實(shí)體之間的關(guān)聯(lián)關(guān)系一共分為四種,分別為OneToOne、OneToMany、ManyToOne和ManyToMany;而實(shí)體之間的關(guān)聯(lián)關(guān)系又分為雙向和單向。實(shí)體之間的關(guān)聯(lián)關(guān)系是在JPA使用中最容易發(fā)生問題的地方。

1、OneToOne關(guān)聯(lián)關(guān)系

@OneToOne一般表示對(duì)象之間一對(duì)一的關(guān)聯(lián)關(guān)系,它可以放在field上面,也可以放在get/set方法上面。其中JPA協(xié)議有規(guī)定,如果配置雙向關(guān)聯(lián),維護(hù)關(guān)聯(lián)關(guān)系的是擁有外鍵的一方,而另一方必須配置mappedBy;如果是單項(xiàng)關(guān)聯(lián),直接配置在擁有外鍵的一方即可。

舉例說明:

user表是用戶的主信息,user_info是用戶的拓展信息,兩者之間是一對(duì)一的關(guān)系。user_info表里面有一個(gè)user_id作為關(guān)聯(lián)關(guān)系的外鍵,如果是單項(xiàng)關(guān)聯(lián),我們的寫法如下:

@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;
}

這就是單向關(guān)聯(lián)關(guān)系,那么如何設(shè)置雙向關(guān)聯(lián)關(guān)系呢? 我們保持UserInfo不變,在User實(shí)體對(duì)象里面添加一段代碼即可

@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;
}

1.1 解讀OneToOne源碼

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:作為關(guān)聯(lián)目標(biāo)的實(shí)體類。
cascade:級(jí)聯(lián)操作策略,就是我們常說的級(jí)聯(lián)操作。
fetch:數(shù)據(jù)獲取方式EAGER(立即加載)/LAZY(延遲加載) optional:表示關(guān)聯(lián)的實(shí)體是否能夠存在null值 mappedBy:關(guān)聯(lián)關(guān)系被誰維護(hù)的一方對(duì)象里面的屬性名字,雙向關(guān)聯(lián)的時(shí)候必填。

1.2 mappedBy 注意事項(xiàng)

  • 只有關(guān)聯(lián)關(guān)系的維護(hù)方才能操作兩個(gè)實(shí)體之間外鍵的關(guān)系。被維護(hù)方即使設(shè)置維護(hù)方屬性進(jìn)行存儲(chǔ)也不會(huì)更新外鍵關(guān)聯(lián)
  • mappedBy不能與@JoinColumn或者@JoinTable同時(shí)使用,因?yàn)闆]有任何意義,關(guān)聯(lián)關(guān)系不在這里面維護(hù)。
  • mappedBy的值是指另一方的實(shí)體里面屬性的字段,而不是數(shù)據(jù)庫字段,也不是實(shí)體的對(duì)象的名字。也就是維護(hù)關(guān)聯(lián)關(guān)系的一方屬性字段名稱,或者加了@JoinColumn 或 @JoinTable注解的屬性字段名稱。如上面的User例子user里面的mappedBy的值,就是userinfo里面的user字段的名字。

1.3 CascadeType 用法

在CascadeType的用法中,CascadeType的枚舉值只有5個(gè),分別如下:

  • CascadeType.PERSIST 級(jí)聯(lián)新建
  • CascadeType.REMOVE 級(jí)聯(lián)刪除
  • CascadeType.PEFRESH 級(jí)聯(lián)刷新
  • CascadeType.MERGE 級(jí)聯(lián)更新
  • CascadeType.ALL 四項(xiàng)全選

測試級(jí)聯(lián)新建和級(jí)聯(lián)刪除:

第一步: 在@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,級(jí)聯(lián)新建User
    userInfoRepo.save(userInfo);
    // 刪除UserInfo,級(jí)聯(lián)刪除User
    userInfoRepo.delete(userInfo);
}

執(zhí)行SQL如下所示:

從上面運(yùn)行結(jié)果中可以看到,執(zhí)行insert的時(shí)候,會(huì)先插入user表,再插入user_info表。 執(zhí)行delete的時(shí)候,先刪除user_info表中數(shù)據(jù),再刪除user表中的數(shù)據(jù)。

上面只是講述級(jí)聯(lián)刪除的場景,下面我們?cè)僬f一下關(guān)聯(lián)關(guān)系的刪除場景該怎么做?

1.4 orphanRemoval屬性用法

orphanRemoval表示當(dāng)關(guān)聯(lián)關(guān)系被刪除的時(shí)候,是否應(yīng)用級(jí)聯(lián)刪除。

首先我們,沿用上面的例子,當(dāng)我們刪除userinfo的時(shí)候,把user置空

userInfo.setUser(null);
userInfoRepo.delete(userInfo);

再看運(yùn)行結(jié)果

Hibernate: delete from user_info where id=?

我們只刪除了UserInfo的數(shù)據(jù),沒有刪除user的數(shù)據(jù),說明沒有進(jìn)行級(jí)聯(lián)刪除,我們將orphanRemoval屬性設(shè)置為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,級(jí)聯(lián)新建User
    userInfoRepo.save(userInfo);
    userInfo.setUser(null);
    // 刪除UserInfo,級(jí)聯(lián)刪除User
    userInfoRepo.delete(userInfo);
}

執(zhí)行結(jié)果如下所示:

在執(zhí)行結(jié)果中多了一條update語句,是因?yàn)槿サ袅薈ascadeType.REMOVE,這個(gè)時(shí)候不會(huì)進(jìn)行級(jí)聯(lián)刪除了。當(dāng)我們把user對(duì)象更新為null的時(shí)候,就會(huì)執(zhí)行一個(gè)update語句把關(guān)聯(lián)關(guān)系去掉。

1.5 orphanRemoval 和 CascadeType.REMOVE的區(qū)別

  • CascadeType.REMOVE 級(jí)聯(lián)刪除,先刪除user表的數(shù)據(jù),再刪除user_info表的數(shù)據(jù)。 (因?yàn)榇嬖谕怄I關(guān)聯(lián),無法先刪除user_info表的數(shù)據(jù))
  • orphanRemoval = true 先將user_info表中的數(shù)據(jù)外鍵user_id 更新為 null,然后刪除user_info表的數(shù)據(jù),再刪除user表的數(shù)據(jù)。

2、@JoinColumns & @JoinColumn

這兩個(gè)注解是集合關(guān)系,他們可以同時(shí)使用,@JoinColumn表示單字段,@JoinColumns表示多個(gè)@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);
}
  • name :代表外鍵的字段名。
  • referencedColumnName :關(guān)聯(lián)表對(duì)應(yīng)的字段,如果不注明,默認(rèn)就是關(guān)聯(lián)表的主鍵
  • unique:外鍵字段是否唯一
  • nullable:外鍵字段是否允許為空
  • insertable:是否跟隨一起新增
  • updateable:是否跟隨一起更新
  • columnDefinition:為列生成DDL時(shí)使用的SQL片段
  • foreignKey:外鍵策略
// 外鍵策略
public enum ConstraintMode {
  // 創(chuàng)建外鍵約束
  CONSTRAINT,
  // 不創(chuàng)建外鍵約束
  NO_CONSTRAINT,
  // 采用默認(rèn)行為
  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;

3、@ManyToOne & @OneToMany

@ManyToOne代表多對(duì)一的關(guān)聯(lián)關(guān)系,而@OneToMany代表一對(duì)多,一般兩個(gè)成對(duì)使用表示雙向關(guān)聯(lián)關(guān)系。在JPA協(xié)議中也是明確規(guī)定:維護(hù)關(guān)聯(lián)關(guān)系的是擁有外鍵的一方,而另一方必須配置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;
}

使用這兩個(gè)字段,需要注意以下幾點(diǎn):

  • @ManyToOne 一定是維護(hù)外鍵關(guān)系的一方,所以沒有mappedBy字段;
  • @ManyToOne 刪除的時(shí)候一定不能把One的一方刪除了,所以也沒有orphanRemoval選項(xiàng);
  • @ManyToOne 的Lazy效果和 @OneToOne 的一樣,所以和上面的用法基本一致;
  • @OneToMany 的Lazy是有效果的;

3.1 Lazy機(jī)制

舉例說明 : 假設(shè)User有多個(gè)地址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 雙向關(guān)聯(lián)并且采用LAZY的機(jī)制

@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());
}

運(yùn)行結(jié)果如下所示:

可以看到當(dāng)我們想要輸出Address信息的時(shí)候,才會(huì)加載Addres的信息

4、ManyToMany

@ManyToMany代表多對(duì)多的關(guān)聯(lián)關(guān)系、這種關(guān)聯(lián)關(guān)系任何一方都可以維護(hù)關(guān)聯(lián)關(guān)系。

我們假設(shè)user表和room表是多對(duì)多的關(guān)系,如下所示:

@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;
}

這種方法實(shí)不可取,當(dāng)用到@ManyToMany的時(shí)候一定是三張表,不要想著建兩張表,兩張表肯定是違背表的原則

改進(jìn)方法:創(chuàng)建中間表 修改Romm里面的內(nèi)容

@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注解創(chuàng)建一張中間表,并且添加了兩個(gè)設(shè)定的外鍵,我們來看看@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 {};
}
  • name:中間表名稱
  • joinColumns:維護(hù)關(guān)聯(lián)關(guān)系一方的外鍵字段的名字
  • inverseJoinColumns:另一方表的外鍵字段的名字

在現(xiàn)實(shí)開發(fā)中,@ManyToMany注解用的比較少,一般都會(huì)使用成對(duì)的@ManyToOne 和 @OneToMany代替,因?yàn)槲覀兊闹虚g表可能還有一些約定的公共字段,如ID,update_time,create_time等其他字段

4.1 利用@ManyToOne 和 @OneToMany表達(dá)多對(duì)多的關(guān)聯(lián)關(guān)系

在上面的Demo中,我們稍作修改,新建一張user_room 中間表來存儲(chǔ)雙方的關(guān)聯(lián)關(guān)系和額外字段

如下所示: 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關(guān)聯(lián)關(guān)系使用詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring Data JPA Entity的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java創(chuàng)建線程三種方式的優(yōu)缺點(diǎn)

    Java創(chuàng)建線程三種方式的優(yōu)缺點(diǎn)

    今天小編就為大家分享一篇關(guān)于Java創(chuàng)建線程三種方式的優(yōu)缺點(diǎn),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • nacos-discovery包名層級(jí)問題解決

    nacos-discovery包名層級(jí)問題解決

    這篇文章主要為大家介紹了nacos-discovery包名層級(jí)問題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • Java JUC中操作List安全類的集合案例

    Java JUC中操作List安全類的集合案例

    這篇文章主要介紹了JUC中操作List安全類的集合案例,本文羅列了不安全的集合和安全的集合進(jìn)行對(duì)比,以及Java中提供的安全措施,需要的朋友可以參考下
    2021-07-07
  • maven打包時(shí)候修改包名稱帶上git版本號(hào)和打包時(shí)間方式

    maven打包時(shí)候修改包名稱帶上git版本號(hào)和打包時(shí)間方式

    這篇文章主要介紹了maven打包時(shí)候修改包名稱帶上git版本號(hào)和打包時(shí)間方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • SpringBoot創(chuàng)建動(dòng)態(tài)定時(shí)任務(wù)的幾種方式小結(jié)

    SpringBoot創(chuàng)建動(dòng)態(tài)定時(shí)任務(wù)的幾種方式小結(jié)

    SpringBoot提供了多種實(shí)現(xiàn)定時(shí)任務(wù)的方式,包括使用@Scheduled注解、SchedulingConfigurer接口、TaskScheduler接口和Quartz框架,@Scheduled適合簡單的定時(shí)任務(wù),文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • Spring?Boot整合Log4j2.xml的問題及解決方法

    Spring?Boot整合Log4j2.xml的問題及解決方法

    這篇文章主要介紹了Spring?Boot整合Log4j2.xml的問題,本文給大家分享解決方案,需要的朋友可以參考下
    2023-09-09
  • Spring Boot使用Thymeleaf + Gradle構(gòu)建war到Tomcat

    Spring Boot使用Thymeleaf + Gradle構(gòu)建war到Tomcat

    今天小編就為大家分享一篇關(guān)于Spring Boot使用Thymeleaf + Gradle構(gòu)建war到Tomcat,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • Java并發(fā)編程中的Exchanger解析

    Java并發(fā)編程中的Exchanger解析

    這篇文章主要介紹了Java并發(fā)編程中的Exchanger解析,Exchanger用于線程間數(shù)據(jù)的交換,它提供一個(gè)同步點(diǎn),在這個(gè)同步點(diǎn),兩個(gè)線程可以交換彼此的數(shù)據(jù),這兩個(gè)線程通過exchange方法交換數(shù)據(jù),如果第一個(gè)線程先執(zhí)行exchange()方法,需要的朋友可以參考下
    2023-11-11
  • springboot+vue部署按照及運(yùn)行方法

    springboot+vue部署按照及運(yùn)行方法

    在本篇文章里小編給大家整理了關(guān)于springboot+vue部署按照及運(yùn)行方法和實(shí)例內(nèi)容,需要的朋友們學(xué)習(xí)參考下。
    2020-01-01
  • Java實(shí)現(xiàn)圖片倒影的源碼實(shí)例內(nèi)容

    Java實(shí)現(xiàn)圖片倒影的源碼實(shí)例內(nèi)容

    在本篇文章里小編給大家整理的是關(guān)于Java實(shí)現(xiàn)圖片倒影的源碼以及相關(guān)知識(shí)點(diǎn),有需要的朋友們學(xué)習(xí)下。
    2019-09-09

最新評(píng)論

永州市| 桂林市| 郓城县| 前郭尔| 余姚市| 滦平县| 葵青区| 中山市| 河源市| 晋宁县| 大关县| 同心县| 革吉县| 庄浪县| 余庆县| 集安市| 普格县| 剑阁县| 安平县| 仁怀市| 乾安县| 鄂州市| 杭锦后旗| 新竹县| 牟定县| 威海市| 张北县| 增城市| 剑阁县| 民乐县| 湖南省| 富川| 岚皋县| 日照市| 泸溪县| 鹿泉市| 铁力市| 天长市| 南漳县| 南雄市| 台前县|