欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringData JPA中@OneToMany和@ManyToOne的用法詳解

 更新時(shí)間:2021年10月15日 11:11:29   作者:cauchy6317  
這篇文章主要介紹了SpringData JPA中@OneToMany和@ManyToOne的用法詳解,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

一. 假設(shè)需求場景

在我們開發(fā)的過程中,經(jīng)常出現(xiàn)兩個(gè)對象存在一對多或多對一的關(guān)系。如何在程序在表明這兩個(gè)對象的關(guān)系,以及如何利用這種關(guān)系優(yōu)雅地使用它們。

其實(shí),在javax.persistence包下有這樣兩個(gè)注解——@OneTomany和@ManyToOne,可以為我們所用。

現(xiàn)在,我們假設(shè)需要開發(fā)一個(gè)校園管理系統(tǒng),管理各大高校的學(xué)生。這是一種典型的一對多場景,學(xué)校和學(xué)生的關(guān)系。這里,我們涉及簡單的級聯(lián)保存,查詢,刪除。

二. 代碼實(shí)現(xiàn)

2.1 級聯(lián)存儲操作

Student類和School類

@Data
@Table
@Entity
@Accessors(chain = true)
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    @ManyToOne
    @JoinColumn(name = "school_fk")
    private School school;
}

Student類上面的四個(gè)注解不做解釋,id主鍵使用自增策略。Student中有個(gè)School的實(shí)例變量school,表明學(xué)生所屬的學(xué)校。@ManyToOne(多對一注解)代表在學(xué)生和學(xué)校關(guān)系中“多”的那方,學(xué)生是“多”的那方,所以在Student類里面使用@ManyToOne。

那么,@ManyToOne中One當(dāng)然是指學(xué)校了,也就是School類。

@JoinColumn(name = “school_fk”)指明School類的主鍵id在student表中的字段名,如果此注解不存在,生成的student表如下:

在這里插入圖片描述

@Data
@Table
@Entity
@Accessors(chain = true)
public class School {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    @OneToMany(mappedBy="school",cascade = CascadeType.PERSIST)
    private List<Student> students;
}

在School類中,維護(hù)一個(gè)類型為List的students實(shí)例變量。@OneToMany(一對多注解)代表在學(xué)生和學(xué)校關(guān)系中“一”的那方,學(xué)校是“一”的那方,所以在School類里面使用@OneToMany。

那么,@OneToMany中many當(dāng)然是指學(xué)生了,也就是Student類。注意@OneToMany中有個(gè)mappedBy參數(shù)設(shè)置為school,這個(gè)值是我們在Student類中的School類型的變量名;cascade參數(shù)表示級聯(lián)操作的類型,它只能是CascadeType的6種枚舉類型。

有的博客經(jīng)常寫成cascade = CascadeType.ALL,這其實(shí)會誤導(dǎo)大家,因?yàn)槔锩娴募壜?lián)刪除會讓你懷疑人生。

我們先使用CascadeType.PERSIST,表示在持久化的級聯(lián)操作,也就是保存學(xué)校的時(shí)候可以一起保存學(xué)生。

StudentRepository和SchoolRepository

public interface StudentRepository extends JpaRepository<Student, Integer> {
}
public interface SchoolRepository extends JpaRepository<School, Integer> {
}

測試類

@RunWith(SpringRunner.class)
@SpringBootTest
public class MultiDateSourceApplicationTests {
    @Autowired
    SchoolRepository schoolRepository;
    @Test
    public void contextLoads() {
        Student jackMa = new Student().setName("Jack Ma");
        Student jackChen = new Student().setName("Jack Chen");
        School school = new School().setName("湖畔大學(xué)");
        List<Student> students = new ArrayList<>();
        students.add(jackMa);
        students.add(jackChen);
        jackMa.setSchool(school);
        jackChen.setSchool(school);
        school.setStudents(students);
        schoolRepository.save(school);
    }
}

運(yùn)行測試類后,數(shù)據(jù)庫的表數(shù)據(jù)如下:

在這里插入圖片描述

在程序中,我們并沒有調(diào)用StudentRepository的save方法,但是我們在@OneToMany中添加了級聯(lián)保存參數(shù)CascadeType.PERSIST,所以在保存學(xué)校的時(shí)候能自動保存學(xué)生, jackMa.setSchool(school);jackChen.setSchool(school);這兩句肯定不能少的。

2.2 查詢操作和toSting問題

上面的添加操作成功了,讓我們來試試查詢操作。

在這里插入圖片描述

控制臺:打印出的錯誤是org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.cauchy6317.multidatesource.cascadestudy.entity.School.students, could not initialize proxy - no Session

這是因?yàn)锧OneToMany的fetch參數(shù)默認(rèn)設(shè)置為FetchType.Lazy模式,即懶加載模式。

也就是說,我們查詢mySchool的時(shí)候,并沒有把在該學(xué)校的學(xué)生查出來。而且,School類的toString方法需要知道students,所以debug模式下mySchool變量報(bào)錯。

我們把@OneToMany的fetch參數(shù)改為Fetch.EAGER,即熱加載。

    @OneToMany(mappedBy="school", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
    private List<Student> students;

再運(yùn)行一次…

在這里插入圖片描述

這次的錯誤是StackOverflowError,為什么會這樣呢?堆棧溢出,也就是我們寫的程序出現(xiàn)了死循環(huán)。可是我們都沒寫循環(huán)語句啊,不急,我們先看看這個(gè)mySchool數(shù)據(jù)。

我們發(fā)現(xiàn)mySchool里面有students,而且students里面又有school變量,變量school里面自然又有students了。由此看來,是這個(gè)死循環(huán)的導(dǎo)致。也就是Student和School的toString方法,循環(huán)調(diào)用彼此。

所以只需要修改其中一個(gè)的toString方法,使它的toString方法不涉及另一個(gè)類型的變量,也就是排除另一個(gè)類型的變量。lombok考慮到這點(diǎn)了,可以使用ToString.exclude

在官網(wǎng)的ToString介紹頁面中,我看到了這個(gè)有意思的小字部分。

在這里插入圖片描述

哈哈哈,這個(gè)地方已經(jīng)說明了如果使用數(shù)組中包含自身,ToString方法會報(bào)StackOverflowError。

那么,我們在Student類中使用ToString.exclude,還是在School類中使用ToString.exclude呢?我們先在School類中試試。

    @ToString.Exclude
    @OneToMany(mappedBy="school", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
    private List<Student> students;

這次我們把學(xué)生也打印出來一個(gè)。

在這里插入圖片描述

可以看到,mySchool的ToString方法沒有將students打印出來;student的toSting方法將School打印出來了。如果在Student類的school變量上使用@ToString.EXCLUDE的話,那么mySchool就會打印出很多student來。

所以,我覺得還是在private List students;上使用@ToString.EXCLUDE較好。

2.3 級聯(lián)刪除

前面我們說過級聯(lián)刪除會讓人懷疑人生,讓我們用代碼來感受一下。

    @ToString.Exclude
    @OneToMany(mappedBy="school", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, fetch = FetchType.EAGER)
    private List<Student> students;

我們在School類中,使用級聯(lián)刪除。也就是說,當(dāng)我們刪除某個(gè)學(xué)校的時(shí)候,把這個(gè)學(xué)校下的所有學(xué)生刪除掉!

在這里插入圖片描述

現(xiàn)在查看數(shù)據(jù)庫的表,可以清楚的看到。school中id為1的學(xué)校沒有了,而且student中學(xué)校外鍵為1的學(xué)生也全部被刪了?;蛟S你會覺得這也沒什么大不了的,因?yàn)閷W(xué)校不存在了,學(xué)校里的學(xué)生自然不存在了。好,那就讓我們來見識一下級聯(lián)刪除的真正威力。我們?nèi)绻苍赟tudent類中使用了級聯(lián)刪除會怎么樣?

    @ManyToOne(cascade = CascadeType.REMOVE)
    @JoinColumn(name = "school_fk")
    private School school;

也就是說,當(dāng)我們刪除某個(gè)學(xué)生時(shí),會級聯(lián)刪除學(xué)生所在的學(xué)校。我們用代碼測試一下是不是這樣。

public interface StudentRepository extends JpaRepository<Student, Integer> {
    /**
     * 根據(jù)姓名刪除學(xué)生對象
     * @param name
     * @return
     */
    @Transactional
    Integer deleteByName(String name);
}

在這里插入圖片描述

可以看到數(shù)據(jù)插入成功了,當(dāng)我們放掉斷點(diǎn)后。

在這里插入圖片描述

可以看到出現(xiàn)了三條刪除語句,我再看看數(shù)據(jù)庫的學(xué)生表,發(fā)現(xiàn)Jack Chen也被刪除了。這是因?yàn)槲覀冊赟tudent類和School類中都使用了級聯(lián)刪除,當(dāng)我們刪除Jack Ma的時(shí)候,級聯(lián)刪除了湖畔大學(xué),當(dāng)刪除湖畔大學(xué)后又級聯(lián)刪除了所有湖畔大學(xué)的student。這就好比,你打算開除一個(gè)學(xué)生,結(jié)果把學(xué)校和學(xué)生的數(shù)據(jù)全刪沒了。是不是很刺激?

2.4 pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.28</version>
        </dependency>
    </dependencies>

環(huán)境:springboot2.1.7+jdk1.8+mysql8.0+druid1.1.10+Springdata JPA+Lombok

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java判斷主機(jī)是否能ping通代碼實(shí)例

    Java判斷主機(jī)是否能ping通代碼實(shí)例

    這篇文章主要介紹了Java判斷主機(jī)是否能ping通代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Java 添加和刪除PDF圖層的示例代碼

    Java 添加和刪除PDF圖層的示例代碼

    本文將介紹如何使用Spire.PDF for Java來添加和刪除PDF圖層,本文通過示例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-02-02
  • java連接Oracle數(shù)據(jù)庫的方法解析

    java連接Oracle數(shù)據(jù)庫的方法解析

    本文主要對java連接Oracle數(shù)據(jù)庫方法進(jìn)行步驟解析,具有很好的參考價(jià)值,需要的朋友一起來看下吧
    2016-12-12
  • RestTemplate未使用線程池問題的解決方法

    RestTemplate未使用線程池問題的解決方法

    今天給大家?guī)淼氖顷P(guān)于Springboot的相關(guān)知識,文章圍繞著RestTemplate未使用線程池展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • JVM的垃圾回收算法工作原理詳解

    JVM的垃圾回收算法工作原理詳解

    這篇文章主要介紹了JVM的垃圾回收算如何判斷對象是否可以被回收,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下
    2019-06-06
  • Springboot通過Scheduled實(shí)現(xiàn)定時(shí)任務(wù)代碼

    Springboot通過Scheduled實(shí)現(xiàn)定時(shí)任務(wù)代碼

    這篇文章主要介紹了Springboot通過Scheduled實(shí)現(xiàn)定時(shí)任務(wù)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Java數(shù)組集合的深度復(fù)制代碼實(shí)例

    Java數(shù)組集合的深度復(fù)制代碼實(shí)例

    這篇文章主要介紹了Java數(shù)組集合的深度復(fù)制代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Java中的collection集合類型總結(jié)

    Java中的collection集合類型總結(jié)

    Java的集合類型都是對java.util包中Collection接口的繼承,這里我們主要介紹依賴于collection的一些主分支,一起來看一下Java中的collection集合類型總結(jié)
    2016-05-05
  • JDK動態(tài)代理原理:只能代理接口,不能代理類問題

    JDK動態(tài)代理原理:只能代理接口,不能代理類問題

    這篇文章主要介紹了JDK動態(tài)代理原理:只能代理接口,不能代理類問題。具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • SpringBoot訪問接口自動跳轉(zhuǎn)login頁面的問題及解決

    SpringBoot訪問接口自動跳轉(zhuǎn)login頁面的問題及解決

    這篇文章主要介紹了SpringBoot訪問接口自動跳轉(zhuǎn)login頁面的問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12

最新評論