首页 > 其他分享 >PA 懒加载(循环引用,N+1,使用关联对象)(二)

PA 懒加载(循环引用,N+1,使用关联对象)(二)

时间:2023-04-03 17:35:45浏览次数:55  
标签:school name 关联 public PA import users id 加载

这次具体讲述一下,对于懒加载遇到(循环引用,N+1,使用关联对象)的解决方案。

为了方便大家模拟操作,我会完整说一下

不想看过程的,直接看总结。

 

一 建表

创建School和User

School

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
 
-- ----------------------------
-- Table structure for school
-- ----------------------------
DROP TABLE IF EXISTS `school`;
CREATE TABLE `school`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
 
-- ----------------------------
-- Records of school
-- ----------------------------
INSERT INTO `school` VALUES (1, 'h1');
INSERT INTO `school` VALUES (2, 'h2');
INSERT INTO `school` VALUES (3, 't3');
INSERT INTO `school` VALUES (4, 'h4');
 
SET FOREIGN_KEY_CHECKS = 1;

 

User

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
 
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `schoolId` int NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `FK3o0riaw95im7i0xlbrwujumpa`(`schoolId` ASC) USING BTREE,
  CONSTRAINT `FK3o0riaw95im7i0xlbrwujumpa` FOREIGN KEY (`schoolId`) REFERENCES `school` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
 
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'u1', 1);
INSERT INTO `user` VALUES (2, 'u2', 1);
INSERT INTO `user` VALUES (3, 'u3', 2);
INSERT INTO `user` VALUES (4, 'u4', NULL);
 
SET FOREIGN_KEY_CHECKS = 1;

 

二 POM

简单说一下:

1 jackson-datatype-hibernate5  懒加载数据,转换json时,避免错误。

2 其他不赘述了。

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-hibernate5</artifactId>
            <version>2.14.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
 
    </dependencies>

 

三 application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test1?serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true

 

四 配置注入

通过jackson-datatype-hibernate5配置,解决懒加载序列化的问题。

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 
import java.text.SimpleDateFormat;
 
@Configuration
public class WebMvcConfig {
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper mapper = converter.getObjectMapper();
        //JPA 懒加载
        Hibernate5Module hibernate5Module = new Hibernate5Module();
        mapper.registerModule(hibernate5Module);
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        return converter;
    }
 
}

 

五 对象

School对象

@Fetch(FetchMode.SUBSELECT)   解决N+1问题。

关联表产生的sql变为:select * from XXX where id in (....)

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.GenericGenerator;
 
import javax.persistence.*;
import java.util.List;
 
@Entity
@Table(name = "school", catalog = "test1")
public class School implements java.io.Serializable {
 
    private Integer id;
    private String name;
 
    private List<User> users;
 
    @Id
    @GenericGenerator(name = "generator", strategy = "identity")
    @Column(name = "id")
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    @Column(name = "name")
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
 
    @Fetch(FetchMode.SUBSELECT)
    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "schoolId", referencedColumnName = "id", insertable = false, updatable = false)
    public List<User> getUsers() {
        return users;
    }
 
    public void setUsers(List<User> users) {
        this.users = users;
    }
}

 

User对象

@JsonIgnoreProperties(value = { "users" })    防止循环引用,指向School表中的users对象

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.hibernate.annotations.GenericGenerator;
 
import javax.persistence.*;
 
@Entity
@Table(name = "user",catalog = "test1")
public class User {
    private Integer id;
    private String name;
    private Integer schoolId;
 
    private School school;
 
    @Id
    @GenericGenerator(name = "generator", strategy = "identity")
    @Column(name = "id")
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    @Column(name = "name")
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Column(name = "schoolId")
    public Integer getSchoolId() {
        return schoolId;
    }
 
    public void setSchoolId(Integer schoolId) {
        this.schoolId = schoolId;
    }
 
    @JsonIgnoreProperties(value = { "users" })
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="schoolId",referencedColumnName = "id",insertable = false,updatable = false)
    public School getSchool() {
        return school;
    }
 
    public void setSchool(School school) {
        this.school = school;
    }
}

 

六 Dao

SchoolDao

import com.example.test_project.model.School;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
@Repository
public interface SchollDao extends JpaRepository<School,Integer> {
}

UserDao

import com.example.test_project.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
@Repository
public interface UserDao extends JpaRepository<User,Integer> {
}

 

七 Controller

package com.example.test_project.controller;
 
import com.example.test_project.dao.SchoolDao;
import com.example.test_project.dao.UserDao;
import com.example.test_project.model.School;
import com.example.test_project.model.User;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
 
import java.util.List;
import java.util.Optional;
 
@RestController
@RequestMapping("api/test")
public class TestController {
    @Autowired
    SchoolDao schoolDao;
    @Autowired
    UserDao userDao;
 
 
    /**
     * School -- findAll users为null
     * @param reqVo
     * @return
     */
    @RequestMapping(value = "t1", method = RequestMethod.POST)
    @ResponseBody
    public List<School> t1(@RequestBody String reqVo) {
        List<School> schollList=schoolDao.findAll();
        return schollList;
    }
 
    /**
     * School -- findAll,通过遍历调用,users有值
     * @param reqVo
     * @return
     */
    @RequestMapping(value = "t2", method = RequestMethod.POST)
    @ResponseBody
    public List<School> t2(@RequestBody String reqVo) {
        List<School> schollList=schoolDao.findAll();
        schollList.forEach(x->Optional.ofNullable(x.getUsers()).toString());
        return schollList;
    }
 
    /**
     * School -- findOne users为null
     * @param reqVo
     * @return
     */
    @RequestMapping(value = "t3", method = RequestMethod.POST)
    @ResponseBody
    public School t3(@RequestBody String reqVo) {
        School scholl=schoolDao.findById(1).get();
        return scholl;
    }
 
    /**
     * School -- findOne,通过遍历调用,users有值
     * @param reqVo
     * @return
     */
    @RequestMapping(value = "t4", method = RequestMethod.POST)
    @ResponseBody
    public School t4(@RequestBody String reqVo) {
        School scholl=schoolDao.findById(1).get();
        Optional.ofNullable(scholl.getUsers()).toString();
        return scholl;
    }
 
    /**
     * User -- findAll 通过遍历 school有值
     * @return
     */
    @RequestMapping(value = "t5", method = RequestMethod.POST)
    @ResponseBody
    public List<User> t5() {
        List<User> list=userDao.findAll();
        list.forEach(x-> Optional.ofNullable(x.getSchool()).toString());
        return list;
    }
 
    /**
     * User -- findOne school为null
     * @return
     */
    @RequestMapping(value = "t6", method = RequestMethod.POST)
    @ResponseBody
    public User t6() {
        User user=userDao.findById(1).get();
        return user;
    }
 
 
}

 

八 测试

1 api/test/t1

没有调用users,所以users为null

[
    {
        "id": 1,
        "name": "h1",
        "users": null
    },
    {
        "id": 2,
        "name": "h2",
        "users": null
    },
    {
        "id": 3,
        "name": "t3",
        "users": null
    },
    {
        "id": 4,
        "name": "h4",
        "users": null
    }
]

2 api/test/t2

调用了schollList.forEach(x->Optional.ofNullable(x.getUsers()).toString());

所以users有数据

[
    {
        "id": 1,
        "name": "h1",
        "users": [
            {
                "id": 1,
                "name": "u1",
                "schoolId": 1,
                "school": {
                    "id": 1,
                    "name": "h1"
                }
            },
            {
                "id": 2,
                "name": "u2",
                "schoolId": 1,
                "school": {
                    "id": 1,
                    "name": "h1"
                }
            }
        ]
    },
    {
        "id": 2,
        "name": "h2",
        "users": [
            {
                "id": 3,
                "name": "u3",
                "schoolId": 2,
                "school": {
                    "id": 2,
                    "name": "h2"
                }
            }
        ]
    },
    {
        "id": 3,
        "name": "t3",
        "users": []
    },
    {
        "id": 4,
        "name": "h4",
        "users": []
    }
]

3 api/test/t3

没有调用users

{
    "id": 1,
    "name": "h1",
    "users": null
}

4 api/test/t4

调用了Users    Optional.ofNullable(scholl.getUsers()).toString();

{
    "id": 1,
    "name": "h1",
    "users": [
        {
            "id": 1,
            "name": "u1",
            "schoolId": 1,
            "school": {
                "id": 1,
                "name": "h1"
            }
        },
        {
            "id": 2,
            "name": "u2",
            "schoolId": 1,
            "school": {
                "id": 1,
                "name": "h1"
            }
        }
    ]
}

5 api/test/t5

调用了school  list.forEach(x-> Optional.ofNullable(x.getSchool()).toString());

[
    {
        "id": 1,
        "name": "u1",
        "schoolId": 1,
        "school": {
            "id": 1,
            "name": "h1"
        }
    },
    {
        "id": 2,
        "name": "u2",
        "schoolId": 1,
        "school": {
            "id": 1,
            "name": "h1"
        }
    },
    {
        "id": 3,
        "name": "u3",
        "schoolId": 2,
        "school": {
            "id": 2,
            "name": "h2"
        }
    },
    {
        "id": 4,
        "name": "u4",
        "schoolId": null,
        "school": null
    }
]

6 api/test/t6

没有调用School

{
    "id": 1,
    "name": "u1",
    "schoolId": 1,
    "school": null
}

 

总结

可以看到,满足了Lazy (循环引用,N+1,使用关联对象)的功能。

1 使用jackson-datatype-hibernate5 配置 WebMvcConfig 解决懒加载的序列化问题。

2 使用@Fetch(FetchMode.SUBSELECT) 解决N+1问题

3 使用@JsonIgnoreProperties(value = { "users" }) 避免循环引用
4 使用Optional.ofNullable(xxx).toString();  是为了避免 null.toString();

 

标签:school,name,关联,public,PA,import,users,id,加载
From: https://www.cnblogs.com/hanjun0612/p/17283742.html

相关文章

  • Apache JMeter压力测试工具的安装与使用
    官网下载https://jmeter.apache.org/download_jmeter.cgi然后解压即可运行双击bin/jmeter.bat汉化在软件里选择语言重启就会还原,所以这里直接改配置文件来永久汉化打开bin/jmeter.properties找到#language行,大概在第39行,改成language=zh_CN保存后重启jmeter即可使用教......
  • Parallels Desktop 18.2.0提示“由于临界误差,不能启动虚拟机”怎么办
    ParallelsDesktop最近更新到了18.2.0版本,但是许多小伙伴更新之后遇到了“由于临界误差,不能启动虚拟机”这样的问题,接下来小编就为大家带来解决PD虚拟机提示临界误差的解决方法。出现这个问题很有可能是Prl_disp_service卡住导致的。所以我们应该先退出ParallelsDesktop。然......
  • THM-密码攻击(Password Attacks)
    密码攻击技术密码攻击技术在这个房间里,我们将讨论可用于执行密码攻击的技术。我们将介绍各种技术,例如字典、蛮力、规则库和猜测攻击。上述所有技术都被视为主动“在线”攻击,其中攻击者需要与目标机器通信以获取密码,以便获得对机器的未授权访问。密码破解与密码猜测本节从网......
  • myBatis报错org.apache.ibatis.ognl.NoSuchPropertyException
    跑批任务时mybatis报错org.apache.ibatis.ognl.NoSuchPropertyException,重跑未出现报错,百度发现是由于mybatis依赖的Ognl版本OgnlRuntime.getMethodValue在并发情况下会存在并发问题,错误地返回null引起报错 以下是搜索该问题时找到的资料:https://github.com/1993hzh/tho......
  • dxg:GridControl 单元格关联其他单元格的颜色设定
    列样式:<dxg:GridColumn.CellStyle><StyleTargetType="{x:Typedxg:LightweightCellEditor}"><Style.Triggers><DataTriggerValue="T......
  • 加载更多 - 监听div的滚动scroll
    前言:某些情况下,在展示列表数据时,为了实现性能优化及用户更好的体验,可以先展示十几条数据,然后边滑动边加载更多,可以减少服务器压力及页面渲染时间。varpageNum=1;//页数vardomHeight=$(".listBox").height()*4;vardom=document.getElementById('list');dom.addEventList......
  • PaddleOCR服务部署-并通过Java进行调用
    文章转载自: https://blog.csdn.net/f2315895270/article/details/128150679选择部署方式  官方推荐有以下几种:  Python推理  C++推理  Serving服务化部署(Python/C++)  Paddle-Lite端侧部署(ARM CPU/OpenCLARMGPU)  Paddle.js部署     由于我......
  • @Transactional(propagation=Propagation.REQUIRED)数据库事务的7种传播行为特性
      ClassA{@Transactional(propagation=propagation.REQUIRED)publicvoidaMethod{Bb=newB();b.bMethod();}}//inB.javaClassB{@Transactional(propagation=propagation.REQUIRED)publicvoidbMethod......
  • PaddleOCR 安装与简单使用(windows)
    文章转载自: https://blog.csdn.net/f2315895270/article/details/128147744前提    已经安装好Python环境   PaddleOCR官方主页:https://www.paddlepaddle.org.cn/   GitHub地址:https://github.com/PaddlePaddle/PaddleOCR   Gitee地址:https://gitee......
  • Spark面试经典系列之数据倾斜解决方案的“银弹”是什么? 本节我们对Spark数据倾斜解决
    Spark面试经典系列之数据倾斜解决方案的“银弹”是什么?本节我们对Spark数据倾斜解决方案进行回顾和总结:1、   数据倾斜运行的症状和危害。如果发行数据倾斜,往往发现作业任务运行特别缓慢,出现OOM内存溢出等现象。2、   如果两个RDD进行操作,其中1个RDD数据不是那么多,我们把这......