目录
- 场景
- 数据
- @Where
- Entity:
- Repository
- Service
- Controller
- 测试
- @Filter
- Entity
- Repository
- 测试
场景
一个老师(Teacher)有很多个学生(Student)和有很多课本(book),一个学生有很多选修课程(Course)。查询老师列表的时候会把老师名下的学生和课本罗列出来,但是不需要展示被删除掉或者状态不对的数据。Teacher和Student通过关键字关联,Teacher和Book通过中间关联表关联,Student和Course通过关键字关联。
数据
Teacher:
1 2 3 4 5 | uuid ------ t1 t2 (2 rows) |
Student:
1 2 3 4 | uuid | deleted | status | teacher ------+---------+----------+--------- s1 | | active | t1 s2 | deleted | inactive | t1 |
Course:
1 2 3 4 5 | uuid | deleted | status | student ------+---------+----------+--------- c1 | | active | s1 c2 | deleted | inactive | s1 (2 rows) |
Book:
1 2 3 4 5 | uuid | deleted | status ------+---------+---------- b1 | | active b2 | deleted | inactive (2 rows) |
teacher_book
1 2 3 4 5 | teacher_id | book_id ------------+--------- t1 | b1 t1 | b2 (2 rows) |
@Where
利用@Where注解过滤掉软删的学生、书、课程。
Entity:
Teacher:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Data @Entity @Table(name = "teacher") @EqualsAndHashCode(of = {"id"}) public class Teacher { @Column(name = "uuid") @Id private String id; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "teacher_book", joinColumns = { @JoinColumn(name = "teacher_id") }, inverseJoinColumns = { @JoinColumn(name = "book_id") }) @Where(clause = " deleted is null ") private Set<Book> books; @OneToMany(mappedBy = "teacher", fetch = FetchType.LAZY) @Where(clause = " deleted is null ") private Set<Student> students; } |
Student:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | @Data @Entity @Table(name = "student") @EqualsAndHashCode(of = {"id"}) public class Student { @Column(name = "uuid") @Id private String id; @Column(name = "deleted") private String deleted; @Column(name = "status") private String status; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "teacher") @JsonIgnore private Teacher teacher; @OneToMany(mappedBy = "student", fetch = FetchType.LAZY) @Where(clause = " deleted is null ") private Set<Course> courses; } |
Course:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Data @Entity @Table(name = "course") @EqualsAndHashCode(of = {"id"}) public class Course { @Column(name = "uuid") @Id private String id; @Column(name = "deleted") private String deleted; @Column(name = "status") private String status; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "student") @JsonIgnore private Student student; } |
Book:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Data @Entity @Table(name = "book") @EqualsAndHashCode(of = {"id"}) public class Book { @Column(name = "uuid") @Id private String id; @Column(name = "status") private String status; @Column(name = "deleted") private String deleted; } |
Repository
1 2 3 4 5 6 7 8 9 | @Repository public interface TeacherRepository extends CrudRepository<Teacher, String>{ @Query("select t from Teacher t " + "left join fetch t.students s " + "left join fetch s.courses c " + "left join fetch t.books b") public Set<Teacher> findTeachersU(); } |
Service
1 2 3 4 5 6 7 8 9 10 | @Service public class TeacherService { @Autowired TeacherRepository teacherRepository; public Set<Teacher> findTeachers(){ return teacherRepository.findTeachersU(); } } |
Controller
1 2 3 4 5 6 7 8 9 10 11 | @RestController public class TeacherController { @Autowired TeacherService teacherService; @GetMapping("/teachers") public Set<Teacher> findTeachers() String status){ return teacherService.findTeachers(); } } |
测试
发送/teachers可得到以下结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | [ { "id": "t1", "books": [ { "id": "b1", "status": "active", "deleted": null }, { "id": "b2", "status": "inactive", "deleted": "deleted" } ], "students": [ { "id": "s1", "deleted": null, "status": "active", "courses": [ { "id": "c1", "deleted": null, "status": "active" } ] } ] }, { "id": "t2", "books": [], "students": [] } ] |
从结果可以看出软删掉的student和student名下的course已经被过滤掉了,而teacher名下的book并没有过滤掉。所以有中间表关联的子集使用@Where无法达到过滤的作用。
1 2 3 4 5 6 7 | Hibernate: select teacher0_.uuid as uuid1_25_0_, students1_.uuid as uuid1_24_1_, courses2_.uuid as uuid1_5_2_, book4_.uuid as uuid1_3_3_, students1_.deleted as deleted2_24_1_, students1_.status as status3_24_1_, students1_.teacher as teacher4_24_1_, students1_.teacher as teacher4_24_0__, students1_.uuid as uuid1_24_0__, courses2_.deleted as deleted2_5_2_, courses2_.status as status3_5_2_, courses2_.student as student4_5_2_, courses2_.student as student4_5_1__, courses2_.uuid as uuid1_5_1__, book4_.deleted as deleted2_3_3_, book4_.status as status3_3_3_, books3_.teacher_id as teacher_1_26_2__, books3_.book_id as book_id2_26_2__ from teacher teacher0_ left outer join student students1_ on teacher0_.uuid=students1_.teacher and ( students1_.deleted is null ) left outer join course courses2_ on students1_.uuid=courses2_.student and ( courses2_.deleted is null ) left outer join teacher_book books3_ on teacher0_.uuid=books3_.teacher_id left outer join book book4_ on books3_.book_id=book4_.uuid and ( book4_.deleted is null ) Hibernate: select book0_.uuid as uuid1_3_0_, book0_.deleted as deleted2_3_0_, book0_.status as status3_3_0_ from book book0_ where book0_.uuid=? |
从sql语句中可以看到Hibernate将@Where的条件添加到了left join 的on条件而进行了过滤。
利用@Where进行的过滤是静态的,如果是变量的话就得利用@Filter了。
@Filter
利用@FilterDef定义变量的名称
@FilterDef(name = “过滤器的名字”, parameters = {@ParamDef(name=“sql中变量的名字”,type=“变量类型”)})
在需要过滤的字段添加@Filter进行过滤
@Filter(name = “过滤器的名字”, condition = " 过滤器中定义的变量=数据库表的字段 ")
Entity
Teacher
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Data @Entity @Table(name = "teacher") @EqualsAndHashCode(of = {"id"}) public class Teacher { @Column(name = "uuid") @Id private String id; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "teacher_book", joinColumns = { @JoinColumn(name = "teacher_id") }, inverseJoinColumns = { @JoinColumn(name = "book_id") }) @Filter(name = "BookStatusFilter", condition = " :status=status ") private Set<Book> books; @OneToMany(mappedBy = "teacher", fetch = FetchType.LAZY) @Filter(name = "StudentStatusFilter", condition = " :status=status ") private Set<Student> students; } |
Student:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | @Data @Entity @Table(name = "student") @EqualsAndHashCode(of = {"id"}) @FilterDef(name = "StudentStatusFilter", parameters = {@ParamDef(name="status",type="string")}) public class Student { @Column(name = "uuid") @Id private String id; @Column(name = "deleted") private String deleted; @Column(name = "status") private String status; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "teacher") @JsonIgnore private Teacher teacher; @OneToMany(mappedBy = "student", fetch = FetchType.LAZY) @Where(clause = " deleted is null ") @Filter(name = "CourseStatusFilter", condition = " :status=status ") private Set<Course> courses; } |
Course:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @Data @Entity @Table(name = "course") @EqualsAndHashCode(of = {"id"}) @FilterDef(name = "CourseStatusFilter", parameters = {@ParamDef(name="status",type="string")}) public class Course { @Column(name = "uuid") @Id private String id; @Column(name = "deleted") private String deleted; @Column(name = "status") private String status; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "student") @JsonIgnore private Student student; } |
Book:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Data @Entity @Table(name = "book") @EqualsAndHashCode(of = {"id"}) @FilterDef(name = "BookStatusFilter", parameters = {@ParamDef(name="status",type="string")}) public class Book { @Column(name = "uuid") @Id private String id; @Column(name = "status") private String status; @Column(name = "deleted") private String deleted; } |
Repository
1 2 3 4 5 6 7 8 9 | @Repository public interface TeacherRepository extends CrudRepository<Teacher, String>{ @Query("select t from Teacher t " + "left join fetch t.students s " + "left join fetch s.courses c " + "left join fetch t.books b") public Set<Teacher> findTeachersU(); } |
Service:
JPA中启用Filter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | @Service public class TeacherService { @Autowired @PersistenceContext() EntityManager entityManager; @Autowired TeacherRepository teacherRepository; public Set<Teacher> findTeachers(){ return teacherRepository.findTeachersU(); } public Set<Teacher> findTeachersByFilter(String status){ Session session = entityManager.unwrap(Session.class); session.enableFilter("StudentStatusFilter").setParameter("status", status); session.enableFilter("CourseStatusFilter").setParameter("status", status); session.enableFilter("BookStatusFilter").setParameter("status", status); Set<Teacher> reSet = teacherRepository.findTeachersU(); session.disableFilter("StudentStatusFilter"); session.disableFilter("CourseStatusFilter"); session.disableFilter("BookStatusFilter"); return reSet; } } |
Controller
1 2 3 4 5 6 7 8 9 10 11 | @RestController public class TeacherController { @Autowired TeacherService teacherService; @GetMapping("/teachers") public Set<Teacher> findTeachers(@RequestParam(value = "status", required = false) String status){ return teacherService.findTeachersByFilter(status); } } |
测试
利用/teachers?status=active动态传输过滤变量过滤。可以得到下列结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | [ { "id": "t1", "books": [ { "id": "b1", "status": "active", "deleted": null }, { "id": "b2", "status": "inactive", "deleted": "deleted" } ], "students": [ { "id": "s1", "deleted": null, "status": "active", "courses": [ { "id": "c1", "deleted": null, "status": "active" } ] } ] }, { "id": "t2", "books": [], "students": [] } ] |
可以看到status为inactive的student和course已经被过滤掉了,而inactive的book并没有被过滤掉。所以,与@Where一样,通过中间表关联的数据利用@Filter达不到过滤的效果。
查询的sql:
1 2 3 4 5 6 7 | Hibernate: select teacher0_.uuid as uuid1_25_0_, students1_.uuid as uuid1_24_1_, courses2_.uuid as uuid1_5_2_, book4_.uuid as uuid1_3_3_, students1_.deleted as deleted2_24_1_, students1_.status as status3_24_1_, students1_.teacher as teacher4_24_1_, students1_.teacher as teacher4_24_0__, students1_.uuid as uuid1_24_0__, courses2_.deleted as deleted2_5_2_, courses2_.status as status3_5_2_, courses2_.student as student4_5_2_, courses2_.student as student4_5_1__, courses2_.uuid as uuid1_5_1__, book4_.deleted as deleted2_3_3_, book4_.status as status3_3_3_, books3_.teacher_id as teacher_1_26_2__, books3_.book_id as book_id2_26_2__ from teacher teacher0_ left outer join student students1_ on teacher0_.uuid=students1_.teacher and ?=students1_.status left outer join course courses2_ on students1_.uuid=courses2_.student and ?=courses2_.status and ( courses2_.deleted is null ) left outer join teacher_book books3_ on teacher0_.uuid=books3_.teacher_id left outer join book book4_ on books3_.book_id=book4_.uuid and ?=book4_.status Hibernate: select book0_.uuid as uuid1_3_0_, book0_.deleted as deleted2_3_0_, book0_.status as status3_3_0_ from book book0_ where book0_.uuid=? |
达到过滤功能的原理和@Where一样。
参考:
https://www.baeldung.com/hibernate-dynamic-mapping
https://stackoverflow.com/questions/49752429/using-a-hibernate-filter-with-spring-boot-jpa