一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringData JPA中@OneToMany和@ManyToOne的用法詳解

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

2022-02-21 00:39cauchy6317 Java教程

這篇文章主要介紹了SpringData JPA中@OneToMany和@ManyToOne的用法詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

一. 假設需求場景

在我們開發的過程中,經常出現兩個對象存在一對多或多對一的關系。如何在程序在表明這兩個對象的關系,以及如何利用這種關系優雅地使用它們。

其實,在javax.persistence包下有這樣兩個注解――@OneTomany和@ManyToOne,可以為我們所用。

現在,我們假設需要開發一個校園管理系統,管理各大高校的學生。這是一種典型的一對多場景,學校和學生的關系。這里,我們涉及簡單的級聯保存,查詢,刪除。

 

二. 代碼實現

2.1 級聯存儲操作

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類上面的四個注解不做解釋,id主鍵使用自增策略。Student中有個School的實例變量school,表明學生所屬的學校。@ManyToOne(多對一注解)代表在學生和學校關系中“多”的那方,學生是“多”的那方,所以在Student類里面使用@ManyToOne。

那么,@ManyToOne中One當然是指學校了,也就是School類。

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

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

@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類中,維護一個類型為List的students實例變量。@OneToMany(一對多注解)代表在學生和學校關系中“一”的那方,學校是“一”的那方,所以在School類里面使用@OneToMany。

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

有的博客經常寫成cascade = CascadeType.ALL,這其實會誤導大家,因為里面的級聯刪除會讓你懷疑人生。

我們先使用CascadeType.PERSIST,表示在持久化的級聯操作,也就是保存學校的時候可以一起保存學生。

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("湖畔大學");
      List<Student> students = new ArrayList<>();
      students.add(jackMa);
      students.add(jackChen);
      jackMa.setSchool(school);
      jackChen.setSchool(school);
      school.setStudents(students);
      schoolRepository.save(school);
  }
}

運行測試類后,數據庫的表數據如下:

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

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

2.2 查詢操作和toSting問題

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

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

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

這是因為@OneToMany的fetch參數默認設置為FetchType.Lazy模式,即懶加載模式。

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

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

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

再運行一次…

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

這次的錯誤是StackOverflowError,為什么會這樣呢?堆棧溢出,也就是我們寫的程序出現了死循環。可是我們都沒寫循環語句啊,不急,我們先看看這個mySchool數據。

我們發現mySchool里面有students,而且students里面又有school變量,變量school里面自然又有students了。由此看來,是這個死循環的導致。也就是Student和School的toString方法,循環調用彼此

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

在官網的ToString介紹頁面中,我看到了這個有意思的小字部分。

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

哈哈哈,這個地方已經說明了如果使用數組中包含自身,ToString方法會報StackOverflowError。

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

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

這次我們把學生也打印出來一個。

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

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

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

2.3 級聯刪除

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

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

我們在School類中,使用級聯刪除。也就是說,當我們刪除某個學校的時候,把這個學校下的所有學生刪除掉!

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

現在查看數據庫的表,可以清楚的看到。school中id為1的學校沒有了,而且student中學校外鍵為1的學生也全部被刪了。或許你會覺得這也沒什么大不了的,因為學校不存在了,學校里的學生自然不存在了。好,那就讓我們來見識一下級聯刪除的真正威力。我們如果也在Student類中使用了級聯刪除會怎么樣?

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

也就是說,當我們刪除某個學生時,會級聯刪除學生所在的學校。我們用代碼測試一下是不是這樣。

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

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

可以看到數據插入成功了,當我們放掉斷點后。

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

可以看到出現了三條刪除語句,我再看看數據庫的學生表,發現Jack Chen也被刪除了。這是因為我們在Student類和School類中都使用了級聯刪除,當我們刪除Jack Ma的時候,級聯刪除了湖畔大學,當刪除湖畔大學后又級聯刪除了所有湖畔大學的student。這就好比,你打算開除一個學生,結果把學校和學生的數據全刪沒了。是不是很刺激?

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>

環境:springboot2.1.7+jdk1.8+mysql8.0+druid1.1.10+Springdata JPA+Lombok

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/cauchy6317/article/details/100924045

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 男人和女人日比 | japanesepooping脱粪| 无限资源在线观看播放 | 国产成人精品高清免费 | 麻豆网站在线免费观看 | 69av免费视频 | 免费视频精品一区二区三区 | 91李宗精品72集在线观看 | 欧美人与禽杂交大片 | 校园高h| 亚洲国产在线视频中文字 | 成年人视频在线 | 2012在线观看免费视频大全 | 亚洲 欧美 偷自乱 图片 | 视频一区二区三区在线 | 男人猛进猛出女人下面视频 | 爱豆传媒最新视频国产 | 肉文高h调教| 亚洲国产午夜看片 | 欧美日韩一区二区三区在线观看 | 国产精品视频网 | 青青草99久久精品国产综合 | 国产成人综合手机在线播放 | 日本三不卡| 5x社区在线观看直接进入 | 日韩毛片在线视频 | 免费一级毛片在线播放放视频 | 女教师雪白老汉 | 8插8插| 国产欧美另类久久精品91 | 无码射肉在线播放视频 | 日韩欧美国产在线 | 貂蝉沦为姓奴小说 | 欧美成人手机 | 情侣宾馆愉拍自拍视频 | 91在线老王精品免费播放 | 网友自拍咪咪爱 | 亚洲天堂免费 | 99精品视频在线观看免费 | 亚洲色图网址 | 啊好痛嗯轻一点免费 |