1. 程式人生 > >[增刪改查] 最規範的 JPA 多對多 CURD 示例

[增刪改查] 最規範的 JPA 多對多 CURD 示例

一、前言

資料庫中表多對多的關係也是很常見的,如一個許可權系統,設定的足夠嚴密,就是 許可權(選單)與角色多對多關係,角色與使用者多對多關係。

二、程式碼

1、程式碼結構

這裡寫圖片描述

省略了 Service 層

2、entity

① 學生

package com.cun.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import
javax.persistence.Table; @Entity @Table(name = "t_student") public class Student { @Id @GeneratedValue private Integer id; @Column(length = 100) private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public
String getName() { return name; } public void setName(String name) { this.name = name; } public Student() { super(); } }

② 教師

package com.cun.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import
javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "t_teacher") public class Teacher { @Id @GeneratedValue private Integer id; @Column(length = 100) private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Teacher() { super(); } }

③ 學生教師關係

package com.cun.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "t_st")
public class StudentTeacher {

    @Id
    @GeneratedValue
    private Integer id;

    @ManyToOne
    @JoinColumn(name="student_id")
    private Student student;

    @ManyToOne
    @JoinColumn(name="teacher_id")
    private Teacher teacher;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

}

3、dao

① 學生

package com.cun.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import com.cun.entity.Student;

public interface StudentDao extends JpaRepository<Student, Integer>{

}
② 教師
package com.cun.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import com.cun.entity.Teacher;

public interface TeacherDao extends JpaRepository<Teacher, Integer>{

}

③ 學生教師關係

package com.cun.dao;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import com.cun.entity.StudentTeacher;

public interface StudentTeacherDao extends JpaRepository<StudentTeacher, Integer> {

    /**
     * 1.1、學生關聯查詢
     * @param id
     * @return
     */
    @Query(value = "select * from t_st where student_id=?1", nativeQuery = true)
    List<StudentTeacher> getStudentTeacherByStudentId(Integer id);

    /**
     * 1.2、教師關聯查詢
     * @param id
     * @return
     */
    @Query(value = "select * from t_st where teacher_id=?1", nativeQuery = true)
    List<StudentTeacher> getStudentTeacherByTeacherId(Integer id);


    /**
     * 2.1、通過教師 id 刪除師生關係
     * ① 在 dao 層中加上 @Modifying
     * ② 注意新增 @Transactional,否則 TransactionRequiredException
     * ③ @Transactional 建議還是在 Service 層中加上,不要在 Controller 層中
     */
    @Modifying
    @Query(value="delete from t_st where teacher_id=?1",nativeQuery=true)
    void deleteConnectionByTeacherId(Integer teacherId);

    /**
     * 2.2、通過學生 id 刪除師生關係
     * ① 在 dao 層中加上 @Modifying,否則 SQLException
     * ② 注意新增 @Transactional,否則 TransactionRequiredException
     * ③ @Transactional 建議還是在 Service 層中加上,不要在 Controller 層中
     */
    @Modifying
    @Query(value="delete from t_st where student_id=?1",nativeQuery=true)
    void deleteConnectionByStudentId(Integer studentId);

}

4、Controller

① 學生

package com.cun.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cun.dao.StudentDao;
import com.cun.dao.StudentTeacherDao;
import com.cun.entity.Student;
import com.cun.entity.StudentTeacher;

import springfox.documentation.swagger2.annotations.EnableSwagger2;

@RestController
@RequestMapping("/student")
@EnableSwagger2
@Transactional // 自定義update、delete、insert 的 Dao 介面必備
public class StudentController {

    @Autowired
    private StudentDao studentDao;

    @Autowired
    private StudentTeacherDao studentTeacherDao;

    /**
     * 1、查
     * ① 這個查的返回值應該是一個 List 集合
     * ② 一個學生可以有多個教師
     * ③ 使用關係表的 JPA 查詢來完成
     * @param id
     * @return
     */
    @GetMapping("/get/{id}")
    public List<StudentTeacher> getStudent(@PathVariable Integer id) {
        // return studentDao.findOne(id); //不符合實際需求的
        return studentTeacherDao.getStudentTeacherByStudentId(id);
    }

    /**
     * 2、增
     * @param student
     * @return
     */
    @PostMapping("insert")
    public Student insertStudent(Student student) {
        studentDao.save(student);

        // 儲存後的 student 有 id
        return student;
    }

    /**
     * 3、改
     * @param student
     * @return
     */
    @PutMapping("/update")
    public Student updateStudent(Student student) {
        studentDao.save(student);
        return student;
    }

    /**
     * 4、刪:
     * ① 刪除前要判斷是否存在關聯關係,不同於一對多/多對一
     * ② 多對要先把關係刪除掉,否則 ConstraintViolationException!
     * ③ 再把要刪除的刪掉,不會手下留情了!
     * ④ 注意新增 @Transactional,建議還是在 Service 層中加上,不要在 Controller 層中
     * @param id
     * @return
     */
    @DeleteMapping("/delete/{id}")
    public Student deleteStudent(@PathVariable Integer id) {
        Map<String, Object> map = new HashMap<String, Object>();
        Student student = studentDao.findOne(id);
        map.put("data", student);

        //先斷絕來外
        studentTeacherDao.deleteConnectionByStudentId(id);

        //再剷除
        studentDao.delete(id);
        return student;
    }

    /**
     * 5、全
     * @return
     */
    @GetMapping("/all")
    public List<Student> getAllStudents() {
        return studentDao.findAll();
    }

}

② 教師

package com.cun.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cun.dao.StudentTeacherDao;
import com.cun.dao.TeacherDao;
import com.cun.entity.StudentTeacher;
import com.cun.entity.Teacher;

import springfox.documentation.swagger2.annotations.EnableSwagger2;

@RestController
@RequestMapping("/teacher")
@EnableSwagger2
@Transactional // 自定義update、delete、insert 的 Dao 介面必備
public class TeacherController {

    @Autowired
    private TeacherDao teacherDao;

    @Autowired
    private StudentTeacherDao studentTeacherDao;

    /**
     * 1、查
     * ① 這個查應該應該是一個 List 集合
     * ② 一個教師可以有多個學生
     * ③ 使用關係表的 JPA 查詢來完成
     * @param id
     * @return
     */
    @GetMapping("/get/{id}")
    public List<StudentTeacher> getTeacherById(@PathVariable Integer id) {
        // return teacherDao.findOne(id); //不符合實際需求的
        return studentTeacherDao.getStudentTeacherByTeacherId(id);
    }

    /**
     * 2、刪:
     * ① 刪除前要判斷是否存在關聯關係,不同於一對多/多對一
     * ② 多對要先把關係刪除掉,否則 ConstraintViolationException!
     * ③ 再把要刪除的刪掉,不會手下留情了!
     * ④ 注意新增 @Transactional,建議還是在 Service 層中加上,不要在 Controller 層中
     * @param id
     * @return
     */
    @DeleteMapping("/delete/{id}")
    public Map<String, Object> deleteTeacherById(@PathVariable Integer id) {
        Map<String, Object> map = new HashMap<String, Object>();
        Teacher teacher = teacherDao.findOne(id);
        map.put("data", teacher);

        //先斷絕來外
        studentTeacherDao.deleteConnectionByTeacherId(id);

        //再剷除
        teacherDao.delete(id);
        return map;
    }

    /**
     * 3、改
     * @param teacher
     * @return
     */
    @PutMapping("/update")
    public Teacher updateTeacher(Teacher teacher) {
        teacherDao.save(teacher);
        return teacher;
    }

    /**
     * 4、增
     * @param teacher
     * @return
     */
    @PostMapping("/insert")
    public Teacher insertTeacher(Teacher teacher) {
        teacherDao.save(teacher);
        // save 後 teacher 有 id 了
        return teacher;
    }

    /**
     * 5、全
     * @return
     */
    @GetMapping("/all")
    public List<Teacher> getAllTeachers() {
        return teacherDao.findAll();
    }

}

5、pom

加入 JPA、MySQL、Web

6、yml

server:
  port: 80 #為了以後訪問專案不用寫埠號
  context-path: / #為了以後訪問專案不用寫專案名
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8  #解決中文亂碼的問題
    username: root
    password: 123
  jpa: 
    hibernate:
      ddl-auto: update  #資料庫同步程式碼
    show-sql: true      #dao操作時,顯示sql語句

三、其他

1、Swagger 介面

這裡寫圖片描述

2、JPA 自動生成的資料庫表的關係

這裡寫圖片描述

3、測試

 學生查詢:輸入 id=1

①、輸入資料

這裡寫圖片描述

②、輸出結果

這裡寫圖片描述

四、小結

易錯點

1、使用 @Query 更新刪除時,同時要使用 @Modifying@Transaction