jpa实现级联操作


README


## 说明

这是JPA实现级联操作的demo。

为了实现方便,就没有写service和impl层,直接写了dao层。(理解级联操作的思路就好)

### 数据库说明

在application.properties中配置您对应的数据库信息。

无需在mysql数据库设计表。运行该项目,则自动生成数据库表。

### 注意点

- 在被维护的一方,比如Survey,添加所有的问题,一定要添加  @ToString.Exclude。
否则报错:$HibernateProxy$Bo3T1LuZ.toString(Unknown Source) ~[classes/:na]

@OneToMany(mappedBy = "survey",cascade=CascadeType.ALL,orphanRemoval = true)
@ToString.Exclude
private List questions=new LinkedList<>();

### 关系说明

一个问卷有多个题目

一个题目有多个选项

### 级联删除

删除问卷,会把相关的问题和选项都删除。

### 级联更新

目前的思路实现是新增一个问题,把以前的无关问题全部删除。详情看TestController的test6.

如果你想更新部分问题,又不想把以前的无关问题删除。
1.这个想法的思路我暂时这么实现。
2.获取以前的问题。
3.使用survey.getQuestions().clear();删除全部的问题
4.将以前的问题进行修改,再重新生成问题

### 级联查询

根据问卷id,能够查询全部的问题和选项

### 级联标签

CascadeType.PRESIST 级联持久化(保存)操作(持久保存拥有方实体时,也会持久保存该实体的所有相关数据。)

CascadeType.REMOVE 级联删除操作(删除一个实体时,也会删除该实体的所有相关数据。)

CascadeType.MERGE 级联更新(合并)操作(将分离的实体重新合并到活动的持久性上下文时,也会合并该实体的所有相关数据。)

CascadeType.REFRESH 级联刷新操作 (只会查询获取操作)

CascadeType.ALL 包含以上全部级联操作

目录

entity

Survey问卷类

package com.lyr.demo.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.util.LinkedList;
import java.util.List;

@ApiModel("问卷")
@Entity
@Data
@Table(name = "t_survey")
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}) //防止转换json数据的时候出现无限制循环
public class Survey {
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;

    @Column
    private String title;

    /**
     * orphanRemoval=true配置表明删除无关联的数据。级联更新子结果集时此配置最关键
     */
    @OneToMany(mappedBy = "survey", cascade = CascadeType.ALL,orphanRemoval = true)
    @JsonBackReference
    private List questions = new LinkedList<>();

    /**
     * 手动添加问题
     */
    public void addQuestion(Question q) {
        if (null != q) {
            questions.add(q);
        }
    }

}

Question问题类

package com.lyr.demo.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * 针对Survey,Question是多的一方,因此在与Survey的关系中,属于维护关系的一方。Survey属于被维护关系的一方。
 *
 */

@ApiModel("问题")
@Entity
@Data
@Table(name = "t_question")
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
public class Question {

    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;

    @Column
    private String title;

    /**
     * (针对survey)建立外键 belong_survey_id.外键不能为空
     */
    @ManyToOne
    @JoinColumn(name = "belong_survey_id",nullable = false)
    @JsonManagedReference
    @ToString.Exclude
    private Survey survey;

    /**
     * (针对Option)
     * orphanRemoval=true配置表明删除无关联的数据。级联更新子结果集时此配置最关键
     */
    @OneToMany(mappedBy = "question",cascade=CascadeType.ALL,orphanRemoval = true)
    @JsonBackReference
    private List

Option选项类

package com.lyr.demo.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

@ApiModel("选项")
@Entity
@Data
@Table(name = "t_option")
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
public class Option {

    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;

    @Column
    private String title;

    /**
     * 建立外键quid.外键不能为空
     */
    @ManyToOne
    @JoinColumn(name = "quid",nullable = false)
    @JsonManagedReference
    @ToString.Exclude
    private Question question;

}

dao层

SurveyDao

package com.lyr.demo.dao;

import com.lyr.demo.entity.Survey;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface SurveyDao extends JpaRepository {
}

QuestionDao

package com.lyr.demo.dao;

import com.lyr.demo.entity.Question;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface QuestionDao extends JpaRepository {
}

OptionDao

package com.lyr.demo.dao;

import com.lyr.demo.entity.Option;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface OptionDao extends JpaRepository {
}

TestController

package com.lyr.demo.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lyr.demo.dao.OptionDao;
import com.lyr.demo.dao.QuestionDao;
import com.lyr.demo.dao.SurveyDao;
import com.lyr.demo.entity.Option;
import com.lyr.demo.entity.Question;
import com.lyr.demo.entity.Survey;
import com.lyr.demo.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
public class TestController {

    @Autowired
    private SurveyDao surveyDao;
    @Autowired
    private QuestionDao questionDao;
    @Autowired
    private OptionDao optionDao;

    /**
     * 级联保存。下面的操作是标准流程.
     *
     * 操作
     *  1.创建一张问卷。问卷有两个问题,每个问题分别有一个选项。
     *  2.对于单个问题:设置问题的标题,设置问卷属性
     *  3.对于单个选项:设置选项的标题,设置问题属性
     *  4.问题添加选型
     *  5.问卷添加问题
     *  6.保存问卷(不需要手动保存问题和问卷)
     */
    @GetMapping("/test1")
    public Result test1() {
        Result result = new Result();

        Survey survey = new Survey();
        survey.setTitle("s1");

        //新增一个问题1和选项,需要setSurvey()
        Question q1 = new Question();
        q1.setTitle("q1");
        q1.setSurvey(survey);
        //新增一个选项,需要setQuestion()
        Option op1 = new Option();
        op1.setTitle("op1");
        op1.setQuestion(q1);

        // 下面是同样的操作:新增问题2和选项
        Question q2 = new Question();
        q2.setTitle("q2");
        q2.setSurvey(survey);
        Option op2 = new Option();
        op2.setTitle("op2");
        op2.setQuestion(q2);

        //Question类需要主动把Option类加入自己的options属性
        q1.addOption(op1);
        q2.addOption(op2);

        //在执行surveyDao.save(survey)之前,Survey类需要主动把Question类加入自己的questions属性
        survey.addQuestion(q1);
        survey.addQuestion(q2);

        surveyDao.save(survey);

        return result;
    }

    /**
     * 级联删除.删除一个问卷,问卷下面的所有问题和所有选项都将会被删除。
     */
    @GetMapping("/test2")
    public Result test2() {
        Result result = new Result();
        //删除一个问卷(改为数据库对应的问卷id)
        surveyDao.deleteById("4028fe8179ef78b80179ef7968160000");
        return result;
    }

    /**
     * 级联删除。删除一个问题,问题下面的所有选项都会被删除,但是问题所属的问卷不会有任何操作。
     */
    @GetMapping("/test3")
    public Result test3() {
        Result result = new Result();
        //删除一个问题(改为数据库对应的问题id)
        questionDao.deleteById("4028fe8179ef65ce0179ef67eaa90001");
        return result;
    }

    /**
     * 级联删除。删除一个选项,对所属的问题没有影响,对所属的问卷也没有影响
     */
    @GetMapping("/test4")
    public Result test4() {
        Result result = new Result();
        //删除一个选项(改为数据库对应的选项id)
        optionDao.deleteById("4028fe8179ef7cc10179ef7dbdbd0002");
        return result;
    }

    /**
     * 级联查询。查询一个问卷,可以查到所有的选项和所有的问题。
     * 返回的是json数据
     */
     /**
     * 返回的测试数据
     * {
     *     "msg": "成功装载数据",
     *     "code": 0,
     *     "data": {
     *         "questions": [
     *             {
     *                 "options": [
     *                     {
     *                         "id": "4028fe8179ef7e470179ef91863a0002",
     *                         "title": "op1"
     *                     },
     *                     {
     *                         "id": "4028fe8179ef7e470179ef91863a0007",
     *                         "title": "op3"
     *                     }
     *                 ],
     *                 "id": "4028fe8179ef7e470179ef91863a0001",
     *                 "title": "q1"
     *             },
     *             {
     *                 "options": [
     *                     {
     *                         "id": "4028fe8179ef7e470179ef91863a0004",
     *                         "title": "op2"
     *                     },
     *                     {
     *                         "id": "4028fe8179ef7e470179ef91863a0008",
     *                         "title": "op4"
     *                     }
     *                 ],
     *                 "id": "4028fe8179ef7e470179ef91863a0003",
     *                 "title": "q2"
     *             }
     *         ],
     *         "id": "4028fe8179ef7e470179ef9186350000",
     *         "title": "s1"
     *     }
     * }
     *
     */
    @GetMapping("/test5")
    public Result test5() {
        Result result = new Result();
        Survey entity = surveyDao.getOne("4028fe8179ef7e470179ef9186350000");
        if (null != entity) {
            JSONObject all = new JSONObject(); //总的一个json数组,最后使用result.putData(all)
            all.put("id", entity.getId());
            all.put("title", entity.getTitle());
            List questions = entity.getQuestions();
            JSONArray arrQuesstions = new JSONArray(); // json数组,装进多个question
            int size = questions.size();
            if (0 != size) {
                //循环获取每个问题
                for (int i = 0; i < size; i++) {
                    JSONObject jsonQuestion = new JSONObject(); //一个问题,json形式
                    Question question = questions.get(i);
                    jsonQuestion.put("id", question.getId());
                    jsonQuestion.put("title", question.getTitle());

                    JSONArray arrOptions = new JSONArray(); //json数组,装进多个option
                    List

Result类(主要是controller层的返回标准实现,我自己自定义的)

package com.lyr.demo.utils;

import io.swagger.annotations.ApiModelProperty;
import java.util.HashMap;
import lombok.Data;

/**
 * @description 结果返回类
 * @author  lyr
 * @date  2021-05-07
 * 说明: 操作成功会返回0 ,操作会返回1
 */
@Data
public class Result extends HashMap {

    @ApiModelProperty(value = "0-成功 1-失败")
    private int code=0;
    @ApiModelProperty(value = "描述信息")
    private String msg="";
    @ApiModelProperty(value = "传递的数据")
    public Object data="";

    public Result(){
        super();
        this.put("code",code);
        this.put("msg",msg);
        this.put("data",data);
    }

    @Override
    public Result put(String key, Object value){
        super.put(key,value);
        return this;
    }

    public Result putData(Object data) {
        this.put("data", data);
        return this;
    }

    public Result putMsg(String msg) {
        this.put("msg", msg);
        return this;
    }

    public Result putCode(int code) {
        this.put("code", code);
        return this;
    }

}


运行类

package com.lyr.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@EnableSwagger2
@SpringBootApplication
public class RunApplication {
    public static void main(String[] args) {
        SpringApplication.run(RunApplication.class,args);
    }
}

application.properties


## port
server.port=8091

## 事务管理
spring.transaction.rollback-on-commit-failure=true

## local mysql
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/lyr?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

    4.0.0

    com.lyr
    jpa级联操作
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.6.RELEASE
         
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
        
        
        
        
        

        
        
            mysql
            mysql-connector-java
        

        
        
            org.springframework.boot
            spring-boot-starter-aop
        

        
        
            org.springframework.boot
            spring-boot-starter-data-jpa
            2.4.1
        

        
        
            org.projectlombok
            lombok
            true
        

        
        
            com.alibaba
            fastjson
            1.2.60
        

        
        
            org.springframework.boot
            spring-boot-starter-freemarker
        


        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        

        
            junit
            junit
            test
        

        
        
            io.springfox
            springfox-swagger2
            2.9.2
        
        
            io.springfox
            springfox-swagger-ui
            2.9.2
        
        
            com.github.caspar-chen
            swagger-ui-layer
            1.1.3
        

        
            org.apache.commons
            commons-lang3
            3.4
        

    

    
        
        QuestionnaireSystem
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
            
                maven-surefire-plugin
                
                    true
                
            
        
        
            
                src/main/java
                
                    **/*.xml
                    **/*.properties
                
                false
            
            
                src/main/resources
                
                    **/*.xml
                    **/*.properties
                    **/*.xls
                
                false
            
        
    


相关