一个老师(Teacher)有很多个学生(Student)和有很多课本(book),一个学生有很多选修课程(Course)。查询老师列表的时候会把老师名下的学生和课本罗列出来,但是不需要展示被删除掉或者状态不对的数据。Teacher和Student通过关键字关联,Teacher和Book通过中间关联表关联,Student和Course通过关键字关联。
Teacher:
uuid
------
t1
t2
(2 rows)
Student:
uuid | deleted | status | teacher
------+---------+----------+---------
s1 | | active | t1
s2 | deleted | inactive | t1
Course:
uuid | deleted | status | student
------+---------+----------+---------
c1 | | active | s1
c2 | deleted | inactive | s1
(2 rows)
Book:
uuid | deleted | status
------+---------+----------
b1 | | active
b2 | deleted | inactive
(2 rows)
teacher_book
teacher_id | book_id
------------+---------
t1 | b1
t1 | b2
(2 rows)
利用@Where注解过滤掉软删的学生、书、课程。
Teacher:
@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:
@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:
@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:
@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
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
public class TeacherService {
@Autowired
TeacherRepository teacherRepository;
public Set<Teacher> findTeachers(){
return teacherRepository.findTeachersU();
}
}
@RestController
public class TeacherController {
@Autowired
TeacherService teacherService;
@GetMapping("/teachers")
public Set<Teacher> findTeachers() String status){
return teacherService.findTeachers();
}
}
发送/teachers可得到以下结果:
[
{
"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无法达到过滤的作用。
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了。
利用@FilterDef定义变量的名称
@FilterDef(name = “过滤器的名字”, parameters = {@ParamDef(name=“sql中变量的名字”,type=“变量类型”)})
在需要过滤的字段添加@Filter进行过滤
@Filter(name = “过滤器的名字”, condition = " 过滤器中定义的变量=数据库表的字段 ")
Teacher
@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:
@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:
@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:
@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
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
@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
@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动态传输过滤变量过滤。可以得到下列结果:
[
{
"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:
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