JPA注解解决Hibernate的N+1问题

JPA注解解决Hibernate的N+1问题

数据准备

映射关系

一个班级对应多个学生 一对多
一个学生对应多个角色 一对多
一个学生对应多项权限,一个权限对应多个学生(例如收作业,语文课代表可以,数学课代表也可以) 多对多

数据实体类

/**
 * 班级实体类
 */
@Data
@Entity
@Table(name = "class")
public class StudentClass {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String className;

    @OneToMany(mappedBy = "studentClass", cascade = {CascadeType.ALL}, orphanRemoval = true)
    @OrderBy("id asc")
    private List<Student> students;

}
/**
 * 学生实体类
 */
@Entity
@Table(name = "student")
@Data
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    private Integer age;

    @ManyToOne
    @JoinColumn(name = "class_id")
    @JsonIgnore
    private StudentClass studentClass;

    @OneToMany(mappedBy = "student", cascade = {CascadeType.ALL})
    private List<Role> roles = new ArrayList<>();


    @ManyToMany
    @JoinTable(
            name = "student_permission",
            joinColumns = @JoinColumn(name = "student_id"),
            inverseJoinColumns = @JoinColumn(name = "permission_id")
    )
    private List<Permission> permissions = new ArrayList<>();
}
/**
 * 角色实体类
 */
@Data
@Entity
@Table(name = "role")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String roleName;

    @ManyToOne
    @JoinColumn(name = "student_id")
    @JsonIgnore
    private Student student;
}
/**
 * 权限实体类
 */
@Data
@Entity
@Table(name = "perminssion")
public class Permission {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    private Integer sort;
}

测试数据

{
    "id": 3,
    "className": "一班",
    "students": [
        {
            "id": 11,
            "name": "张三",
            "age": 18,
            "roles": [
                {
                    "id": 8,
                    "roleName": "管理员"
                },
                {
                    "id": 9,
                    "roleName": "技术员"
                },
                {
                    "id": 11,
                    "roleName": "1"
                }
            ],
            "permissions": [
                {
                    "id": 1,
                    "name": "发作业",
                    "sort": 2
                },
                {
                    "id": 2,
                    "name": "收作业",
                    "sort": 1
                }
            ]
        },
        {
            "id": 12,
            "name": "李四",
            "age": 18,
            "roles": [
                {
                    "id": 10,
                    "roleName": "岗位员"
                }
            ],
            "permissions": [
                {
                    "id": 3,
                    "name": "测试",
                    "sort": 1
                },
                {
                    "id": 4,
                    "name": "111",
                    "sort": 2
                }
            ]
        },
        {
            "id": 13,
            "name": "王五",
            "age": 18,
            "roles": [],
            "permissions": []
        }
    ]
}

N+1问题复现

  1. 调用查询方法
classRepository.findById(id).orElseGet(null);
  1. 查询结果
    n+1
    一个班有三个学生,每个学生都单独去查询了一次role与permission表,共8条sql语句

N+1解决办法

结合JPA使用@NamedEntityGraph注解

  1. 在StudentClass上面添加对应注解
@NamedEntityGraph(name = "calss.one", attributeNodes = {
        @NamedAttributeNode(value = "students", subgraph = "students")
}, subgraphs = {
        @NamedSubgraph(name = "students", attributeNodes = {
                @NamedAttributeNode(value = "roles", subgraph = "roles"),
                @NamedAttributeNode(value = "permissions", subgraph = "permissions")
        })
})
  1. 在Repository重写findById方法
    @Override
    @EntityGraph(value="calss.one", type = EntityGraph.EntityGraphType.FETCH)
    Optional<StudentClass> findById(Integer id);
  1. 调用
classRepository.findById(id).orElseGet(null);
  1. 查询结果
    n+1
    只有一条sql
  2. 存在问题
    n+1
    n+1
    使用了左外连接数据重复
  3. 数据重复问题解决
    将实体类中的List改为Set接收
    在这里插入图片描述
posted @ 2020-11-11 18:00  夏天hipo  阅读(272)  评论(0编辑  收藏  举报