• 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<Question> 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<Question> 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> options=new LinkedList<>();
    
        /** 手动添加选项 */
        public void addOption(Option option){
            if(null!=option){
                options.add(option);
            }
        }
    
    }
    
    

    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<Survey,String> {
    }
    
    

    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<Question,String> {
    }
    

    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<Option,String> {
    }
    

    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<Question> 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<Option> options = question.getOptions();
                        int opSize = options.size();
                        //循环获取某个问题的选项
                        if (0 != opSize) {
                            for (int j = 0; j < opSize; j++) {
                                JSONObject jsonOp = new JSONObject();
                                Option option = options.get(j);
                                jsonOp.put("id", option.getId());
                                jsonOp.put("title", option.getTitle());
                                arrOptions.add(j, jsonOp);
                            }
                            jsonQuestion.put("options", arrOptions);
                        }
                        arrQuesstions.add(i, jsonQuestion);
                    }
                    all.put("questions", arrQuesstions);
                    return result.putCode(0).putMsg("成功装载数据").putData(all);
                }
            }
            return result;
        }
    
        /**
         * 级联更新.
         *
         * 目前需要操作的是:可能问卷需要大改。有删除,也有新增。
         *
         * 说明
         *  1.关键: survey.getQuestions().clear(); 如果缺少了这个,则不能删除无关的数据
         *  2.注解@OneToMany(mappedBy = "survey", cascade = CascadeType.ALL,orphanRemoval = true)
         *
         * 操作步骤(新增一个问题,把以前的无关问题清空)
         *  1.拿到一个问卷
         *  2.把该问卷后面的所有问题清空,也就是clear()
         *  3.添加新问题,设置问题的属性,问题添加问卷的属性
         *  4.问卷添加问题
         *  5.保存问卷
         */
        @GetMapping("/test6")
        public Result test6() {
            Result result = new Result();
    
            Survey survey = surveyDao.getOne("4028fe8179f396280179f39de6c20006");
            survey.getQuestions().clear();
    
            Question question=new Question();
            question.setTitle("新的问题");
            question.setSurvey(survey);
            survey.addQuestion(question);
    
            surveyDao.save(survey);
    
            return result;
        }
    
        @GetMapping("/test7")
        public Result test7() {
            Result result = new Result();
            return result;
        }
    
        @GetMapping("/test8")
        public Result test8() {
            Result result = new Result();
            return result;
        }
    
    }
    
    

    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<String, Object> {
    
        @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"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.lyr</groupId>
        <artifactId>jpa级联操作</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.6.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!--本地运行时要注释掉-->
            <!--<dependency>-->
            <!--<groupId>org.springframework.boot</groupId>-->
            <!--<artifactId>spring-boot-starter-tomcat</artifactId>-->
            <!--<scope>provided</scope>-->
            <!--</dependency>-->
            <!--本地运行时要注释掉-->
    
            <!--数据库驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <!--aop 做日志管理-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    
            <!--jpa-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
                <version>2.4.1</version>
            </dependency>
    
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <!-- Fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.60</version>
            </dependency>
    
            <!--freemarker-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-freemarker</artifactId>
            </dependency>
    
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!--引入swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>com.github.caspar-chen</groupId>
                <artifactId>swagger-ui-layer</artifactId>
                <version>1.1.3</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.4</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <!--项目名称-->
            <finalName>QuestionnaireSystem</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <skipTests>true</skipTests>
                    </configuration>
                </plugin>
            </plugins>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                        <include>**/*.properties</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.xml</include>
                        <include>**/*.properties</include>
                        <include>**/*.xls</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
            </resources>
        </build>
    </project>
    
    
  • 相关阅读:
    图像切割之(五)活动轮廓模型之Snake模型简单介绍
    拓扑排序的原理及事实上现
    理解class.forName()
    Java中StringBuilder的清空方法比較
    Java实现BASE64编解码
    KMP算法具体解释(贴链接)
    基于PCM2912a的USB声卡设计
    51. 腾讯面试题:一个二叉树,中序遍历,找一个节点的后一个节点
    Handler和HandlerThread
    Nuget-QRCode:jquery-qrcode
  • 原文地址:https://www.cnblogs.com/YuRong3333/p/14870245.html
Copyright © 2020-2023  润新知